From b0d8ed94fe9e74afb49fdf5f11e4add29879c65c Mon Sep 17 00:00:00 2001 From: Rene Mayrhofer Date: Thu, 12 Apr 2007 20:30:08 +0000 Subject: [svn-upgrade] Integrating new upstream version, strongswan (4.1.1) --- src/Makefile.am | 1 + src/Makefile.in | 497 ++ src/_copyright/Makefile.am | 6 + src/_copyright/Makefile.in | 529 ++ src/_copyright/_copyright.8 | 32 + src/_copyright/_copyright.c | 69 + src/_updown/Makefile.am | 3 + src/_updown/Makefile.in | 421 ++ src/_updown/_updown | 503 ++ src/_updown/_updown.8 | 19 + src/_updown_espmark/Makefile.am | 2 + src/_updown_espmark/Makefile.in | 421 ++ src/_updown_espmark/_updown_espmark | 452 ++ src/_updown_espmark/_updown_espmark.8 | 18 + src/charon/Makefile.am | 87 + src/charon/Makefile.in | 1878 +++++++ src/charon/bus/bus.c | 397 ++ src/charon/bus/bus.h | 366 ++ src/charon/bus/listeners/file_logger.c | 128 + src/charon/bus/listeners/file_logger.h | 73 + src/charon/bus/listeners/sys_logger.c | 131 + src/charon/bus/listeners/sys_logger.h | 75 + src/charon/config/configuration.c | 162 + src/charon/config/configuration.h | 102 + src/charon/config/connections/connection.c | 404 ++ src/charon/config/connections/connection.h | 292 + src/charon/config/connections/connection_store.h | 118 + .../config/connections/local_connection_store.c | 237 + .../config/connections/local_connection_store.h | 62 + .../config/credentials/local_credential_store.c | 1363 +++++ .../config/credentials/local_credential_store.h | 64 + src/charon/config/policies/local_policy_store.c | 282 + src/charon/config/policies/local_policy_store.h | 60 + src/charon/config/policies/policy.c | 635 +++ src/charon/config/policies/policy.h | 413 ++ src/charon/config/policies/policy_store.h | 119 + src/charon/config/proposal.c | 641 +++ src/charon/config/proposal.h | 266 + src/charon/config/traffic_selector.c | 795 +++ src/charon/config/traffic_selector.h | 312 ++ src/charon/daemon.c | 529 ++ src/charon/daemon.h | 403 ++ src/charon/encoding/generator.c | 1063 ++++ src/charon/encoding/generator.h | 102 + src/charon/encoding/message.c | 1316 +++++ src/charon/encoding/message.h | 390 ++ src/charon/encoding/parser.c | 1048 ++++ src/charon/encoding/parser.h | 95 + src/charon/encoding/payloads/auth_payload.c | 265 + src/charon/encoding/payloads/auth_payload.h | 121 + src/charon/encoding/payloads/cert_payload.c | 290 + src/charon/encoding/payloads/cert_payload.h | 166 + src/charon/encoding/payloads/certreq_payload.c | 335 ++ src/charon/encoding/payloads/certreq_payload.h | 144 + .../encoding/payloads/configuration_attribute.c | 313 ++ .../encoding/payloads/configuration_attribute.h | 147 + src/charon/encoding/payloads/cp_payload.c | 277 + src/charon/encoding/payloads/cp_payload.h | 132 + src/charon/encoding/payloads/delete_payload.c | 299 ++ src/charon/encoding/payloads/delete_payload.h | 102 + src/charon/encoding/payloads/eap_payload.c | 331 ++ src/charon/encoding/payloads/eap_payload.h | 149 + src/charon/encoding/payloads/encodings.c | 66 + src/charon/encoding/payloads/encodings.h | 537 ++ src/charon/encoding/payloads/encryption_payload.c | 646 +++ src/charon/encoding/payloads/encryption_payload.h | 197 + src/charon/encoding/payloads/id_payload.c | 323 ++ src/charon/encoding/payloads/id_payload.h | 172 + src/charon/encoding/payloads/ike_header.c | 406 ++ src/charon/encoding/payloads/ike_header.h | 260 + src/charon/encoding/payloads/ke_payload.c | 277 + src/charon/encoding/payloads/ke_payload.h | 121 + src/charon/encoding/payloads/nonce_payload.c | 232 + src/charon/encoding/payloads/nonce_payload.h | 99 + src/charon/encoding/payloads/notify_payload.c | 481 ++ src/charon/encoding/payloads/notify_payload.h | 224 + src/charon/encoding/payloads/payload.c | 161 + src/charon/encoding/payloads/payload.h | 282 + .../encoding/payloads/proposal_substructure.c | 603 +++ .../encoding/payloads/proposal_substructure.h | 206 + src/charon/encoding/payloads/sa_payload.c | 375 ++ src/charon/encoding/payloads/sa_payload.h | 141 + .../payloads/traffic_selector_substructure.c | 283 + .../payloads/traffic_selector_substructure.h | 172 + src/charon/encoding/payloads/transform_attribute.c | 332 ++ src/charon/encoding/payloads/transform_attribute.h | 154 + .../encoding/payloads/transform_substructure.c | 409 ++ .../encoding/payloads/transform_substructure.h | 198 + src/charon/encoding/payloads/ts_payload.c | 341 ++ src/charon/encoding/payloads/ts_payload.h | 153 + src/charon/encoding/payloads/unknown_payload.c | 208 + src/charon/encoding/payloads/unknown_payload.h | 95 + src/charon/encoding/payloads/vendor_id_payload.c | 228 + src/charon/encoding/payloads/vendor_id_payload.h | 104 + src/charon/network/packet.c | 168 + src/charon/network/packet.h | 134 + src/charon/network/socket.c | 755 +++ src/charon/network/socket.h | 112 + src/charon/queues/event_queue.c | 290 + src/charon/queues/event_queue.h | 118 + src/charon/queues/job_queue.c | 139 + src/charon/queues/job_queue.h | 100 + src/charon/queues/jobs/acquire_job.c | 98 + src/charon/queues/jobs/acquire_job.h | 60 + src/charon/queues/jobs/delete_child_sa_job.c | 113 + src/charon/queues/jobs/delete_child_sa_job.h | 68 + src/charon/queues/jobs/delete_ike_sa_job.c | 126 + src/charon/queues/jobs/delete_ike_sa_job.h | 66 + src/charon/queues/jobs/initiate_job.c | 112 + src/charon/queues/jobs/initiate_job.h | 61 + src/charon/queues/jobs/job.c | 39 + src/charon/queues/jobs/job.h | 165 + src/charon/queues/jobs/process_message_job.c | 106 + src/charon/queues/jobs/process_message_job.h | 58 + src/charon/queues/jobs/rekey_child_sa_job.c | 112 + src/charon/queues/jobs/rekey_child_sa_job.h | 65 + src/charon/queues/jobs/rekey_ike_sa_job.c | 120 + src/charon/queues/jobs/rekey_ike_sa_job.h | 60 + src/charon/queues/jobs/retransmit_job.c | 109 + src/charon/queues/jobs/retransmit_job.h | 64 + src/charon/queues/jobs/route_job.c | 125 + src/charon/queues/jobs/route_job.h | 59 + src/charon/queues/jobs/send_dpd_job.c | 110 + src/charon/queues/jobs/send_dpd_job.h | 68 + src/charon/queues/jobs/send_keepalive_job.c | 103 + src/charon/queues/jobs/send_keepalive_job.h | 67 + src/charon/sa/authenticators/authenticator.c | 56 + src/charon/sa/authenticators/authenticator.h | 139 + src/charon/sa/authenticators/eap/eap_identity.c | 135 + src/charon/sa/authenticators/eap/eap_identity.h | 59 + src/charon/sa/authenticators/eap/eap_method.c | 245 + src/charon/sa/authenticators/eap/eap_method.h | 242 + src/charon/sa/authenticators/eap/eap_sim.c | 703 +++ src/charon/sa/authenticators/eap/eap_sim.h | 141 + src/charon/sa/authenticators/eap_authenticator.c | 360 ++ src/charon/sa/authenticators/eap_authenticator.h | 156 + src/charon/sa/authenticators/psk_authenticator.c | 204 + src/charon/sa/authenticators/psk_authenticator.h | 57 + src/charon/sa/authenticators/rsa_authenticator.c | 180 + src/charon/sa/authenticators/rsa_authenticator.h | 57 + src/charon/sa/child_sa.c | 1130 ++++ src/charon/sa/child_sa.h | 298 ++ src/charon/sa/ike_sa.c | 2032 +++++++ src/charon/sa/ike_sa.h | 649 +++ src/charon/sa/ike_sa_id.c | 215 + src/charon/sa/ike_sa_id.h | 147 + src/charon/sa/ike_sa_manager.c | 914 ++++ src/charon/sa/ike_sa_manager.h | 231 + src/charon/sa/task_manager.c | 854 +++ src/charon/sa/task_manager.h | 144 + src/charon/sa/tasks/child_create.c | 804 +++ src/charon/sa/tasks/child_create.h | 88 + src/charon/sa/tasks/child_delete.c | 292 + src/charon/sa/tasks/child_delete.h | 66 + src/charon/sa/tasks/child_rekey.c | 346 ++ src/charon/sa/tasks/child_rekey.h | 70 + src/charon/sa/tasks/ike_auth.c | 750 +++ src/charon/sa/tasks/ike_auth.h | 64 + src/charon/sa/tasks/ike_cert.c | 370 ++ src/charon/sa/tasks/ike_cert.h | 61 + src/charon/sa/tasks/ike_config.c | 428 ++ src/charon/sa/tasks/ike_config.h | 59 + src/charon/sa/tasks/ike_delete.c | 172 + src/charon/sa/tasks/ike_delete.h | 57 + src/charon/sa/tasks/ike_dpd.c | 106 + src/charon/sa/tasks/ike_dpd.h | 58 + src/charon/sa/tasks/ike_init.c | 598 +++ src/charon/sa/tasks/ike_init.h | 68 + src/charon/sa/tasks/ike_natd.c | 371 ++ src/charon/sa/tasks/ike_natd.h | 57 + src/charon/sa/tasks/ike_rekey.c | 329 ++ src/charon/sa/tasks/ike_rekey.h | 69 + src/charon/sa/tasks/task.c | 38 + src/charon/sa/tasks/task.h | 151 + src/charon/threads/kernel_interface.c | 1964 +++++++ src/charon/threads/kernel_interface.h | 331 ++ src/charon/threads/receiver.c | 372 ++ src/charon/threads/receiver.h | 81 + src/charon/threads/scheduler.c | 102 + src/charon/threads/scheduler.h | 68 + src/charon/threads/sender.c | 149 + src/charon/threads/sender.h | 74 + src/charon/threads/stroke_interface.c | 1456 +++++ src/charon/threads/stroke_interface.h | 61 + src/charon/threads/thread_pool.c | 181 + src/charon/threads/thread_pool.h | 87 + src/ipsec/Makefile.am | 16 + src/ipsec/Makefile.in | 434 ++ src/ipsec/ipsec.8 | 342 ++ src/ipsec/ipsec.in | 294 + src/libcrypto/Makefile.am | 11 + src/libcrypto/Makefile.in | 761 +++ src/libcrypto/include/cbc_generic.h | 110 + src/libcrypto/include/hmac_generic.h | 60 + src/libcrypto/include/md32_common.h | 607 +++ src/libcrypto/libaes/aes.c | 1415 +++++ src/libcrypto/libaes/aes.h | 97 + src/libcrypto/libaes/aes_cbc.c | 13 + src/libcrypto/libaes/aes_cbc.h | 4 + src/libcrypto/libaes/aes_xcbc_mac.c | 67 + src/libcrypto/libaes/aes_xcbc_mac.h | 12 + src/libcrypto/libblowfish/bf_enc.c | 306 ++ src/libcrypto/libblowfish/bf_locl.h | 218 + src/libcrypto/libblowfish/bf_pi.h | 325 ++ src/libcrypto/libblowfish/bf_skey.c | 122 + src/libcrypto/libblowfish/blowfish.h | 133 + src/libcrypto/libdes/cbc_enc.c | 135 + src/libcrypto/libdes/des.h | 308 ++ src/libcrypto/libdes/des_enc.c | 502 ++ src/libcrypto/libdes/des_locl.h | 515 ++ src/libcrypto/libdes/des_opts.c | 620 +++ src/libcrypto/libdes/des_ver.h | 60 + src/libcrypto/libdes/destest.c | 871 +++ src/libcrypto/libdes/ecb_enc.c | 128 + src/libcrypto/libdes/fcrypt.c | 152 + src/libcrypto/libdes/fcrypt_b.c | 148 + src/libcrypto/libdes/podd.h | 75 + src/libcrypto/libdes/set_key.c | 246 + src/libcrypto/libdes/sk.h | 204 + src/libcrypto/libdes/speed.c | 329 ++ src/libcrypto/libdes/spr.h | 204 + src/libcrypto/libserpent/serpent.c | 995 ++++ src/libcrypto/libserpent/serpent.h | 17 + src/libcrypto/libserpent/serpent_cbc.c | 8 + src/libcrypto/libserpent/serpent_cbc.h | 3 + src/libcrypto/libsha2/hmac_sha2.c | 32 + src/libcrypto/libsha2/hmac_sha2.h | 17 + src/libcrypto/libsha2/sha2.c | 437 ++ src/libcrypto/libsha2/sha2.h | 52 + src/libcrypto/libtwofish/twofish.c | 861 +++ src/libcrypto/libtwofish/twofish.h | 20 + src/libcrypto/libtwofish/twofish_cbc.c | 8 + src/libcrypto/libtwofish/twofish_cbc.h | 3 + src/libfreeswan/Makefile.am | 19 + src/libfreeswan/Makefile.in | 574 ++ src/libfreeswan/addrtoa.c | 68 + src/libfreeswan/addrtot.c | 302 ++ src/libfreeswan/addrtypeof.c | 94 + src/libfreeswan/anyaddr.3 | 87 + src/libfreeswan/anyaddr.c | 146 + src/libfreeswan/atoaddr.3 | 294 + src/libfreeswan/atoaddr.c | 238 + src/libfreeswan/atoasr.3 | 186 + src/libfreeswan/atoasr.c | 212 + src/libfreeswan/atosa.3 | 218 + src/libfreeswan/atosa.c | 200 + src/libfreeswan/atosubnet.c | 216 + src/libfreeswan/atoul.3 | 161 + src/libfreeswan/atoul.c | 90 + src/libfreeswan/copyright.c | 56 + src/libfreeswan/datatot.c | 233 + src/libfreeswan/freeswan.h | 470 ++ src/libfreeswan/goodmask.3 | 57 + src/libfreeswan/goodmask.c | 97 + src/libfreeswan/initaddr.3 | 129 + src/libfreeswan/initaddr.c | 51 + src/libfreeswan/initsaid.c | 33 + src/libfreeswan/initsubnet.3 | 137 + src/libfreeswan/initsubnet.c | 95 + src/libfreeswan/internal.h | 81 + src/libfreeswan/ipcomp.h | 61 + src/libfreeswan/ipsec_ah.h | 235 + src/libfreeswan/ipsec_alg.h | 254 + src/libfreeswan/ipsec_encap.h | 143 + src/libfreeswan/ipsec_eroute.h | 103 + src/libfreeswan/ipsec_errs.h | 53 + src/libfreeswan/ipsec_esp.h | 220 + src/libfreeswan/ipsec_ipe4.h | 68 + src/libfreeswan/ipsec_kversion.h | 227 + src/libfreeswan/ipsec_life.h | 112 + src/libfreeswan/ipsec_md5h.h | 140 + src/libfreeswan/ipsec_param.h | 226 + src/libfreeswan/ipsec_policy.h | 225 + src/libfreeswan/ipsec_proto.h | 111 + src/libfreeswan/ipsec_radij.h | 63 + src/libfreeswan/ipsec_rcv.h | 196 + src/libfreeswan/ipsec_sa.h | 338 ++ src/libfreeswan/ipsec_sha1.h | 79 + src/libfreeswan/ipsec_stats.h | 38 + src/libfreeswan/ipsec_tunnel.h | 265 + src/libfreeswan/ipsec_xform.h | 274 + src/libfreeswan/ipsec_xmit.h | 140 + src/libfreeswan/keyblobtoid.3 | 103 + src/libfreeswan/keyblobtoid.c | 148 + src/libfreeswan/optionsfrom.3 | 182 + src/libfreeswan/optionsfrom.c | 301 ++ src/libfreeswan/pfkey.h | 498 ++ src/libfreeswan/pfkey_v2_build.c | 1435 +++++ src/libfreeswan/pfkey_v2_debug.c | 177 + src/libfreeswan/pfkey_v2_ext_bits.c | 789 +++ src/libfreeswan/pfkey_v2_parse.c | 1824 +++++++ src/libfreeswan/pfkeyv2.h | 375 ++ src/libfreeswan/portof.3 | 70 + src/libfreeswan/portof.c | 96 + src/libfreeswan/prng.3 | 121 + src/libfreeswan/prng.c | 202 + src/libfreeswan/radij.h | 280 + src/libfreeswan/rangetoa.c | 61 + src/libfreeswan/rangetosubnet.3 | 59 + src/libfreeswan/rangetosubnet.c | 226 + src/libfreeswan/sameaddr.3 | 165 + src/libfreeswan/sameaddr.c | 190 + src/libfreeswan/satoa.c | 102 + src/libfreeswan/satot.c | 132 + src/libfreeswan/subnetof.3 | 47 + src/libfreeswan/subnetof.c | 60 + src/libfreeswan/subnettoa.c | 62 + src/libfreeswan/subnettot.c | 56 + src/libfreeswan/subnettypeof.c | 109 + src/libfreeswan/ttoaddr.3 | 377 ++ src/libfreeswan/ttoaddr.c | 426 ++ src/libfreeswan/ttodata.3 | 281 + src/libfreeswan/ttodata.c | 722 +++ src/libfreeswan/ttoprotoport.c | 103 + src/libfreeswan/ttosa.3 | 288 + src/libfreeswan/ttosa.c | 280 + src/libfreeswan/ttosubnet.c | 296 + src/libfreeswan/ttoul.3 | 192 + src/libfreeswan/ttoul.c | 91 + src/libfreeswan/ultoa.c | 67 + src/libfreeswan/ultot.c | 83 + src/libfreeswan/version.3 | 44 + src/libfreeswan/version.c | 43 + src/libstrongswan/Makefile.am | 69 + src/libstrongswan/Makefile.in | 820 +++ src/libstrongswan/asn1/asn1.c | 733 +++ src/libstrongswan/asn1/asn1.h | 135 + src/libstrongswan/asn1/oid.c | 197 + src/libstrongswan/asn1/oid.h | 80 + src/libstrongswan/asn1/oid.pl | 127 + src/libstrongswan/asn1/oid.txt | 184 + src/libstrongswan/asn1/pem.c | 366 ++ src/libstrongswan/asn1/pem.h | 27 + src/libstrongswan/asn1/ttodata.c | 378 ++ src/libstrongswan/asn1/ttodata.h | 28 + src/libstrongswan/chunk.c | 410 ++ src/libstrongswan/chunk.h | 154 + src/libstrongswan/credential_store.h | 294 + src/libstrongswan/crypto/ca.c | 788 +++ src/libstrongswan/crypto/ca.h | 215 + src/libstrongswan/crypto/certinfo.c | 305 ++ src/libstrongswan/crypto/certinfo.h | 203 + src/libstrongswan/crypto/crl.c | 533 ++ src/libstrongswan/crypto/crl.h | 147 + .../crypto/crypters/aes_cbc_crypter.c | 1620 ++++++ .../crypto/crypters/aes_cbc_crypter.h | 61 + src/libstrongswan/crypto/crypters/crypter.c | 68 + src/libstrongswan/crypto/crypters/crypter.h | 155 + src/libstrongswan/crypto/crypters/des_crypter.c | 1535 ++++++ src/libstrongswan/crypto/crypters/des_crypter.h | 58 + src/libstrongswan/crypto/diffie_hellman.c | 612 +++ src/libstrongswan/crypto/diffie_hellman.h | 147 + src/libstrongswan/crypto/hashers/hasher.c | 65 + src/libstrongswan/crypto/hashers/hasher.h | 159 + src/libstrongswan/crypto/hashers/md5_hasher.c | 405 ++ src/libstrongswan/crypto/hashers/md5_hasher.h | 60 + src/libstrongswan/crypto/hashers/sha1_hasher.c | 280 + src/libstrongswan/crypto/hashers/sha1_hasher.h | 60 + src/libstrongswan/crypto/hashers/sha2_hasher.c | 672 +++ src/libstrongswan/crypto/hashers/sha2_hasher.h | 62 + src/libstrongswan/crypto/hmac.c | 215 + src/libstrongswan/crypto/hmac.h | 117 + src/libstrongswan/crypto/ocsp.c | 924 ++++ src/libstrongswan/crypto/ocsp.h | 86 + src/libstrongswan/crypto/prf_plus.c | 156 + src/libstrongswan/crypto/prf_plus.h | 92 + src/libstrongswan/crypto/prfs/fips_prf.c | 258 + src/libstrongswan/crypto/prfs/fips_prf.h | 80 + src/libstrongswan/crypto/prfs/hmac_prf.c | 118 + src/libstrongswan/crypto/prfs/hmac_prf.h | 65 + src/libstrongswan/crypto/prfs/prf.c | 70 + src/libstrongswan/crypto/prfs/prf.h | 142 + src/libstrongswan/crypto/rsa/rsa_private_key.c | 774 +++ src/libstrongswan/crypto/rsa/rsa_private_key.h | 184 + src/libstrongswan/crypto/rsa/rsa_public_key.c | 497 ++ src/libstrongswan/crypto/rsa/rsa_public_key.h | 164 + src/libstrongswan/crypto/signers/hmac_signer.c | 174 + src/libstrongswan/crypto/signers/hmac_signer.h | 68 + src/libstrongswan/crypto/signers/signer.c | 65 + src/libstrongswan/crypto/signers/signer.h | 147 + src/libstrongswan/crypto/x509.c | 1354 +++++ src/libstrongswan/crypto/x509.h | 290 + src/libstrongswan/debug.c | 41 + src/libstrongswan/debug.h | 60 + src/libstrongswan/enum.c | 73 + src/libstrongswan/enum.h | 106 + src/libstrongswan/library.c | 184 + src/libstrongswan/library.h | 301 ++ src/libstrongswan/printf_hook.c | 118 + src/libstrongswan/printf_hook.h | 76 + src/libstrongswan/utils/fetcher.c | 421 ++ src/libstrongswan/utils/fetcher.h | 95 + src/libstrongswan/utils/host.c | 526 ++ src/libstrongswan/utils/host.h | 231 + src/libstrongswan/utils/identification.c | 1144 ++++ src/libstrongswan/utils/identification.h | 261 + src/libstrongswan/utils/iterator.h | 166 + src/libstrongswan/utils/leak_detective.c | 459 ++ src/libstrongswan/utils/leak_detective.h | 35 + src/libstrongswan/utils/lexparser.c | 137 + src/libstrongswan/utils/lexparser.h | 57 + src/libstrongswan/utils/linked_list.c | 763 +++ src/libstrongswan/utils/linked_list.h | 232 + src/libstrongswan/utils/randomizer.c | 165 + src/libstrongswan/utils/randomizer.h | 114 + src/openac/Makefile.am | 98 + src/openac/Makefile.in | 624 +++ src/openac/build.c | 242 + src/openac/build.h | 47 + src/openac/loglite.c | 295 + src/openac/openac.8 | 180 + src/openac/openac.c | 438 ++ src/pluto/Makefile.am | 140 + src/pluto/Makefile.in | 878 +++ src/pluto/TODO | 129 + src/pluto/ac.c | 1018 ++++ src/pluto/ac.h | 103 + src/pluto/adns.c | 615 +++ src/pluto/adns.h | 75 + src/pluto/alg/ike_alg_aes.c | 68 + src/pluto/alg/ike_alg_blowfish.c | 52 + src/pluto/alg/ike_alg_serpent.c | 70 + src/pluto/alg/ike_alg_sha2.c | 634 +++ src/pluto/alg/ike_alg_twofish.c | 85 + src/pluto/alg/ike_alginit.c | 7 + src/pluto/alg_info.c | 1205 +++++ src/pluto/alg_info.h | 85 + src/pluto/asn1.c | 770 +++ src/pluto/asn1.h | 141 + src/pluto/ca.c | 694 +++ src/pluto/ca.h | 70 + src/pluto/certs.c | 287 + src/pluto/certs.h | 80 + src/pluto/connections.c | 4406 +++++++++++++++ src/pluto/connections.h | 367 ++ src/pluto/constants.c | 1353 +++++ src/pluto/constants.h | 1269 +++++ src/pluto/cookie.c | 67 + src/pluto/cookie.h | 24 + src/pluto/crl.c | 763 +++ src/pluto/crl.h | 87 + src/pluto/crypto.c | 627 +++ src/pluto/crypto.h | 108 + src/pluto/db_ops.c | 439 ++ src/pluto/db_ops.h | 56 + src/pluto/defs.c | 374 ++ src/pluto/defs.h | 145 + src/pluto/demux.c | 2499 +++++++++ src/pluto/demux.h | 93 + src/pluto/dnskey.c | 1962 +++++++ src/pluto/dnskey.h | 84 + src/pluto/dsa.c | 476 ++ src/pluto/dsa.h | 32 + src/pluto/elgamal.c | 613 +++ src/pluto/elgamal.h | 35 + src/pluto/fetch.c | 1081 ++++ src/pluto/fetch.h | 79 + src/pluto/foodgroups.c | 462 ++ src/pluto/foodgroups.h | 24 + src/pluto/gcryptfix.c | 283 + src/pluto/gcryptfix.h | 111 + src/pluto/id.c | 509 ++ src/pluto/id.h | 67 + src/pluto/ike_alg.c | 592 ++ src/pluto/ike_alg.h | 94 + src/pluto/ipsec.secrets.5 | 175 + src/pluto/ipsec_doi.c | 5630 ++++++++++++++++++++ src/pluto/ipsec_doi.h | 104 + src/pluto/kameipsec.h | 47 + src/pluto/kernel.c | 2995 +++++++++++ src/pluto/kernel.h | 198 + src/pluto/kernel_alg.c | 775 +++ src/pluto/kernel_alg.h | 46 + src/pluto/kernel_netlink.c | 1219 +++++ src/pluto/kernel_netlink.h | 20 + src/pluto/kernel_noklips.c | 126 + src/pluto/kernel_noklips.h | 19 + src/pluto/kernel_pfkey.c | 926 ++++ src/pluto/kernel_pfkey.h | 23 + src/pluto/keys.c | 1514 ++++++ src/pluto/keys.h | 113 + src/pluto/lex.c | 213 + src/pluto/lex.h | 52 + src/pluto/linux26/netlink.h | 90 + src/pluto/linux26/rtnetlink.h | 562 ++ src/pluto/linux26/xfrm.h | 233 + src/pluto/log.c | 841 +++ src/pluto/log.h | 236 + src/pluto/md2.c | 237 + src/pluto/md2.h | 72 + src/pluto/md5.c | 385 ++ src/pluto/md5.h | 75 + src/pluto/modecfg.c | 1078 ++++ src/pluto/modecfg.h | 47 + src/pluto/mp_defs.c | 70 + src/pluto/mp_defs.h | 36 + src/pluto/nat_traversal.c | 866 +++ src/pluto/nat_traversal.h | 154 + src/pluto/ocsp.c | 1568 ++++++ src/pluto/ocsp.h | 85 + src/pluto/oid.c | 197 + src/pluto/oid.h | 78 + src/pluto/oid.pl | 123 + src/pluto/oid.txt | 184 + src/pluto/packet.c | 1244 +++++ src/pluto/packet.h | 655 +++ src/pluto/pem.c | 463 ++ src/pluto/pem.h | 18 + src/pluto/pgp.c | 647 +++ src/pluto/pgp.h | 54 + src/pluto/pkcs1.c | 674 +++ src/pluto/pkcs1.h | 88 + src/pluto/pkcs7.c | 862 +++ src/pluto/pkcs7.h | 51 + src/pluto/pluto.8 | 1649 ++++++ src/pluto/plutomain.c | 655 +++ src/pluto/primegen.c | 593 +++ src/pluto/rcv_whack.c | 685 +++ src/pluto/rcv_whack.h | 17 + src/pluto/rnd.c | 250 + src/pluto/rnd.h | 21 + src/pluto/rsaref/pkcs11.h | 299 ++ src/pluto/rsaref/pkcs11f.h | 912 ++++ src/pluto/rsaref/pkcs11t.h | 1685 ++++++ src/pluto/rsaref/unix.h | 24 + src/pluto/server.c | 996 ++++ src/pluto/server.h | 58 + src/pluto/sha1.c | 193 + src/pluto/sha1.h | 16 + src/pluto/smallprime.c | 122 + src/pluto/smartcard.c | 1956 +++++++ src/pluto/smartcard.h | 100 + src/pluto/spdb.c | 2314 ++++++++ src/pluto/spdb.h | 112 + src/pluto/state.c | 1012 ++++ src/pluto/state.h | 273 + src/pluto/timer.c | 532 ++ src/pluto/timer.h | 34 + src/pluto/vendor.c | 528 ++ src/pluto/vendor.h | 131 + src/pluto/virtual.c | 334 ++ src/pluto/virtual.h | 31 + src/pluto/x509.c | 2241 ++++++++ src/pluto/x509.h | 138 + src/pluto/xauth.c | 79 + src/pluto/xauth.h | 41 + src/scepclient/Makefile.am | 103 + src/scepclient/Makefile.in | 630 +++ src/scepclient/pkcs10.c | 220 + src/scepclient/pkcs10.h | 57 + src/scepclient/rsakey.c | 349 ++ src/scepclient/rsakey.h | 31 + src/scepclient/scep.c | 598 +++ src/scepclient/scep.h | 93 + src/scepclient/scepclient.8 | 288 + src/scepclient/scepclient.c | 1036 ++++ src/starter/Makefile.am | 37 + src/starter/Makefile.in | 581 ++ src/starter/README | 104 + src/starter/args.c | 632 +++ src/starter/args.h | 34 + src/starter/cmp.c | 105 + src/starter/cmp.h | 29 + src/starter/confread.c | 936 ++++ src/starter/confread.h | 210 + src/starter/exec.c | 54 + src/starter/exec.h | 23 + src/starter/files.h | 40 + src/starter/interfaces.c | 595 +++ src/starter/interfaces.h | 41 + src/starter/invokecharon.c | 251 + src/starter/invokecharon.h | 31 + src/starter/invokepluto.c | 286 + src/starter/invokepluto.h | 28 + src/starter/ipsec.conf | 42 + src/starter/ipsec.conf.5 | 1062 ++++ src/starter/keywords.c | 261 + src/starter/keywords.h | 176 + src/starter/keywords.txt | 118 + src/starter/lex.yy.c | 1966 +++++++ src/starter/netkey.c | 85 + src/starter/netkey.h | 24 + src/starter/parser.h | 57 + src/starter/parser.l | 190 + src/starter/parser.y | 283 + src/starter/starter.c | 643 +++ src/starter/starterstroke.c | 295 + src/starter/starterstroke.h | 29 + src/starter/starterwhack.c | 369 ++ src/starter/starterwhack.h | 32 + src/starter/y.tab.c | 1832 +++++++ src/starter/y.tab.h | 82 + src/stroke/Makefile.am | 9 + src/stroke/Makefile.in | 483 ++ src/stroke/stroke.c | 421 ++ src/stroke/stroke.h | 226 + src/stroke/stroke_keywords.c | 179 + src/stroke/stroke_keywords.h | 55 + src/stroke/stroke_keywords.txt | 50 + src/whack/Makefile.am | 8 + src/whack/Makefile.in | 478 ++ src/whack/whack.c | 1905 +++++++ src/whack/whack.h | 318 ++ 603 files changed, 199836 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/_copyright/Makefile.am create mode 100644 src/_copyright/Makefile.in create mode 100644 src/_copyright/_copyright.8 create mode 100644 src/_copyright/_copyright.c create mode 100644 src/_updown/Makefile.am create mode 100644 src/_updown/Makefile.in create mode 100755 src/_updown/_updown create mode 100644 src/_updown/_updown.8 create mode 100644 src/_updown_espmark/Makefile.am create mode 100644 src/_updown_espmark/Makefile.in create mode 100644 src/_updown_espmark/_updown_espmark create mode 100644 src/_updown_espmark/_updown_espmark.8 create mode 100644 src/charon/Makefile.am create mode 100644 src/charon/Makefile.in create mode 100644 src/charon/bus/bus.c create mode 100644 src/charon/bus/bus.h create mode 100644 src/charon/bus/listeners/file_logger.c create mode 100644 src/charon/bus/listeners/file_logger.h create mode 100644 src/charon/bus/listeners/sys_logger.c create mode 100644 src/charon/bus/listeners/sys_logger.h create mode 100755 src/charon/config/configuration.c create mode 100755 src/charon/config/configuration.h create mode 100644 src/charon/config/connections/connection.c create mode 100644 src/charon/config/connections/connection.h create mode 100755 src/charon/config/connections/connection_store.h create mode 100644 src/charon/config/connections/local_connection_store.c create mode 100644 src/charon/config/connections/local_connection_store.h create mode 100644 src/charon/config/credentials/local_credential_store.c create mode 100644 src/charon/config/credentials/local_credential_store.h create mode 100644 src/charon/config/policies/local_policy_store.c create mode 100644 src/charon/config/policies/local_policy_store.h create mode 100644 src/charon/config/policies/policy.c create mode 100644 src/charon/config/policies/policy.h create mode 100755 src/charon/config/policies/policy_store.h create mode 100644 src/charon/config/proposal.c create mode 100644 src/charon/config/proposal.h create mode 100644 src/charon/config/traffic_selector.c create mode 100644 src/charon/config/traffic_selector.h create mode 100644 src/charon/daemon.c create mode 100644 src/charon/daemon.h create mode 100644 src/charon/encoding/generator.c create mode 100644 src/charon/encoding/generator.h create mode 100644 src/charon/encoding/message.c create mode 100644 src/charon/encoding/message.h create mode 100644 src/charon/encoding/parser.c create mode 100644 src/charon/encoding/parser.h create mode 100644 src/charon/encoding/payloads/auth_payload.c create mode 100644 src/charon/encoding/payloads/auth_payload.h create mode 100644 src/charon/encoding/payloads/cert_payload.c create mode 100644 src/charon/encoding/payloads/cert_payload.h create mode 100644 src/charon/encoding/payloads/certreq_payload.c create mode 100644 src/charon/encoding/payloads/certreq_payload.h create mode 100644 src/charon/encoding/payloads/configuration_attribute.c create mode 100644 src/charon/encoding/payloads/configuration_attribute.h create mode 100644 src/charon/encoding/payloads/cp_payload.c create mode 100644 src/charon/encoding/payloads/cp_payload.h create mode 100644 src/charon/encoding/payloads/delete_payload.c create mode 100644 src/charon/encoding/payloads/delete_payload.h create mode 100644 src/charon/encoding/payloads/eap_payload.c create mode 100644 src/charon/encoding/payloads/eap_payload.h create mode 100644 src/charon/encoding/payloads/encodings.c create mode 100644 src/charon/encoding/payloads/encodings.h create mode 100644 src/charon/encoding/payloads/encryption_payload.c create mode 100644 src/charon/encoding/payloads/encryption_payload.h create mode 100644 src/charon/encoding/payloads/id_payload.c create mode 100644 src/charon/encoding/payloads/id_payload.h create mode 100644 src/charon/encoding/payloads/ike_header.c create mode 100644 src/charon/encoding/payloads/ike_header.h create mode 100644 src/charon/encoding/payloads/ke_payload.c create mode 100644 src/charon/encoding/payloads/ke_payload.h create mode 100644 src/charon/encoding/payloads/nonce_payload.c create mode 100644 src/charon/encoding/payloads/nonce_payload.h create mode 100644 src/charon/encoding/payloads/notify_payload.c create mode 100644 src/charon/encoding/payloads/notify_payload.h create mode 100644 src/charon/encoding/payloads/payload.c create mode 100644 src/charon/encoding/payloads/payload.h create mode 100644 src/charon/encoding/payloads/proposal_substructure.c create mode 100644 src/charon/encoding/payloads/proposal_substructure.h create mode 100644 src/charon/encoding/payloads/sa_payload.c create mode 100644 src/charon/encoding/payloads/sa_payload.h create mode 100644 src/charon/encoding/payloads/traffic_selector_substructure.c create mode 100644 src/charon/encoding/payloads/traffic_selector_substructure.h create mode 100644 src/charon/encoding/payloads/transform_attribute.c create mode 100644 src/charon/encoding/payloads/transform_attribute.h create mode 100644 src/charon/encoding/payloads/transform_substructure.c create mode 100644 src/charon/encoding/payloads/transform_substructure.h create mode 100644 src/charon/encoding/payloads/ts_payload.c create mode 100644 src/charon/encoding/payloads/ts_payload.h create mode 100644 src/charon/encoding/payloads/unknown_payload.c create mode 100644 src/charon/encoding/payloads/unknown_payload.h create mode 100644 src/charon/encoding/payloads/vendor_id_payload.c create mode 100644 src/charon/encoding/payloads/vendor_id_payload.h create mode 100644 src/charon/network/packet.c create mode 100644 src/charon/network/packet.h create mode 100644 src/charon/network/socket.c create mode 100644 src/charon/network/socket.h create mode 100644 src/charon/queues/event_queue.c create mode 100644 src/charon/queues/event_queue.h create mode 100644 src/charon/queues/job_queue.c create mode 100644 src/charon/queues/job_queue.h create mode 100644 src/charon/queues/jobs/acquire_job.c create mode 100644 src/charon/queues/jobs/acquire_job.h create mode 100644 src/charon/queues/jobs/delete_child_sa_job.c create mode 100644 src/charon/queues/jobs/delete_child_sa_job.h create mode 100644 src/charon/queues/jobs/delete_ike_sa_job.c create mode 100644 src/charon/queues/jobs/delete_ike_sa_job.h create mode 100644 src/charon/queues/jobs/initiate_job.c create mode 100644 src/charon/queues/jobs/initiate_job.h create mode 100644 src/charon/queues/jobs/job.c create mode 100644 src/charon/queues/jobs/job.h create mode 100644 src/charon/queues/jobs/process_message_job.c create mode 100644 src/charon/queues/jobs/process_message_job.h create mode 100644 src/charon/queues/jobs/rekey_child_sa_job.c create mode 100644 src/charon/queues/jobs/rekey_child_sa_job.h create mode 100644 src/charon/queues/jobs/rekey_ike_sa_job.c create mode 100644 src/charon/queues/jobs/rekey_ike_sa_job.h create mode 100644 src/charon/queues/jobs/retransmit_job.c create mode 100644 src/charon/queues/jobs/retransmit_job.h create mode 100644 src/charon/queues/jobs/route_job.c create mode 100644 src/charon/queues/jobs/route_job.h create mode 100644 src/charon/queues/jobs/send_dpd_job.c create mode 100644 src/charon/queues/jobs/send_dpd_job.h create mode 100644 src/charon/queues/jobs/send_keepalive_job.c create mode 100644 src/charon/queues/jobs/send_keepalive_job.h create mode 100644 src/charon/sa/authenticators/authenticator.c create mode 100644 src/charon/sa/authenticators/authenticator.h create mode 100644 src/charon/sa/authenticators/eap/eap_identity.c create mode 100644 src/charon/sa/authenticators/eap/eap_identity.h create mode 100644 src/charon/sa/authenticators/eap/eap_method.c create mode 100644 src/charon/sa/authenticators/eap/eap_method.h create mode 100644 src/charon/sa/authenticators/eap/eap_sim.c create mode 100644 src/charon/sa/authenticators/eap/eap_sim.h create mode 100644 src/charon/sa/authenticators/eap_authenticator.c create mode 100644 src/charon/sa/authenticators/eap_authenticator.h create mode 100644 src/charon/sa/authenticators/psk_authenticator.c create mode 100644 src/charon/sa/authenticators/psk_authenticator.h create mode 100644 src/charon/sa/authenticators/rsa_authenticator.c create mode 100644 src/charon/sa/authenticators/rsa_authenticator.h create mode 100644 src/charon/sa/child_sa.c create mode 100644 src/charon/sa/child_sa.h create mode 100644 src/charon/sa/ike_sa.c create mode 100644 src/charon/sa/ike_sa.h create mode 100644 src/charon/sa/ike_sa_id.c create mode 100644 src/charon/sa/ike_sa_id.h create mode 100644 src/charon/sa/ike_sa_manager.c create mode 100644 src/charon/sa/ike_sa_manager.h create mode 100644 src/charon/sa/task_manager.c create mode 100644 src/charon/sa/task_manager.h create mode 100644 src/charon/sa/tasks/child_create.c create mode 100644 src/charon/sa/tasks/child_create.h create mode 100644 src/charon/sa/tasks/child_delete.c create mode 100644 src/charon/sa/tasks/child_delete.h create mode 100644 src/charon/sa/tasks/child_rekey.c create mode 100644 src/charon/sa/tasks/child_rekey.h create mode 100644 src/charon/sa/tasks/ike_auth.c create mode 100644 src/charon/sa/tasks/ike_auth.h create mode 100644 src/charon/sa/tasks/ike_cert.c create mode 100644 src/charon/sa/tasks/ike_cert.h create mode 100644 src/charon/sa/tasks/ike_config.c create mode 100644 src/charon/sa/tasks/ike_config.h create mode 100644 src/charon/sa/tasks/ike_delete.c create mode 100644 src/charon/sa/tasks/ike_delete.h create mode 100644 src/charon/sa/tasks/ike_dpd.c create mode 100644 src/charon/sa/tasks/ike_dpd.h create mode 100644 src/charon/sa/tasks/ike_init.c create mode 100644 src/charon/sa/tasks/ike_init.h create mode 100644 src/charon/sa/tasks/ike_natd.c create mode 100644 src/charon/sa/tasks/ike_natd.h create mode 100644 src/charon/sa/tasks/ike_rekey.c create mode 100644 src/charon/sa/tasks/ike_rekey.h create mode 100644 src/charon/sa/tasks/task.c create mode 100644 src/charon/sa/tasks/task.h create mode 100644 src/charon/threads/kernel_interface.c create mode 100644 src/charon/threads/kernel_interface.h create mode 100644 src/charon/threads/receiver.c create mode 100644 src/charon/threads/receiver.h create mode 100644 src/charon/threads/scheduler.c create mode 100644 src/charon/threads/scheduler.h create mode 100644 src/charon/threads/sender.c create mode 100644 src/charon/threads/sender.h create mode 100755 src/charon/threads/stroke_interface.c create mode 100644 src/charon/threads/stroke_interface.h create mode 100644 src/charon/threads/thread_pool.c create mode 100644 src/charon/threads/thread_pool.h create mode 100644 src/ipsec/Makefile.am create mode 100644 src/ipsec/Makefile.in create mode 100644 src/ipsec/ipsec.8 create mode 100755 src/ipsec/ipsec.in create mode 100644 src/libcrypto/Makefile.am create mode 100644 src/libcrypto/Makefile.in create mode 100644 src/libcrypto/include/cbc_generic.h create mode 100644 src/libcrypto/include/hmac_generic.h create mode 100644 src/libcrypto/include/md32_common.h create mode 100644 src/libcrypto/libaes/aes.c create mode 100644 src/libcrypto/libaes/aes.h create mode 100644 src/libcrypto/libaes/aes_cbc.c create mode 100644 src/libcrypto/libaes/aes_cbc.h create mode 100644 src/libcrypto/libaes/aes_xcbc_mac.c create mode 100644 src/libcrypto/libaes/aes_xcbc_mac.h create mode 100644 src/libcrypto/libblowfish/bf_enc.c create mode 100644 src/libcrypto/libblowfish/bf_locl.h create mode 100644 src/libcrypto/libblowfish/bf_pi.h create mode 100644 src/libcrypto/libblowfish/bf_skey.c create mode 100644 src/libcrypto/libblowfish/blowfish.h create mode 100644 src/libcrypto/libdes/cbc_enc.c create mode 100644 src/libcrypto/libdes/des.h create mode 100644 src/libcrypto/libdes/des_enc.c create mode 100644 src/libcrypto/libdes/des_locl.h create mode 100644 src/libcrypto/libdes/des_opts.c create mode 100644 src/libcrypto/libdes/des_ver.h create mode 100644 src/libcrypto/libdes/destest.c create mode 100644 src/libcrypto/libdes/ecb_enc.c create mode 100644 src/libcrypto/libdes/fcrypt.c create mode 100644 src/libcrypto/libdes/fcrypt_b.c create mode 100644 src/libcrypto/libdes/podd.h create mode 100644 src/libcrypto/libdes/set_key.c create mode 100644 src/libcrypto/libdes/sk.h create mode 100644 src/libcrypto/libdes/speed.c create mode 100644 src/libcrypto/libdes/spr.h create mode 100644 src/libcrypto/libserpent/serpent.c create mode 100644 src/libcrypto/libserpent/serpent.h create mode 100644 src/libcrypto/libserpent/serpent_cbc.c create mode 100644 src/libcrypto/libserpent/serpent_cbc.h create mode 100644 src/libcrypto/libsha2/hmac_sha2.c create mode 100644 src/libcrypto/libsha2/hmac_sha2.h create mode 100644 src/libcrypto/libsha2/sha2.c create mode 100644 src/libcrypto/libsha2/sha2.h create mode 100644 src/libcrypto/libtwofish/twofish.c create mode 100644 src/libcrypto/libtwofish/twofish.h create mode 100644 src/libcrypto/libtwofish/twofish_cbc.c create mode 100644 src/libcrypto/libtwofish/twofish_cbc.h create mode 100644 src/libfreeswan/Makefile.am create mode 100644 src/libfreeswan/Makefile.in create mode 100644 src/libfreeswan/addrtoa.c create mode 100644 src/libfreeswan/addrtot.c create mode 100644 src/libfreeswan/addrtypeof.c create mode 100644 src/libfreeswan/anyaddr.3 create mode 100644 src/libfreeswan/anyaddr.c create mode 100644 src/libfreeswan/atoaddr.3 create mode 100644 src/libfreeswan/atoaddr.c create mode 100644 src/libfreeswan/atoasr.3 create mode 100644 src/libfreeswan/atoasr.c create mode 100644 src/libfreeswan/atosa.3 create mode 100644 src/libfreeswan/atosa.c create mode 100644 src/libfreeswan/atosubnet.c create mode 100644 src/libfreeswan/atoul.3 create mode 100644 src/libfreeswan/atoul.c create mode 100644 src/libfreeswan/copyright.c create mode 100644 src/libfreeswan/datatot.c create mode 100644 src/libfreeswan/freeswan.h create mode 100644 src/libfreeswan/goodmask.3 create mode 100644 src/libfreeswan/goodmask.c create mode 100644 src/libfreeswan/initaddr.3 create mode 100644 src/libfreeswan/initaddr.c create mode 100644 src/libfreeswan/initsaid.c create mode 100644 src/libfreeswan/initsubnet.3 create mode 100644 src/libfreeswan/initsubnet.c create mode 100644 src/libfreeswan/internal.h create mode 100644 src/libfreeswan/ipcomp.h create mode 100644 src/libfreeswan/ipsec_ah.h create mode 100644 src/libfreeswan/ipsec_alg.h create mode 100644 src/libfreeswan/ipsec_encap.h create mode 100644 src/libfreeswan/ipsec_eroute.h create mode 100644 src/libfreeswan/ipsec_errs.h create mode 100644 src/libfreeswan/ipsec_esp.h create mode 100644 src/libfreeswan/ipsec_ipe4.h create mode 100644 src/libfreeswan/ipsec_kversion.h create mode 100644 src/libfreeswan/ipsec_life.h create mode 100644 src/libfreeswan/ipsec_md5h.h create mode 100644 src/libfreeswan/ipsec_param.h create mode 100644 src/libfreeswan/ipsec_policy.h create mode 100644 src/libfreeswan/ipsec_proto.h create mode 100644 src/libfreeswan/ipsec_radij.h create mode 100644 src/libfreeswan/ipsec_rcv.h create mode 100644 src/libfreeswan/ipsec_sa.h create mode 100644 src/libfreeswan/ipsec_sha1.h create mode 100644 src/libfreeswan/ipsec_stats.h create mode 100644 src/libfreeswan/ipsec_tunnel.h create mode 100644 src/libfreeswan/ipsec_xform.h create mode 100644 src/libfreeswan/ipsec_xmit.h create mode 100644 src/libfreeswan/keyblobtoid.3 create mode 100644 src/libfreeswan/keyblobtoid.c create mode 100644 src/libfreeswan/optionsfrom.3 create mode 100644 src/libfreeswan/optionsfrom.c create mode 100644 src/libfreeswan/pfkey.h create mode 100644 src/libfreeswan/pfkey_v2_build.c create mode 100644 src/libfreeswan/pfkey_v2_debug.c create mode 100644 src/libfreeswan/pfkey_v2_ext_bits.c create mode 100644 src/libfreeswan/pfkey_v2_parse.c create mode 100644 src/libfreeswan/pfkeyv2.h create mode 100644 src/libfreeswan/portof.3 create mode 100644 src/libfreeswan/portof.c create mode 100644 src/libfreeswan/prng.3 create mode 100644 src/libfreeswan/prng.c create mode 100644 src/libfreeswan/radij.h create mode 100644 src/libfreeswan/rangetoa.c create mode 100644 src/libfreeswan/rangetosubnet.3 create mode 100644 src/libfreeswan/rangetosubnet.c create mode 100644 src/libfreeswan/sameaddr.3 create mode 100644 src/libfreeswan/sameaddr.c create mode 100644 src/libfreeswan/satoa.c create mode 100644 src/libfreeswan/satot.c create mode 100644 src/libfreeswan/subnetof.3 create mode 100644 src/libfreeswan/subnetof.c create mode 100644 src/libfreeswan/subnettoa.c create mode 100644 src/libfreeswan/subnettot.c create mode 100644 src/libfreeswan/subnettypeof.c create mode 100644 src/libfreeswan/ttoaddr.3 create mode 100644 src/libfreeswan/ttoaddr.c create mode 100644 src/libfreeswan/ttodata.3 create mode 100644 src/libfreeswan/ttodata.c create mode 100644 src/libfreeswan/ttoprotoport.c create mode 100644 src/libfreeswan/ttosa.3 create mode 100644 src/libfreeswan/ttosa.c create mode 100644 src/libfreeswan/ttosubnet.c create mode 100644 src/libfreeswan/ttoul.3 create mode 100644 src/libfreeswan/ttoul.c create mode 100644 src/libfreeswan/ultoa.c create mode 100644 src/libfreeswan/ultot.c create mode 100644 src/libfreeswan/version.3 create mode 100644 src/libfreeswan/version.c create mode 100644 src/libstrongswan/Makefile.am create mode 100644 src/libstrongswan/Makefile.in create mode 100644 src/libstrongswan/asn1/asn1.c create mode 100644 src/libstrongswan/asn1/asn1.h create mode 100644 src/libstrongswan/asn1/oid.c create mode 100644 src/libstrongswan/asn1/oid.h create mode 100644 src/libstrongswan/asn1/oid.pl create mode 100644 src/libstrongswan/asn1/oid.txt create mode 100755 src/libstrongswan/asn1/pem.c create mode 100755 src/libstrongswan/asn1/pem.h create mode 100644 src/libstrongswan/asn1/ttodata.c create mode 100644 src/libstrongswan/asn1/ttodata.h create mode 100644 src/libstrongswan/chunk.c create mode 100644 src/libstrongswan/chunk.h create mode 100755 src/libstrongswan/credential_store.h create mode 100644 src/libstrongswan/crypto/ca.c create mode 100644 src/libstrongswan/crypto/ca.h create mode 100644 src/libstrongswan/crypto/certinfo.c create mode 100644 src/libstrongswan/crypto/certinfo.h create mode 100755 src/libstrongswan/crypto/crl.c create mode 100755 src/libstrongswan/crypto/crl.h create mode 100644 src/libstrongswan/crypto/crypters/aes_cbc_crypter.c create mode 100644 src/libstrongswan/crypto/crypters/aes_cbc_crypter.h create mode 100644 src/libstrongswan/crypto/crypters/crypter.c create mode 100644 src/libstrongswan/crypto/crypters/crypter.h create mode 100644 src/libstrongswan/crypto/crypters/des_crypter.c create mode 100644 src/libstrongswan/crypto/crypters/des_crypter.h create mode 100644 src/libstrongswan/crypto/diffie_hellman.c create mode 100644 src/libstrongswan/crypto/diffie_hellman.h create mode 100644 src/libstrongswan/crypto/hashers/hasher.c create mode 100644 src/libstrongswan/crypto/hashers/hasher.h create mode 100644 src/libstrongswan/crypto/hashers/md5_hasher.c create mode 100644 src/libstrongswan/crypto/hashers/md5_hasher.h create mode 100644 src/libstrongswan/crypto/hashers/sha1_hasher.c create mode 100644 src/libstrongswan/crypto/hashers/sha1_hasher.h create mode 100644 src/libstrongswan/crypto/hashers/sha2_hasher.c create mode 100644 src/libstrongswan/crypto/hashers/sha2_hasher.h create mode 100644 src/libstrongswan/crypto/hmac.c create mode 100644 src/libstrongswan/crypto/hmac.h create mode 100644 src/libstrongswan/crypto/ocsp.c create mode 100644 src/libstrongswan/crypto/ocsp.h create mode 100644 src/libstrongswan/crypto/prf_plus.c create mode 100644 src/libstrongswan/crypto/prf_plus.h create mode 100644 src/libstrongswan/crypto/prfs/fips_prf.c create mode 100644 src/libstrongswan/crypto/prfs/fips_prf.h create mode 100644 src/libstrongswan/crypto/prfs/hmac_prf.c create mode 100644 src/libstrongswan/crypto/prfs/hmac_prf.h create mode 100644 src/libstrongswan/crypto/prfs/prf.c create mode 100644 src/libstrongswan/crypto/prfs/prf.h create mode 100644 src/libstrongswan/crypto/rsa/rsa_private_key.c create mode 100644 src/libstrongswan/crypto/rsa/rsa_private_key.h create mode 100644 src/libstrongswan/crypto/rsa/rsa_public_key.c create mode 100644 src/libstrongswan/crypto/rsa/rsa_public_key.h create mode 100644 src/libstrongswan/crypto/signers/hmac_signer.c create mode 100644 src/libstrongswan/crypto/signers/hmac_signer.h create mode 100644 src/libstrongswan/crypto/signers/signer.c create mode 100644 src/libstrongswan/crypto/signers/signer.h create mode 100755 src/libstrongswan/crypto/x509.c create mode 100755 src/libstrongswan/crypto/x509.h create mode 100644 src/libstrongswan/debug.c create mode 100644 src/libstrongswan/debug.h create mode 100644 src/libstrongswan/enum.c create mode 100644 src/libstrongswan/enum.h create mode 100644 src/libstrongswan/library.c create mode 100644 src/libstrongswan/library.h create mode 100644 src/libstrongswan/printf_hook.c create mode 100644 src/libstrongswan/printf_hook.h create mode 100644 src/libstrongswan/utils/fetcher.c create mode 100644 src/libstrongswan/utils/fetcher.h create mode 100644 src/libstrongswan/utils/host.c create mode 100644 src/libstrongswan/utils/host.h create mode 100644 src/libstrongswan/utils/identification.c create mode 100644 src/libstrongswan/utils/identification.h create mode 100644 src/libstrongswan/utils/iterator.h create mode 100644 src/libstrongswan/utils/leak_detective.c create mode 100644 src/libstrongswan/utils/leak_detective.h create mode 100644 src/libstrongswan/utils/lexparser.c create mode 100644 src/libstrongswan/utils/lexparser.h create mode 100644 src/libstrongswan/utils/linked_list.c create mode 100644 src/libstrongswan/utils/linked_list.h create mode 100644 src/libstrongswan/utils/randomizer.c create mode 100644 src/libstrongswan/utils/randomizer.h create mode 100644 src/openac/Makefile.am create mode 100644 src/openac/Makefile.in create mode 100644 src/openac/build.c create mode 100644 src/openac/build.h create mode 100644 src/openac/loglite.c create mode 100644 src/openac/openac.8 create mode 100755 src/openac/openac.c create mode 100644 src/pluto/Makefile.am create mode 100644 src/pluto/Makefile.in create mode 100644 src/pluto/TODO create mode 100644 src/pluto/ac.c create mode 100644 src/pluto/ac.h create mode 100644 src/pluto/adns.c create mode 100644 src/pluto/adns.h create mode 100644 src/pluto/alg/ike_alg_aes.c create mode 100644 src/pluto/alg/ike_alg_blowfish.c create mode 100644 src/pluto/alg/ike_alg_serpent.c create mode 100644 src/pluto/alg/ike_alg_sha2.c create mode 100644 src/pluto/alg/ike_alg_twofish.c create mode 100644 src/pluto/alg/ike_alginit.c create mode 100644 src/pluto/alg_info.c create mode 100644 src/pluto/alg_info.h create mode 100644 src/pluto/asn1.c create mode 100644 src/pluto/asn1.h create mode 100644 src/pluto/ca.c create mode 100644 src/pluto/ca.h create mode 100644 src/pluto/certs.c create mode 100644 src/pluto/certs.h create mode 100644 src/pluto/connections.c create mode 100644 src/pluto/connections.h create mode 100644 src/pluto/constants.c create mode 100644 src/pluto/constants.h create mode 100644 src/pluto/cookie.c create mode 100644 src/pluto/cookie.h create mode 100644 src/pluto/crl.c create mode 100644 src/pluto/crl.h create mode 100644 src/pluto/crypto.c create mode 100644 src/pluto/crypto.h create mode 100644 src/pluto/db_ops.c create mode 100644 src/pluto/db_ops.h create mode 100644 src/pluto/defs.c create mode 100644 src/pluto/defs.h create mode 100644 src/pluto/demux.c create mode 100644 src/pluto/demux.h create mode 100644 src/pluto/dnskey.c create mode 100644 src/pluto/dnskey.h create mode 100644 src/pluto/dsa.c create mode 100644 src/pluto/dsa.h create mode 100644 src/pluto/elgamal.c create mode 100644 src/pluto/elgamal.h create mode 100644 src/pluto/fetch.c create mode 100644 src/pluto/fetch.h create mode 100644 src/pluto/foodgroups.c create mode 100644 src/pluto/foodgroups.h create mode 100644 src/pluto/gcryptfix.c create mode 100644 src/pluto/gcryptfix.h create mode 100644 src/pluto/id.c create mode 100644 src/pluto/id.h create mode 100644 src/pluto/ike_alg.c create mode 100644 src/pluto/ike_alg.h create mode 100644 src/pluto/ipsec.secrets.5 create mode 100644 src/pluto/ipsec_doi.c create mode 100644 src/pluto/ipsec_doi.h create mode 100644 src/pluto/kameipsec.h create mode 100644 src/pluto/kernel.c create mode 100644 src/pluto/kernel.h create mode 100644 src/pluto/kernel_alg.c create mode 100644 src/pluto/kernel_alg.h create mode 100644 src/pluto/kernel_netlink.c create mode 100644 src/pluto/kernel_netlink.h create mode 100644 src/pluto/kernel_noklips.c create mode 100644 src/pluto/kernel_noklips.h create mode 100644 src/pluto/kernel_pfkey.c create mode 100644 src/pluto/kernel_pfkey.h create mode 100644 src/pluto/keys.c create mode 100644 src/pluto/keys.h create mode 100644 src/pluto/lex.c create mode 100644 src/pluto/lex.h create mode 100644 src/pluto/linux26/netlink.h create mode 100644 src/pluto/linux26/rtnetlink.h create mode 100644 src/pluto/linux26/xfrm.h create mode 100644 src/pluto/log.c create mode 100644 src/pluto/log.h create mode 100644 src/pluto/md2.c create mode 100644 src/pluto/md2.h create mode 100644 src/pluto/md5.c create mode 100644 src/pluto/md5.h create mode 100644 src/pluto/modecfg.c create mode 100644 src/pluto/modecfg.h create mode 100644 src/pluto/mp_defs.c create mode 100644 src/pluto/mp_defs.h create mode 100644 src/pluto/nat_traversal.c create mode 100644 src/pluto/nat_traversal.h create mode 100644 src/pluto/ocsp.c create mode 100644 src/pluto/ocsp.h create mode 100644 src/pluto/oid.c create mode 100644 src/pluto/oid.h create mode 100644 src/pluto/oid.pl create mode 100644 src/pluto/oid.txt create mode 100644 src/pluto/packet.c create mode 100644 src/pluto/packet.h create mode 100644 src/pluto/pem.c create mode 100644 src/pluto/pem.h create mode 100644 src/pluto/pgp.c create mode 100644 src/pluto/pgp.h create mode 100644 src/pluto/pkcs1.c create mode 100644 src/pluto/pkcs1.h create mode 100644 src/pluto/pkcs7.c create mode 100644 src/pluto/pkcs7.h create mode 100644 src/pluto/pluto.8 create mode 100644 src/pluto/plutomain.c create mode 100644 src/pluto/primegen.c create mode 100644 src/pluto/rcv_whack.c create mode 100644 src/pluto/rcv_whack.h create mode 100644 src/pluto/rnd.c create mode 100644 src/pluto/rnd.h create mode 100644 src/pluto/rsaref/pkcs11.h create mode 100644 src/pluto/rsaref/pkcs11f.h create mode 100644 src/pluto/rsaref/pkcs11t.h create mode 100644 src/pluto/rsaref/unix.h create mode 100644 src/pluto/server.c create mode 100644 src/pluto/server.h create mode 100644 src/pluto/sha1.c create mode 100644 src/pluto/sha1.h create mode 100644 src/pluto/smallprime.c create mode 100644 src/pluto/smartcard.c create mode 100644 src/pluto/smartcard.h create mode 100644 src/pluto/spdb.c create mode 100644 src/pluto/spdb.h create mode 100644 src/pluto/state.c create mode 100644 src/pluto/state.h create mode 100644 src/pluto/timer.c create mode 100644 src/pluto/timer.h create mode 100644 src/pluto/vendor.c create mode 100644 src/pluto/vendor.h create mode 100644 src/pluto/virtual.c create mode 100644 src/pluto/virtual.h create mode 100644 src/pluto/x509.c create mode 100644 src/pluto/x509.h create mode 100644 src/pluto/xauth.c create mode 100644 src/pluto/xauth.h create mode 100644 src/scepclient/Makefile.am create mode 100644 src/scepclient/Makefile.in create mode 100644 src/scepclient/pkcs10.c create mode 100644 src/scepclient/pkcs10.h create mode 100644 src/scepclient/rsakey.c create mode 100644 src/scepclient/rsakey.h create mode 100644 src/scepclient/scep.c create mode 100644 src/scepclient/scep.h create mode 100644 src/scepclient/scepclient.8 create mode 100644 src/scepclient/scepclient.c create mode 100644 src/starter/Makefile.am create mode 100644 src/starter/Makefile.in create mode 100644 src/starter/README create mode 100644 src/starter/args.c create mode 100644 src/starter/args.h create mode 100644 src/starter/cmp.c create mode 100644 src/starter/cmp.h create mode 100644 src/starter/confread.c create mode 100644 src/starter/confread.h create mode 100644 src/starter/exec.c create mode 100644 src/starter/exec.h create mode 100644 src/starter/files.h create mode 100644 src/starter/interfaces.c create mode 100644 src/starter/interfaces.h create mode 100644 src/starter/invokecharon.c create mode 100644 src/starter/invokecharon.h create mode 100644 src/starter/invokepluto.c create mode 100644 src/starter/invokepluto.h create mode 100644 src/starter/ipsec.conf create mode 100644 src/starter/ipsec.conf.5 create mode 100644 src/starter/keywords.c create mode 100644 src/starter/keywords.h create mode 100644 src/starter/keywords.txt create mode 100644 src/starter/lex.yy.c create mode 100644 src/starter/netkey.c create mode 100644 src/starter/netkey.h create mode 100644 src/starter/parser.h create mode 100644 src/starter/parser.l create mode 100644 src/starter/parser.y create mode 100644 src/starter/starter.c create mode 100644 src/starter/starterstroke.c create mode 100644 src/starter/starterstroke.h create mode 100644 src/starter/starterwhack.c create mode 100644 src/starter/starterwhack.h create mode 100644 src/starter/y.tab.c create mode 100644 src/starter/y.tab.h create mode 100644 src/stroke/Makefile.am create mode 100644 src/stroke/Makefile.in create mode 100644 src/stroke/stroke.c create mode 100644 src/stroke/stroke.h create mode 100644 src/stroke/stroke_keywords.c create mode 100644 src/stroke/stroke_keywords.h create mode 100644 src/stroke/stroke_keywords.txt create mode 100644 src/whack/Makefile.am create mode 100644 src/whack/Makefile.in create mode 100644 src/whack/whack.c create mode 100644 src/whack/whack.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..a3f90f39e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = libfreeswan libcrypto libstrongswan pluto whack charon stroke starter openac scepclient ipsec _updown _updown_espmark _copyright diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 000000000..6fa95d413 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,497 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = libfreeswan libcrypto libstrongswan pluto whack charon stroke starter openac scepclient ipsec _updown _updown_espmark _copyright +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-libtool clean-recursive ctags \ + ctags-recursive distclean distclean-generic distclean-libtool \ + distclean-recursive distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive \ + mostlyclean mostlyclean-generic mostlyclean-libtool \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/_copyright/Makefile.am b/src/_copyright/Makefile.am new file mode 100644 index 000000000..d8dcfb3f1 --- /dev/null +++ b/src/_copyright/Makefile.am @@ -0,0 +1,6 @@ +ipsec_PROGRAMS = _copyright +_copyright_SOURCES = _copyright.c +dist_man8_MANS = _copyright.8 + +INCLUDES = -I$(top_srcdir)/src/libfreeswan +_copyright_LDADD = $(top_srcdir)/src/libfreeswan/libfreeswan.a diff --git a/src/_copyright/Makefile.in b/src/_copyright/Makefile.in new file mode 100644 index 000000000..7e78b9185 --- /dev/null +++ b/src/_copyright/Makefile.in @@ -0,0 +1,529 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +ipsec_PROGRAMS = _copyright$(EXEEXT) +subdir = src/_copyright +DIST_COMMON = $(dist_man8_MANS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)" +ipsecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(ipsec_PROGRAMS) +am__copyright_OBJECTS = _copyright.$(OBJEXT) +_copyright_OBJECTS = $(am__copyright_OBJECTS) +_copyright_DEPENDENCIES = $(top_srcdir)/src/libfreeswan/libfreeswan.a +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(_copyright_SOURCES) +DIST_SOURCES = $(_copyright_SOURCES) +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man8_MANS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +_copyright_SOURCES = _copyright.c +dist_man8_MANS = _copyright.8 +INCLUDES = -I$(top_srcdir)/src/libfreeswan +_copyright_LDADD = $(top_srcdir)/src/libfreeswan/libfreeswan.a +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/_copyright/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/_copyright/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-ipsecPROGRAMS: $(ipsec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ipsecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-ipsecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +clean-ipsecPROGRAMS: + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +_copyright$(EXEEXT): $(_copyright_OBJECTS) $(_copyright_DEPENDENCIES) + @rm -f _copyright$(EXEEXT) + $(LINK) $(_copyright_LDFLAGS) $(_copyright_OBJECTS) $(_copyright_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/_copyright.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-ipsecPROGRAMS clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-ipsecPROGRAMS install-man + +install-exec-am: + +install-info: install-info-am + +install-man: install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-ipsecPROGRAMS uninstall-man + +uninstall-man: uninstall-man8 + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-ipsecPROGRAMS clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-ipsecPROGRAMS install-man install-man8 install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-ipsecPROGRAMS uninstall-man uninstall-man8 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/_copyright/_copyright.8 b/src/_copyright/_copyright.8 new file mode 100644 index 000000000..87e4adc98 --- /dev/null +++ b/src/_copyright/_copyright.8 @@ -0,0 +1,32 @@ +.TH _COPYRIGHT 8 "25 Apr 2002" +.\" +.\" RCSID $Id: _copyright.8,v 1.1 2004/03/15 20:35:27 as Exp $ +.\" +.SH NAME +ipsec _copyright \- prints FreeSWAN copyright +.SH DESCRIPTION +.I _copyright +outputs the FreeSWAN copyright, and version numbers for "ipsec --copyright" +.SH "SEE ALSO" +ipsec(8) +.SH HISTORY +Man page written for the Linux FreeS/WAN project + +by Michael Richardson. Program written by Henry Spencer. +.\" +.\" $Log: _copyright.8,v $ +.\" Revision 1.1 2004/03/15 20:35:27 as +.\" added files from freeswan-2.04-x509-1.5.3 +.\" +.\" Revision 1.2 2002/04/29 22:39:31 mcr +.\" added basic man page for all internal commands. +.\" +.\" Revision 1.1 2002/04/26 01:21:43 mcr +.\" while tracking down a missing (not installed) /etc/ipsec.conf, +.\" MCR has decided that it is not okay for each program subdir to have +.\" some subset (determined with -f) of possible files. +.\" Each subdir that defines $PROGRAM, MUST have a PROGRAM.8 file as well as a PROGRAM file. +.\" Optional PROGRAM.5 files have been added to the makefiles. +.\" +.\" +.\" diff --git a/src/_copyright/_copyright.c b/src/_copyright/_copyright.c new file mode 100644 index 000000000..0fb360f40 --- /dev/null +++ b/src/_copyright/_copyright.c @@ -0,0 +1,69 @@ +/* + * copyright reporter + * (just avoids having the info in more than one place in the source) + * Copyright (C) 2001 Henry Spencer. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: _copyright.c,v 1.1 2004/03/15 20:35:27 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +char usage[] = "Usage: ipsec _copyright"; +struct option opts[] = { + {"help", 0, NULL, 'h',}, + {"version", 0, NULL, 'v',}, + {0, 0, NULL, 0, }, +}; + +char me[] = "ipsec _copyright"; /* for messages */ + +int +main(int argc, char *argv[]) +{ + int opt; + extern int optind; + int errflg = 0; + const char *version = ipsec_version_code(); + const char **notice = ipsec_copyright_notice(); + const char **co; + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF) + switch (opt) { + case 'h': /* help */ + printf("%s\n", usage); + exit(0); + break; + case 'v': /* version */ + printf("%s %s\n", me, version); + exit(0); + break; + case '?': + default: + errflg = 1; + break; + } + if (errflg || optind != argc) { + fprintf(stderr, "%s\n", usage); + exit(2); + } + + for (co = notice; *co != NULL; co++) + printf("%s\n", *co); + exit(0); +} diff --git a/src/_updown/Makefile.am b/src/_updown/Makefile.am new file mode 100644 index 000000000..27a467c4f --- /dev/null +++ b/src/_updown/Makefile.am @@ -0,0 +1,3 @@ +dist_ipsec_SCRIPTS = _updown +dist_man8_MANS = _updown.8 + diff --git a/src/_updown/Makefile.in b/src/_updown/Makefile.in new file mode 100644 index 000000000..ccb176fbc --- /dev/null +++ b/src/_updown/Makefile.in @@ -0,0 +1,421 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/_updown +DIST_COMMON = $(dist_ipsec_SCRIPTS) $(dist_man8_MANS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)" +dist_ipsecSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(dist_ipsec_SCRIPTS) +SOURCES = +DIST_SOURCES = +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man8_MANS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +dist_ipsec_SCRIPTS = _updown +dist_man8_MANS = _updown.8 +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/_updown/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/_updown/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-dist_ipsecSCRIPTS: $(dist_ipsec_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(dist_ipsec_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(dist_ipsecSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(dist_ipsecSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(ipsecdir)/$$f"; \ + else :; fi; \ + done + +uninstall-dist_ipsecSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dist_ipsec_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-dist_ipsecSCRIPTS install-man + +install-exec-am: + +install-info: install-info-am + +install-man: install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dist_ipsecSCRIPTS uninstall-info-am \ + uninstall-man + +uninstall-man: uninstall-man8 + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dist_ipsecSCRIPTS \ + install-exec install-exec-am install-info install-info-am \ + install-man install-man8 install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-dist_ipsecSCRIPTS uninstall-info-am uninstall-man \ + uninstall-man8 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/_updown/_updown b/src/_updown/_updown new file mode 100755 index 000000000..8db74f737 --- /dev/null +++ b/src/_updown/_updown @@ -0,0 +1,503 @@ +#! /bin/sh +# iproute2 version, default updown script +# +# Copyright (C) 2003-2004 Nigel Meteringham +# Copyright (C) 2003-2004 Tuomo Soini +# Copyright (C) 2002-2004 Michael Richardson +# Copyright (C) 2005-2006 Andreas Steffen +# +# This program 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. See . +# +# 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 General Public License +# for more details. +# +# RCSID $Id: _updown.in,v 1.2 2006/04/17 15:06:29 as Exp $ + +# CAUTION: Installing a new version of strongSwan will install a new +# copy of this script, wiping out any custom changes you make. If +# you need changes, make a copy of this under another name, and customize +# that, and use the (left/right)updown parameters in ipsec.conf to make +# strongSwan use yours instead of this default one. + +# things that this script gets (from ipsec_pluto(8) man page) +# +# PLUTO_VERSION +# indicates what version of this interface is being +# used. This document describes version 1.1. This +# is upwardly compatible with version 1.0. +# +# PLUTO_VERB +# specifies the name of the operation to be performed +# (prepare-host, prepare-client, up-host, up-client, +# down-host, or down-client). If the address family +# for security gateway to security gateway communica­ +# tions is IPv6, then a suffix of -v6 is added to the +# verb. +# +# PLUTO_CONNECTION +# is the name of the connection for which we are +# routing. +# +# PLUTO_NEXT_HOP +# is the next hop to which packets bound for the peer +# must be sent. +# +# PLUTO_INTERFACE +# is the name of the ipsec interface to be used. +# +# PLUTO_REQID +# is the requid of the ESP policy +# +# PLUTO_ME +# is the IP address of our host. +# +# PLUTO_MY_ID +# is the ID of our host. +# +# PLUTO_MY_CLIENT +# is the IP address / count of our client subnet. If +# the client is just the host, this will be the +# host's own IP address / max (where max is 32 for +# IPv4 and 128 for IPv6). +# +# PLUTO_MY_CLIENT_NET +# is the IP address of our client net. If the client +# is just the host, this will be the host's own IP +# address. +# +# PLUTO_MY_CLIENT_MASK +# is the mask for our client net. If the client is +# just the host, this will be 255.255.255.255. +# +# PLUTO_MY_SOURCEIP +# if non-empty, then the source address for the route will be +# set to this IP address. +# +# PLUTO_MY_PROTOCOL +# is the IP protocol that will be transported. +# +# PLUTO_MY_PORT +# is the UDP/TCP port to which the IPsec SA is +# restricted on our side. +# +# PLUTO_PEER +# is the IP address of our peer. +# +# PLUTO_PEER_ID +# is the ID of our peer. +# +# PLUTO_PEER_CA +# is the CA which issued the cert of our peer. +# +# PLUTO_PEER_CLIENT +# is the IP address / count of the peer's client sub­ +# net. If the client is just the peer, this will be +# the peer's own IP address / max (where max is 32 +# for IPv4 and 128 for IPv6). +# +# PLUTO_PEER_CLIENT_NET +# is the IP address of the peer's client net. If the +# client is just the peer, this will be the peer's +# own IP address. +# +# PLUTO_PEER_CLIENT_MASK +# is the mask for the peer's client net. If the +# client is just the peer, this will be +# 255.255.255.255. +# +# PLUTO_PEER_PROTOCOL +# is the IP protocol that will be transported. +# +# PLUTO_PEER_PORT +# is the UDP/TCP port to which the IPsec SA is +# restricted on the peer side. +# + +# uncomment to log VPN connections +VPN_LOGGING=1 +# +# tag put in front of each log entry: +TAG=vpn +# +# syslog facility and priority used: +FAC_PRIO=local0.notice +# +# to create a special vpn logging file, put the following line into +# the syslog configuration file /etc/syslog.conf: +# +# local0.notice -/var/log/vpn +# + +# check interface version +case "$PLUTO_VERSION" in +1.[0|1]) # Older Pluto?!? Play it safe, script may be using new features. + echo "$0: obsolete interface version \`$PLUTO_VERSION'," >&2 + echo "$0: called by obsolete Pluto?" >&2 + exit 2 + ;; +1.*) ;; +*) echo "$0: unknown interface version \`$PLUTO_VERSION'" >&2 + exit 2 + ;; +esac + +# check parameter(s) +case "$1:$*" in +':') # no parameters + ;; +iptables:iptables) # due to (left/right)firewall; for default script only + ;; +custom:*) # custom parameters (see above CAUTION comment) + ;; +*) echo "$0: unknown parameters \`$*'" >&2 + exit 2 + ;; +esac + +# utility functions for route manipulation +# Meddling with this stuff should not be necessary and requires great care. +uproute() { + doroute add + ip route flush cache +} +downroute() { + doroute delete + ip route flush cache +} + +addsource() { + st=0 + if ! ip -o route get ${PLUTO_MY_SOURCEIP%/*} | grep -q ^local + then + it="ip addr add ${PLUTO_MY_SOURCEIP%/*}/32 dev $PLUTO_INTERFACE" + oops="`eval $it 2>&1`" + st=$? + if test " $oops" = " " -a " $st" != " 0" + then + oops="silent error, exit status $st" + fi + if test " $oops" != " " -o " $st" != " 0" + then + echo "$0: addsource \`$it' failed ($oops)" >&2 + fi + fi + return $st +} + +doroute() { + st=0 + parms="$PLUTO_PEER_CLIENT" + + parms2= + if [ -n "$PLUTO_NEXT_HOP" ] + then + parms2="via $PLUTO_NEXT_HOP" + fi + parms2="$parms2 dev $PLUTO_INTERFACE" + + if [ -z "$PLUTO_MY_SOURCEIP" ] + then + if [ -f /etc/sysconfig/defaultsource ] + then + . /etc/sysconfig/defaultsource + fi + + if [ -f /etc/conf.d/defaultsource ] + then + . /etc/conf.d/defaultsource + fi + + if [ -n "$DEFAULTSOURCE" ] + then + PLUTO_MY_SOURCEIP=$DEFAULTSOURCE + fi + fi + + parms3= + if test "$1" = "add" -a -n "$PLUTO_MY_SOURCEIP" + then + addsource + parms3="$parms3 src ${PLUTO_MY_SOURCEIP%/*}" + fi + + case "$PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK" in + "0.0.0.0/0.0.0.0") + # opportunistic encryption work around + # need to provide route that eclipses default, without + # replacing it. + it="ip route $1 0.0.0.0/1 $parms2 $parms3 && + ip route $1 128.0.0.0/1 $parms2 $parms3" + ;; + *) it="ip route $1 $parms $parms2 $parms3" + ;; + esac + oops="`eval $it 2>&1`" + st=$? + if test " $oops" = " " -a " $st" != " 0" + then + oops="silent error, exit status $st" + fi + if test " $oops" != " " -o " $st" != " 0" + then + echo "$0: doroute \`$it' failed ($oops)" >&2 + fi + return $st +} + +# in the presence of KLIPS and ipsecN interfaces do not use IPSEC_POLICY +if [ `echo "$PLUTO_INTERFACE" | grep "ipsec"` ] +then + IPSEC_POLICY_IN="" + IPSEC_POLICY_OUT="" +else + IPSEC_POLICY="-m policy --pol ipsec --proto esp --reqid $PLUTO_REQID" + IPSEC_POLICY_IN="$IPSEC_POLICY --dir in" + IPSEC_POLICY_OUT="$IPSEC_POLICY --dir out" +fi + +# are there port numbers? +if [ "$PLUTO_MY_PORT" != 0 ] +then + S_MY_PORT="--sport $PLUTO_MY_PORT" + D_MY_PORT="--dport $PLUTO_MY_PORT" +fi +if [ "$PLUTO_PEER_PORT" != 0 ] +then + S_PEER_PORT="--sport $PLUTO_PEER_PORT" + D_PEER_PORT="--dport $PLUTO_PEER_PORT" +fi + +# the big choice +case "$PLUTO_VERB:$1" in +prepare-host:*|prepare-client:*) + # delete possibly-existing route (preliminary to adding a route) + case "$PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK" in + "0.0.0.0/0.0.0.0") + # need to provide route that eclipses default, without + # replacing it. + parms1="0.0.0.0/1" + parms2="128.0.0.0/1" + it="ip route delete $parms1 2>&1 ; ip route delete $parms2 2>&1" + oops="`ip route delete $parms1 2>&1 ; ip route delete $parms2 2>&1`" + ;; + *) + parms="$PLUTO_PEER_CLIENT" + it="ip route delete $parms 2>&1" + oops="`ip route delete $parms 2>&1`" + ;; + esac + status="$?" + if test " $oops" = " " -a " $status" != " 0" + then + oops="silent error, exit status $status" + fi + case "$oops" in + *'RTNETLINK answers: No such process'*) + # This is what route (currently -- not documented!) gives + # for "could not find such a route". + oops= + status=0 + ;; + esac + if test " $oops" != " " -o " $status" != " 0" + then + echo "$0: \`$it' failed ($oops)" >&2 + fi + exit $status + ;; +route-host:*|route-client:*) + # connection to me or my client subnet being routed + uproute + ;; +unroute-host:*|unroute-client:*) + # connection to me or my client subnet being unrouted + downroute + ;; +up-host:) + # connection to me coming up + # If you are doing a custom version, firewall commands go here. + ;; +down-host:) + # connection to me going down + # If you are doing a custom version, firewall commands go here. + ;; +up-client:) + # connection to my client subnet coming up + # If you are doing a custom version, firewall commands go here. + ;; +down-client:) + # connection to my client subnet going down + # If you are doing a custom version, firewall commands go here. + ;; +up-host:iptables) + # connection to me, with (left/right)firewall=yes, coming up + # This is used only by the default updown script, not by your custom + # ones, so do not mess with it; see CAUTION comment up at top. + iptables -I INPUT 1 -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_ME $D_MY_PORT $IPSEC_POLICY_IN -j ACCEPT + iptables -I OUTPUT 1 -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_ME $S_MY_PORT $IPSEC_POLICY_OUT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT -j ACCEPT + # + # log IPsec host connection setup + if [ $VPN_LOGGING ] + then + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME" + else + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME" + fi + fi + ;; +down-host:iptables) + # connection to me, with (left/right)firewall=yes, going down + # This is used only by the default updown script, not by your custom + # ones, so do not mess with it; see CAUTION comment up at top. + iptables -D INPUT -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_ME $D_MY_PORT $IPSEC_POLICY_IN -j ACCEPT + iptables -D OUTPUT -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_ME $S_MY_PORT $IPSEC_POLICY_OUT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT -j ACCEPT + # + # log IPsec host connection teardown + if [ $VPN_LOGGING ] + then + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME" + else + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME" + fi + fi + ;; +up-client:iptables) + # connection to client subnet, with (left/right)firewall=yes, coming up + # This is used only by the default updown script, not by your custom + # ones, so do not mess with it; see CAUTION comment up at top. + if [ "$PLUTO_PEER_CLIENT" != "$PLUTO_MY_SOURCEIP/32" ] + then + iptables -I FORWARD 1 -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT \ + $IPSEC_POLICY_OUT -j ACCEPT + iptables -I FORWARD 1 -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $D_MY_PORT \ + $IPSEC_POLICY_IN -j ACCEPT + fi + # + # a virtual IP requires an INPUT and OUTPUT rule on the host + # or sometimes host access via the internal IP is needed + if [ -n "$PLUTO_MY_SOURCEIP" -o -n "$PLUTO_HOST_ACCESS" ] + then + iptables -I INPUT 1 -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $D_MY_PORT \ + $IPSEC_POLICY_IN -j ACCEPT + iptables -I OUTPUT 1 -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT \ + $IPSEC_POLICY_OUT -j ACCEPT + fi + # + # log IPsec client connection setup + if [ $VPN_LOGGING ] + then + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + else + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + fi + fi + ;; +down-client:iptables) + # connection to client subnet, with (left/right)firewall=yes, going down + # This is used only by the default updown script, not by your custom + # ones, so do not mess with it; see CAUTION comment up at top. + if [ "$PLUTO_PEER_CLIENT" != "$PLUTO_MY_SOURCEIP/32" ] + then + iptables -D FORWARD -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT \ + $IPSEC_POLICY_OUT -j ACCEPT + iptables -D FORWARD -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $D_MY_PORT \ + $IPSEC_POLICY_IN -j ACCEPT + fi + # + # a virtual IP requires an INPUT and OUTPUT rule on the host + # or sometimes host access via the internal IP is needed + if [ -n "$PLUTO_MY_SOURCEIP" -o -n "$PLUTO_HOST_ACCESS" ] + then + iptables -D INPUT -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $D_MY_PORT \ + $IPSEC_POLICY_IN -j ACCEPT + iptables -D OUTPUT -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT \ + $IPSEC_POLICY_OUT -j ACCEPT + fi + # + # log IPsec client connection teardown + if [ $VPN_LOGGING ] + then + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + else + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + fi + fi + ;; +# +# IPv6 +# +prepare-host-v6:*|prepare-client-v6:*) + ;; +route-host-v6:*|route-client-v6:*) + # connection to me or my client subnet being routed + #uproute_v6 + ;; +unroute-host-v6:*|unroute-client-v6:*) + # connection to me or my client subnet being unrouted + #downroute_v6 + ;; +up-host-v6:*) + # connection to me coming up + # If you are doing a custom version, firewall commands go here. + ;; +down-host-v6:*) + # connection to me going down + # If you are doing a custom version, firewall commands go here. + ;; +up-client-v6:) + # connection to my client subnet coming up + # If you are doing a custom version, firewall commands go here. + ;; +down-client-v6:) + # connection to my client subnet going down + # If you are doing a custom version, firewall commands go here. + ;; +*) echo "$0: unknown verb \`$PLUTO_VERB' or parameter \`$1'" >&2 + exit 1 + ;; +esac diff --git a/src/_updown/_updown.8 b/src/_updown/_updown.8 new file mode 100644 index 000000000..5107d3694 --- /dev/null +++ b/src/_updown/_updown.8 @@ -0,0 +1,19 @@ +.TH _UPDOWN 8 "27 Apr 2006" +.\" +.\" RCSID $Id: _updown.8,v 1.2 2006/04/17 06:48:49 as Exp $ +.\" +.SH NAME +ipsec _updown \- route and firewall manipulation script +.SH SYNOPSIS +.I _updown +is invoked by pluto when it has brought up a new connection. This script +is used to insert the appropriate routing entries for IPsec operation. +It can also be used to insert and delete dynamic iptables firewall rules. +The interface to the script is documented in the pluto man page. +.SH "SEE ALSO" +ipsec(8), ipsec_pluto(8). +.SH HISTORY +Man page written for the Linux FreeS/WAN project +by Michael Richardson. Original program written by Henry Spencer. Extended +for the Linux strongSwan project by Andreas +Steffen. diff --git a/src/_updown_espmark/Makefile.am b/src/_updown_espmark/Makefile.am new file mode 100644 index 000000000..456702690 --- /dev/null +++ b/src/_updown_espmark/Makefile.am @@ -0,0 +1,2 @@ +dist_ipsec_SCRIPTS = _updown_espmark +dist_man8_MANS = _updown_espmark.8 diff --git a/src/_updown_espmark/Makefile.in b/src/_updown_espmark/Makefile.in new file mode 100644 index 000000000..0286c8f58 --- /dev/null +++ b/src/_updown_espmark/Makefile.in @@ -0,0 +1,421 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/_updown_espmark +DIST_COMMON = $(dist_ipsec_SCRIPTS) $(dist_man8_MANS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)" +dist_ipsecSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(dist_ipsec_SCRIPTS) +SOURCES = +DIST_SOURCES = +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man8_MANS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +dist_ipsec_SCRIPTS = _updown_espmark +dist_man8_MANS = _updown_espmark.8 +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/_updown_espmark/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/_updown_espmark/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-dist_ipsecSCRIPTS: $(dist_ipsec_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(dist_ipsec_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(dist_ipsecSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(dist_ipsecSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(ipsecdir)/$$f"; \ + else :; fi; \ + done + +uninstall-dist_ipsecSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dist_ipsec_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-dist_ipsecSCRIPTS install-man + +install-exec-am: + +install-info: install-info-am + +install-man: install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dist_ipsecSCRIPTS uninstall-info-am \ + uninstall-man + +uninstall-man: uninstall-man8 + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dist_ipsecSCRIPTS \ + install-exec install-exec-am install-info install-info-am \ + install-man install-man8 install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-dist_ipsecSCRIPTS uninstall-info-am uninstall-man \ + uninstall-man8 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/_updown_espmark/_updown_espmark b/src/_updown_espmark/_updown_espmark new file mode 100644 index 000000000..3627d470d --- /dev/null +++ b/src/_updown_espmark/_updown_espmark @@ -0,0 +1,452 @@ +#! /bin/sh +# iproute2 version, default updown script +# +# Copyright (C) 2003-2004 Nigel Meteringham +# Copyright (C) 2003-2004 Tuomo Soini +# Copyright (C) 2002-2004 Michael Richardson +# Copyright (C) 2005 Andreas Steffen +# +# This program 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. See . +# +# 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 General Public License +# for more details. +# +# RCSID $Id: _updown_espmark.in,v 1.4 2005/09/14 14:33:05 as Exp $ + + + +# CAUTION: Installing a new version of strongSwan will install a new +# copy of this script, wiping out any custom changes you make. If +# you need changes, make a copy of this under another name, and customize +# that, and use the (left/right)updown parameters in ipsec.conf to make +# FreeS/WAN use yours instead of this default one. + +# things that this script gets (from ipsec_pluto(8) man page) +# +# +# PLUTO_VERSION +# indicates what version of this interface is being +# used. This document describes version 1.1. This +# is upwardly compatible with version 1.0. +# +# PLUTO_VERB +# specifies the name of the operation to be performed +# (prepare-host, prepare-client, up-host, up-client, +# down-host, or down-client). If the address family +# for security gateway to security gateway communica­ +# tions is IPv6, then a suffix of -v6 is added to the +# verb. +# +# PLUTO_CONNECTION +# is the name of the connection for which we are +# routing. +# +# PLUTO_NEXT_HOP +# is the next hop to which packets bound for the peer +# must be sent. +# +# PLUTO_INTERFACE +# is the name of the ipsec interface to be used. +# +# PLUTO_ME +# is the IP address of our host. +# +# PLUTO_MY_ID +# is the ID of our host. +# +# PLUTO_MY_CLIENT +# is the IP address / count of our client subnet. If +# the client is just the host, this will be the +# host's own IP address / max (where max is 32 for +# IPv4 and 128 for IPv6). +# +# PLUTO_MY_CLIENT_NET +# is the IP address of our client net. If the client +# is just the host, this will be the host's own IP +# address. +# +# PLUTO_MY_CLIENT_MASK +# is the mask for our client net. If the client is +# just the host, this will be 255.255.255.255. +# +# PLUTO_MY_SOURCEIP +# if non-empty, then the source address for the route will be +# set to this IP address. +# +# PLUTO_MY_PROTOCOL +# is the IP protocol that will be transported. +# +# PLUTO_MY_PORT +# is the UDP/TCP port to which the IPsec SA is +# restricted on our side. +# +# PLUTO_PEER +# is the IP address of our peer. +# +# PLUTO_PEER_ID +# is the ID of our peer. +# +# PLUTO_PEER_CA +# is the CA which issued the cert of our peer. +# +# PLUTO_PEER_CLIENT +# is the IP address / count of the peer's client sub­ +# net. If the client is just the peer, this will be +# the peer's own IP address / max (where max is 32 +# for IPv4 and 128 for IPv6). +# +# PLUTO_PEER_CLIENT_NET +# is the IP address of the peer's client net. If the +# client is just the peer, this will be the peer's +# own IP address. +# +# PLUTO_PEER_CLIENT_MASK +# is the mask for the peer's client net. If the +# client is just the peer, this will be +# 255.255.255.255. +# +# PLUTO_PEER_PROTOCOL +# is the IP protocol that will be transported. +# +# PLUTO_PEER_PORT +# is the UDP/TCP port to which the IPsec SA is +# restricted on the peer side. +# + +# logging of VPN connections +# +# tag put in front of each log entry: +TAG=vpn +# +# syslog facility and priority used: +FAC_PRIO=local0.notice +# +# to create a special vpn logging file, put the following line into +# the syslog configuration file /etc/syslog.conf: +# +# local0.notice -/var/log/vpn +# + +# check interface version +case "$PLUTO_VERSION" in +1.[0]) # Older Pluto?!? Play it safe, script may be using new features. + echo "$0: obsolete interface version \`$PLUTO_VERSION'," >&2 + echo "$0: called by obsolete Pluto?" >&2 + exit 2 + ;; +1.*) ;; +*) echo "$0: unknown interface version \`$PLUTO_VERSION'" >&2 + exit 2 + ;; +esac + +# check parameter(s) +case "$1:$*" in +':') # no parameters + ;; +ipfwadm:ipfwadm) # due to (left/right)firewall; for default script only + ;; +custom:*) # custom parameters (see above CAUTION comment) + ;; +*) echo "$0: unknown parameters \`$*'" >&2 + exit 2 + ;; +esac + +# utility functions for route manipulation +# Meddling with this stuff should not be necessary and requires great care. +uproute() { + doroute add + ip route flush cache +} +downroute() { + doroute delete + ip route flush cache +} + +addsource() { + st=0 + if ! ip -o route get ${PLUTO_MY_SOURCEIP%/*} | grep -q ^local + then + it="ip addr add ${PLUTO_MY_SOURCEIP%/*}/32 dev $PLUTO_INTERFACE" + oops="`eval $it 2>&1`" + st=$? + if test " $oops" = " " -a " $st" != " 0" + then + oops="silent error, exit status $st" + fi + if test " $oops" != " " -o " $st" != " 0" + then + echo "$0: addsource \`$it' failed ($oops)" >&2 + fi + fi + return $st +} + +doroute() { + st=0 + parms="$PLUTO_PEER_CLIENT" + + parms2= + if [ -n "$PLUTO_NEXT_HOP" ] + then + parms2="via $PLUTO_NEXT_HOP" + fi + parms2="$parms2 dev $PLUTO_INTERFACE" + + if [ -z "$PLUTO_MY_SOURCEIP" ] + then + if [ -f /etc/sysconfig/defaultsource ] + then + . /etc/sysconfig/defaultsource + fi + + if [ -f /etc/conf.d/defaultsource ] + then + . /etc/conf.d/defaultsource + fi + + if [ -n "$DEFAULTSOURCE" ] + then + PLUTO_MY_SOURCEIP=$DEFAULTSOURCE + fi + fi + + parms3= + if test "$1" = "add" -a -n "$PLUTO_MY_SOURCEIP" + then + addsource + parms3="$parms3 src ${PLUTO_MY_SOURCEIP%/*}" + fi + + case "$PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK" in + "0.0.0.0/0.0.0.0") + # opportunistic encryption work around + # need to provide route that eclipses default, without + # replacing it. + it="ip route $1 0.0.0.0/1 $parms2 $parms3 && + ip route $1 128.0.0.0/1 $parms2 $parms3" + ;; + *) it="ip route $1 $parms $parms2 $parms3" + ;; + esac + oops="`eval $it 2>&1`" + st=$? + if test " $oops" = " " -a " $st" != " 0" + then + oops="silent error, exit status $st" + fi + if test " $oops" != " " -o " $st" != " 0" + then + echo "$0: doroute \`$it' failed ($oops)" >&2 + fi + return $st +} + +# define ESP mark +ESP_MARK=50 + +# add the following static rule to the INPUT chain in the mangle table +# iptables -t mangle -A INPUT -p 50 -j MARK --set-mark 50 + +# NAT traversal via UDP encapsulation is supported with the rule +# iptables -t mangle -A INPUT -p udp --dport 4500 -j MARK --set-mark 50 + +# in the presence of KLIPS and ipsecN interfaces do not use ESP mark rules +if [ `echo "$PLUTO_INTERFACE" | grep "ipsec"` ] +then + CHECK_MARK="" +else + CHECK_MARK="-m mark --mark $ESP_MARK" +fi + +# are there port numbers? +if [ "$PLUTO_MY_PORT" != 0 ] +then + S_MY_PORT="--sport $PLUTO_MY_PORT" + D_MY_PORT="--dport $PLUTO_MY_PORT" +fi +if [ "$PLUTO_PEER_PORT" != 0 ] +then + S_PEER_PORT="--sport $PLUTO_PEER_PORT" + D_PEER_PORT="--dport $PLUTO_PEER_PORT" +fi + +# the big choice +case "$PLUTO_VERB:$1" in +prepare-host:*|prepare-client:*) + # delete possibly-existing route (preliminary to adding a route) + case "$PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK" in + "0.0.0.0/0.0.0.0") + # need to provide route that eclipses default, without + # replacing it. + parms1="0.0.0.0/1" + parms2="128.0.0.0/1" + it="ip route delete $parms1 2>&1 ; ip route delete $parms2 2>&1" + oops="`ip route delete $parms1 2>&1 ; ip route delete $parms2 2>&1`" + ;; + *) + parms="$PLUTO_PEER_CLIENT" + it="ip route delete $parms 2>&1" + oops="`ip route delete $parms 2>&1`" + ;; + esac + status="$?" + if test " $oops" = " " -a " $status" != " 0" + then + oops="silent error, exit status $status" + fi + case "$oops" in + *'RTNETLINK answers: No such process'*) + # This is what route (currently -- not documented!) gives + # for "could not find such a route". + oops= + status=0 + ;; + esac + if test " $oops" != " " -o " $status" != " 0" + then + echo "$0: \`$it' failed ($oops)" >&2 + fi + exit $status + ;; +route-host:*|route-client:*) + # connection to me or my client subnet being routed + uproute + ;; +unroute-host:*|unroute-client:*) + # connection to me or my client subnet being unrouted + downroute + ;; +up-host:*) + # connection to me coming up + # If you are doing a custom version, firewall commands go here. + iptables -I INPUT 1 -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_ME $D_MY_PORT $CHECK_MARK -j ACCEPT + iptables -I OUTPUT 1 -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_ME $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT -j ACCEPT + # + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME" + else + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME" + fi + ;; +down-host:*) + # connection to me going down + # If you are doing a custom version, firewall commands go here. + # connection to me going down + # If you are doing a custom version, firewall commands go here. + iptables -D INPUT -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_ME $D_MY_PORT $CHECK_MARK -j ACCEPT + iptables -D OUTPUT -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_ME $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT -j ACCEPT + # + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME" + else + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME" + fi + ;; +up-client:) + # connection to my client subnet coming up + # If you are doing a custom version, firewall commands go here. + iptables -I FORWARD 1 -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT -j ACCEPT + iptables -I FORWARD 1 -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $D_MY_PORT \ + $CHECK_MARK -j ACCEPT + # + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + else + logger -t $TAG -p $FAC_PRIO \ + "+ `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + fi + ;; +down-client:) + # connection to my client subnet going down + # If you are doing a custom version, firewall commands go here. + iptables -D FORWARD -o $PLUTO_INTERFACE -p $PLUTO_PEER_PROTOCOL \ + -s $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $S_MY_PORT \ + -d $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $D_PEER_PORT -j ACCEPT + iptables -D FORWARD -i $PLUTO_INTERFACE -p $PLUTO_MY_PROTOCOL \ + -s $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK $S_PEER_PORT \ + -d $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK $D_MY_PORT \ + $CHECK_MARK -j ACCEPT + # + if [ "$PLUTO_PEER_CLIENT" == "$PLUTO_PEER/32" ] + then + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + else + logger -t $TAG -p $FAC_PRIO -- \ + "- `echo -e $PLUTO_PEER_ID` $PLUTO_PEER_CLIENT == $PLUTO_PEER -- $PLUTO_ME == $PLUTO_MY_CLIENT" + fi + ;; +up-client:ipfwadm) + # connection to client subnet, with (left/right)firewall=yes, coming up + # This is used only by the default updown script, not by your custom + # ones, so do not mess with it; see CAUTION comment up at top. + ipfwadm -F -i accept -b -S $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK \ + -D $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK + ;; +down-client:ipfwadm) + # connection to client subnet, with (left/right)firewall=yes, going down + # This is used only by the default updown script, not by your custom + # ones, so do not mess with it; see CAUTION comment up at top. + ipfwadm -F -d accept -b -S $PLUTO_MY_CLIENT_NET/$PLUTO_MY_CLIENT_MASK \ + -D $PLUTO_PEER_CLIENT_NET/$PLUTO_PEER_CLIENT_MASK + ;; +# +# IPv6 +# +prepare-host-v6:*|prepare-client-v6:*) + ;; +route-host-v6:*|route-client-v6:*) + # connection to me or my client subnet being routed + #uproute_v6 + ;; +unroute-host-v6:*|unroute-client-v6:*) + # connection to me or my client subnet being unrouted + #downroute_v6 + ;; +up-host-v6:*) + # connection to me coming up + # If you are doing a custom version, firewall commands go here. + ;; +down-host-v6:*) + # connection to me going down + # If you are doing a custom version, firewall commands go here. + ;; +up-client-v6:) + # connection to my client subnet coming up + # If you are doing a custom version, firewall commands go here. + ;; +down-client-v6:) + # connection to my client subnet going down + # If you are doing a custom version, firewall commands go here. + ;; +*) echo "$0: unknown verb \`$PLUTO_VERB' or parameter \`$1'" >&2 + exit 1 + ;; +esac diff --git a/src/_updown_espmark/_updown_espmark.8 b/src/_updown_espmark/_updown_espmark.8 new file mode 100644 index 000000000..91eaa5cb7 --- /dev/null +++ b/src/_updown_espmark/_updown_espmark.8 @@ -0,0 +1,18 @@ +.TH _UPDOWN_ESPMARK 8 "7 Apr 2005" +.\" +.\" RCSID $Id: _updown_espmark.8,v 1.1 2005/04/07 21:34:19 as Exp $ +.\" +.SH NAME +ipsec _updown_espmark \- manages routes and firewall rules +.SH SYNOPSIS +.I _updown_espmark +is invoked by pluto when it has brought up a new connection. This script +is used to insert the appropriate routing and iptables firewall entries for +IPsec operation. The incoming ESP traffic must be marked by a static rule +in the mangle table. The default value for the mark is 50. +The interface to the script is documented in the pluto man page. +.SH "SEE ALSO" +ipsec(8), ipsec_pluto(8). +.SH HISTORY +Man page written for the Linux strongSwan project +by Andreas Steffen. Original program written by Henry Spencer. diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am new file mode 100644 index 000000000..9522b6e6d --- /dev/null +++ b/src/charon/Makefile.am @@ -0,0 +1,87 @@ +# SUBDIRS = . testing + +eap_LTLIBRARIES = libeapidentity.la + +# always build EAP Identity module +libeapidentity_la_SOURCES = sa/authenticators/eap/eap_identity.h sa/authenticators/eap/eap_identity.c +libeapidentity_la_LDFLAGS = -module + +# build optional EAP modules +if BUILD_EAP_SIM + eap_LTLIBRARIES += libeapsim.la + libeapsim_la_SOURCES = sa/authenticators/eap/eap_sim.h sa/authenticators/eap/eap_sim.c + libeapsim_la_LDFLAGS = -module +endif + +ipsec_PROGRAMS = charon + +charon_SOURCES = \ +bus/bus.c bus/bus.h \ +bus/listeners/sys_logger.c bus/listeners/sys_logger.h \ +bus/listeners/file_logger.c bus/listeners/file_logger.h \ +config/connections/connection.c config/connections/connection.h \ +config/connections/local_connection_store.c config/connections/local_connection_store.h config/connections/connection_store.h \ +config/policies/policy.c config/policies/policy.h \ +config/policies/local_policy_store.c config/policies/policy_store.h config/policies/local_policy_store.h \ +config/credentials/local_credential_store.c config/credentials/local_credential_store.h \ +config/traffic_selector.c config/traffic_selector.h \ +config/proposal.c config/proposal.h config/configuration.c config/configuration.h \ +sa/authenticators/eap_authenticator.h sa/authenticators/eap_authenticator.c \ +sa/authenticators/eap/eap_method.h sa/authenticators/eap/eap_method.c \ +sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \ +sa/ike_sa_id.c sa/ike_sa_id.h sa/tasks/task.c sa/tasks/task.h \ +sa/tasks/ike_init.c sa/tasks/ike_init.h \ +sa/tasks/ike_natd.c sa/tasks/ike_natd.h \ +sa/tasks/ike_auth.c sa/tasks/ike_auth.h \ +sa/tasks/ike_config.c sa/tasks/ike_config.h \ +sa/tasks/ike_cert.c sa/tasks/ike_cert.h \ +sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \ +sa/tasks/ike_delete.c sa/tasks/ike_delete.h \ +sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \ +sa/tasks/child_create.c sa/tasks/child_create.h \ +sa/tasks/child_delete.c sa/tasks/child_delete.h \ +sa/tasks/child_rekey.c sa/tasks/child_rekey.h \ +sa/authenticators/authenticator.c sa/authenticators/authenticator.h \ +sa/authenticators/rsa_authenticator.c sa/authenticators/rsa_authenticator.h \ +sa/authenticators/psk_authenticator.c sa/authenticators/psk_authenticator.h \ +sa/task_manager.c sa/task_manager.h encoding/payloads/encryption_payload.c \ +encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \ +encoding/payloads/configuration_attribute.h encoding/payloads/proposal_substructure.h \ +encoding/payloads/transform_attribute.c encoding/payloads/transform_attribute.h \ +encoding/payloads/configuration_attribute.c encoding/payloads/transform_substructure.c \ +encoding/payloads/encryption_payload.h encoding/payloads/auth_payload.c encoding/payloads/ike_header.c \ +encoding/payloads/transform_substructure.h encoding/payloads/nonce_payload.c encoding/payloads/cert_payload.h \ +encoding/payloads/eap_payload.c encoding/payloads/ike_header.h encoding/payloads/auth_payload.h \ +encoding/payloads/ts_payload.c encoding/payloads/traffic_selector_substructure.h encoding/payloads/nonce_payload.h \ +encoding/payloads/notify_payload.c encoding/payloads/eap_payload.h encoding/payloads/notify_payload.h \ +encoding/payloads/ts_payload.h encoding/payloads/id_payload.c encoding/payloads/ke_payload.c \ +encoding/payloads/unknown_payload.c encoding/payloads/encodings.c encoding/payloads/id_payload.h \ +encoding/payloads/cp_payload.c encoding/payloads/delete_payload.c encoding/payloads/sa_payload.c \ +encoding/payloads/ke_payload.h encoding/payloads/unknown_payload.h encoding/payloads/encodings.h \ +encoding/payloads/certreq_payload.c encoding/payloads/cp_payload.h encoding/payloads/delete_payload.h \ +encoding/payloads/sa_payload.h encoding/payloads/vendor_id_payload.c encoding/payloads/certreq_payload.h \ +encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/payloads/payload.c \ +encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \ +encoding/parser.c daemon.c daemon.h network/packet.c \ +network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \ +queues/jobs/retransmit_job.h queues/jobs/initiate_job.h \ +queues/jobs/process_message_job.h queues/jobs/process_message_job.c \ +queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \ +queues/jobs/retransmit_job.c queues/jobs/initiate_job.c \ +queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ +queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ +queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \ +queues/jobs/acquire_job.c queues/jobs/acquire_job.h queues/jobs/rekey_ike_sa_job.c queues/jobs/rekey_ike_sa_job.h \ +queues/job_queue.c queues/event_queue.c queues/job_queue.h queues/event_queue.h \ +threads/kernel_interface.c threads/thread_pool.c threads/scheduler.c threads/sender.c \ +threads/sender.h threads/kernel_interface.h threads/scheduler.h threads/receiver.c threads/stroke_interface.c \ +threads/thread_pool.h threads/receiver.h threads/stroke_interface.h + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke +AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\" -DIPSEC_EAPDIR=\"${eapdir}\" +charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread -lm -ldl + +if USE_LIBCURL + charon_LDADD += -lcurl +endif + diff --git a/src/charon/Makefile.in b/src/charon/Makefile.in new file mode 100644 index 000000000..0f2979d32 --- /dev/null +++ b/src/charon/Makefile.in @@ -0,0 +1,1878 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +# SUBDIRS = . testing + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ + +# build optional EAP modules +@BUILD_EAP_SIM_TRUE@am__append_1 = libeapsim.la +ipsec_PROGRAMS = charon$(EXEEXT) +@USE_LIBCURL_TRUE@am__append_2 = -lcurl +subdir = src/charon +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(eapdir)" "$(DESTDIR)$(ipsecdir)" +eapLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(eap_LTLIBRARIES) +libeapidentity_la_LIBADD = +am_libeapidentity_la_OBJECTS = eap_identity.lo +libeapidentity_la_OBJECTS = $(am_libeapidentity_la_OBJECTS) +libeapsim_la_LIBADD = +am__libeapsim_la_SOURCES_DIST = sa/authenticators/eap/eap_sim.h \ + sa/authenticators/eap/eap_sim.c +@BUILD_EAP_SIM_TRUE@am_libeapsim_la_OBJECTS = eap_sim.lo +libeapsim_la_OBJECTS = $(am_libeapsim_la_OBJECTS) +@BUILD_EAP_SIM_TRUE@am_libeapsim_la_rpath = -rpath $(eapdir) +ipsecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(ipsec_PROGRAMS) +am_charon_OBJECTS = bus.$(OBJEXT) sys_logger.$(OBJEXT) \ + file_logger.$(OBJEXT) connection.$(OBJEXT) \ + local_connection_store.$(OBJEXT) policy.$(OBJEXT) \ + local_policy_store.$(OBJEXT) local_credential_store.$(OBJEXT) \ + traffic_selector.$(OBJEXT) proposal.$(OBJEXT) \ + configuration.$(OBJEXT) eap_authenticator.$(OBJEXT) \ + eap_method.$(OBJEXT) child_sa.$(OBJEXT) ike_sa.$(OBJEXT) \ + ike_sa_manager.$(OBJEXT) ike_sa_id.$(OBJEXT) task.$(OBJEXT) \ + ike_init.$(OBJEXT) ike_natd.$(OBJEXT) ike_auth.$(OBJEXT) \ + ike_config.$(OBJEXT) ike_cert.$(OBJEXT) ike_rekey.$(OBJEXT) \ + ike_delete.$(OBJEXT) ike_dpd.$(OBJEXT) child_create.$(OBJEXT) \ + child_delete.$(OBJEXT) child_rekey.$(OBJEXT) \ + authenticator.$(OBJEXT) rsa_authenticator.$(OBJEXT) \ + psk_authenticator.$(OBJEXT) task_manager.$(OBJEXT) \ + encryption_payload.$(OBJEXT) cert_payload.$(OBJEXT) \ + traffic_selector_substructure.$(OBJEXT) \ + transform_attribute.$(OBJEXT) \ + configuration_attribute.$(OBJEXT) \ + transform_substructure.$(OBJEXT) auth_payload.$(OBJEXT) \ + ike_header.$(OBJEXT) nonce_payload.$(OBJEXT) \ + eap_payload.$(OBJEXT) ts_payload.$(OBJEXT) \ + notify_payload.$(OBJEXT) id_payload.$(OBJEXT) \ + ke_payload.$(OBJEXT) unknown_payload.$(OBJEXT) \ + encodings.$(OBJEXT) cp_payload.$(OBJEXT) \ + delete_payload.$(OBJEXT) sa_payload.$(OBJEXT) \ + certreq_payload.$(OBJEXT) vendor_id_payload.$(OBJEXT) \ + proposal_substructure.$(OBJEXT) payload.$(OBJEXT) \ + message.$(OBJEXT) generator.$(OBJEXT) parser.$(OBJEXT) \ + daemon.$(OBJEXT) packet.$(OBJEXT) socket.$(OBJEXT) \ + job.$(OBJEXT) process_message_job.$(OBJEXT) \ + delete_ike_sa_job.$(OBJEXT) retransmit_job.$(OBJEXT) \ + initiate_job.$(OBJEXT) send_keepalive_job.$(OBJEXT) \ + rekey_child_sa_job.$(OBJEXT) delete_child_sa_job.$(OBJEXT) \ + send_dpd_job.$(OBJEXT) route_job.$(OBJEXT) \ + acquire_job.$(OBJEXT) rekey_ike_sa_job.$(OBJEXT) \ + job_queue.$(OBJEXT) event_queue.$(OBJEXT) \ + kernel_interface.$(OBJEXT) thread_pool.$(OBJEXT) \ + scheduler.$(OBJEXT) sender.$(OBJEXT) receiver.$(OBJEXT) \ + stroke_interface.$(OBJEXT) +charon_OBJECTS = $(am_charon_OBJECTS) +am__DEPENDENCIES_1 = +charon_DEPENDENCIES = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libeapidentity_la_SOURCES) $(libeapsim_la_SOURCES) \ + $(charon_SOURCES) +DIST_SOURCES = $(libeapidentity_la_SOURCES) \ + $(am__libeapsim_la_SOURCES_DIST) $(charon_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +eap_LTLIBRARIES = libeapidentity.la $(am__append_1) + +# always build EAP Identity module +libeapidentity_la_SOURCES = sa/authenticators/eap/eap_identity.h sa/authenticators/eap/eap_identity.c +libeapidentity_la_LDFLAGS = -module +@BUILD_EAP_SIM_TRUE@libeapsim_la_SOURCES = sa/authenticators/eap/eap_sim.h sa/authenticators/eap/eap_sim.c +@BUILD_EAP_SIM_TRUE@libeapsim_la_LDFLAGS = -module +charon_SOURCES = \ +bus/bus.c bus/bus.h \ +bus/listeners/sys_logger.c bus/listeners/sys_logger.h \ +bus/listeners/file_logger.c bus/listeners/file_logger.h \ +config/connections/connection.c config/connections/connection.h \ +config/connections/local_connection_store.c config/connections/local_connection_store.h config/connections/connection_store.h \ +config/policies/policy.c config/policies/policy.h \ +config/policies/local_policy_store.c config/policies/policy_store.h config/policies/local_policy_store.h \ +config/credentials/local_credential_store.c config/credentials/local_credential_store.h \ +config/traffic_selector.c config/traffic_selector.h \ +config/proposal.c config/proposal.h config/configuration.c config/configuration.h \ +sa/authenticators/eap_authenticator.h sa/authenticators/eap_authenticator.c \ +sa/authenticators/eap/eap_method.h sa/authenticators/eap/eap_method.c \ +sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \ +sa/ike_sa_id.c sa/ike_sa_id.h sa/tasks/task.c sa/tasks/task.h \ +sa/tasks/ike_init.c sa/tasks/ike_init.h \ +sa/tasks/ike_natd.c sa/tasks/ike_natd.h \ +sa/tasks/ike_auth.c sa/tasks/ike_auth.h \ +sa/tasks/ike_config.c sa/tasks/ike_config.h \ +sa/tasks/ike_cert.c sa/tasks/ike_cert.h \ +sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \ +sa/tasks/ike_delete.c sa/tasks/ike_delete.h \ +sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \ +sa/tasks/child_create.c sa/tasks/child_create.h \ +sa/tasks/child_delete.c sa/tasks/child_delete.h \ +sa/tasks/child_rekey.c sa/tasks/child_rekey.h \ +sa/authenticators/authenticator.c sa/authenticators/authenticator.h \ +sa/authenticators/rsa_authenticator.c sa/authenticators/rsa_authenticator.h \ +sa/authenticators/psk_authenticator.c sa/authenticators/psk_authenticator.h \ +sa/task_manager.c sa/task_manager.h encoding/payloads/encryption_payload.c \ +encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \ +encoding/payloads/configuration_attribute.h encoding/payloads/proposal_substructure.h \ +encoding/payloads/transform_attribute.c encoding/payloads/transform_attribute.h \ +encoding/payloads/configuration_attribute.c encoding/payloads/transform_substructure.c \ +encoding/payloads/encryption_payload.h encoding/payloads/auth_payload.c encoding/payloads/ike_header.c \ +encoding/payloads/transform_substructure.h encoding/payloads/nonce_payload.c encoding/payloads/cert_payload.h \ +encoding/payloads/eap_payload.c encoding/payloads/ike_header.h encoding/payloads/auth_payload.h \ +encoding/payloads/ts_payload.c encoding/payloads/traffic_selector_substructure.h encoding/payloads/nonce_payload.h \ +encoding/payloads/notify_payload.c encoding/payloads/eap_payload.h encoding/payloads/notify_payload.h \ +encoding/payloads/ts_payload.h encoding/payloads/id_payload.c encoding/payloads/ke_payload.c \ +encoding/payloads/unknown_payload.c encoding/payloads/encodings.c encoding/payloads/id_payload.h \ +encoding/payloads/cp_payload.c encoding/payloads/delete_payload.c encoding/payloads/sa_payload.c \ +encoding/payloads/ke_payload.h encoding/payloads/unknown_payload.h encoding/payloads/encodings.h \ +encoding/payloads/certreq_payload.c encoding/payloads/cp_payload.h encoding/payloads/delete_payload.h \ +encoding/payloads/sa_payload.h encoding/payloads/vendor_id_payload.c encoding/payloads/certreq_payload.h \ +encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/payloads/payload.c \ +encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \ +encoding/parser.c daemon.c daemon.h network/packet.c \ +network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \ +queues/jobs/retransmit_job.h queues/jobs/initiate_job.h \ +queues/jobs/process_message_job.h queues/jobs/process_message_job.c \ +queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \ +queues/jobs/retransmit_job.c queues/jobs/initiate_job.c \ +queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ +queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ +queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \ +queues/jobs/acquire_job.c queues/jobs/acquire_job.h queues/jobs/rekey_ike_sa_job.c queues/jobs/rekey_ike_sa_job.h \ +queues/job_queue.c queues/event_queue.c queues/job_queue.h queues/event_queue.h \ +threads/kernel_interface.c threads/thread_pool.c threads/scheduler.c threads/sender.c \ +threads/sender.h threads/kernel_interface.h threads/scheduler.h threads/receiver.c threads/stroke_interface.c \ +threads/thread_pool.h threads/receiver.h threads/stroke_interface.h + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke +AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\" -DIPSEC_EAPDIR=\"${eapdir}\" +charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \ + -lgmp -lpthread -lm -ldl $(am__append_2) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/charon/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/charon/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-eapLTLIBRARIES: $(eap_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(eapdir)" || $(mkdir_p) "$(DESTDIR)$(eapdir)" + @list='$(eap_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(eapLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(eapdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(eapLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(eapdir)/$$f"; \ + else :; fi; \ + done + +uninstall-eapLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(eap_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(eapdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(eapdir)/$$p"; \ + done + +clean-eapLTLIBRARIES: + -test -z "$(eap_LTLIBRARIES)" || rm -f $(eap_LTLIBRARIES) + @list='$(eap_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libeapidentity.la: $(libeapidentity_la_OBJECTS) $(libeapidentity_la_DEPENDENCIES) + $(LINK) -rpath $(eapdir) $(libeapidentity_la_LDFLAGS) $(libeapidentity_la_OBJECTS) $(libeapidentity_la_LIBADD) $(LIBS) +libeapsim.la: $(libeapsim_la_OBJECTS) $(libeapsim_la_DEPENDENCIES) + $(LINK) $(am_libeapsim_la_rpath) $(libeapsim_la_LDFLAGS) $(libeapsim_la_OBJECTS) $(libeapsim_la_LIBADD) $(LIBS) +install-ipsecPROGRAMS: $(ipsec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ipsecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-ipsecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +clean-ipsecPROGRAMS: + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +charon$(EXEEXT): $(charon_OBJECTS) $(charon_DEPENDENCIES) + @rm -f charon$(EXEEXT) + $(LINK) $(charon_LDFLAGS) $(charon_OBJECTS) $(charon_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acquire_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cert_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certreq_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_create.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_delete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_rekey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/child_sa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configuration.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configuration_attribute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cp_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_child_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_ike_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_identity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_method.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_sim.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encodings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encryption_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_logger.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_auth.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_cert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_delete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_dpd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_header.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_init.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_natd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_rekey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_sa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_sa_id.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_sa_manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initiate_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ke_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_connection_store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_credential_store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local_policy_store.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nonce_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process_message_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proposal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proposal_substructure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psk_authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/receiver.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rekey_child_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rekey_ike_sa_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/retransmit_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsa_authenticator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sa_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scheduler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send_dpd_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send_keepalive_job.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sender.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stroke_interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_logger.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/task_manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_pool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traffic_selector.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/traffic_selector_substructure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transform_attribute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transform_substructure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ts_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unknown_payload.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vendor_id_payload.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +eap_identity.lo: sa/authenticators/eap/eap_identity.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_identity.lo -MD -MP -MF "$(DEPDIR)/eap_identity.Tpo" -c -o eap_identity.lo `test -f 'sa/authenticators/eap/eap_identity.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_identity.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_identity.Tpo" "$(DEPDIR)/eap_identity.Plo"; else rm -f "$(DEPDIR)/eap_identity.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_identity.c' object='eap_identity.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_identity.lo `test -f 'sa/authenticators/eap/eap_identity.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_identity.c + +eap_sim.lo: sa/authenticators/eap/eap_sim.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_sim.lo -MD -MP -MF "$(DEPDIR)/eap_sim.Tpo" -c -o eap_sim.lo `test -f 'sa/authenticators/eap/eap_sim.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_sim.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_sim.Tpo" "$(DEPDIR)/eap_sim.Plo"; else rm -f "$(DEPDIR)/eap_sim.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_sim.c' object='eap_sim.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_sim.lo `test -f 'sa/authenticators/eap/eap_sim.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_sim.c + +bus.o: bus/bus.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus.o -MD -MP -MF "$(DEPDIR)/bus.Tpo" -c -o bus.o `test -f 'bus/bus.c' || echo '$(srcdir)/'`bus/bus.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bus.Tpo" "$(DEPDIR)/bus.Po"; else rm -f "$(DEPDIR)/bus.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/bus.c' object='bus.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus.o `test -f 'bus/bus.c' || echo '$(srcdir)/'`bus/bus.c + +bus.obj: bus/bus.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus.obj -MD -MP -MF "$(DEPDIR)/bus.Tpo" -c -o bus.obj `if test -f 'bus/bus.c'; then $(CYGPATH_W) 'bus/bus.c'; else $(CYGPATH_W) '$(srcdir)/bus/bus.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bus.Tpo" "$(DEPDIR)/bus.Po"; else rm -f "$(DEPDIR)/bus.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/bus.c' object='bus.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus.obj `if test -f 'bus/bus.c'; then $(CYGPATH_W) 'bus/bus.c'; else $(CYGPATH_W) '$(srcdir)/bus/bus.c'; fi` + +sys_logger.o: bus/listeners/sys_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sys_logger.o -MD -MP -MF "$(DEPDIR)/sys_logger.Tpo" -c -o sys_logger.o `test -f 'bus/listeners/sys_logger.c' || echo '$(srcdir)/'`bus/listeners/sys_logger.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sys_logger.Tpo" "$(DEPDIR)/sys_logger.Po"; else rm -f "$(DEPDIR)/sys_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/sys_logger.c' object='sys_logger.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sys_logger.o `test -f 'bus/listeners/sys_logger.c' || echo '$(srcdir)/'`bus/listeners/sys_logger.c + +sys_logger.obj: bus/listeners/sys_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sys_logger.obj -MD -MP -MF "$(DEPDIR)/sys_logger.Tpo" -c -o sys_logger.obj `if test -f 'bus/listeners/sys_logger.c'; then $(CYGPATH_W) 'bus/listeners/sys_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/sys_logger.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sys_logger.Tpo" "$(DEPDIR)/sys_logger.Po"; else rm -f "$(DEPDIR)/sys_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/sys_logger.c' object='sys_logger.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sys_logger.obj `if test -f 'bus/listeners/sys_logger.c'; then $(CYGPATH_W) 'bus/listeners/sys_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/sys_logger.c'; fi` + +file_logger.o: bus/listeners/file_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT file_logger.o -MD -MP -MF "$(DEPDIR)/file_logger.Tpo" -c -o file_logger.o `test -f 'bus/listeners/file_logger.c' || echo '$(srcdir)/'`bus/listeners/file_logger.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/file_logger.Tpo" "$(DEPDIR)/file_logger.Po"; else rm -f "$(DEPDIR)/file_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/file_logger.c' object='file_logger.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o file_logger.o `test -f 'bus/listeners/file_logger.c' || echo '$(srcdir)/'`bus/listeners/file_logger.c + +file_logger.obj: bus/listeners/file_logger.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT file_logger.obj -MD -MP -MF "$(DEPDIR)/file_logger.Tpo" -c -o file_logger.obj `if test -f 'bus/listeners/file_logger.c'; then $(CYGPATH_W) 'bus/listeners/file_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/file_logger.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/file_logger.Tpo" "$(DEPDIR)/file_logger.Po"; else rm -f "$(DEPDIR)/file_logger.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bus/listeners/file_logger.c' object='file_logger.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o file_logger.obj `if test -f 'bus/listeners/file_logger.c'; then $(CYGPATH_W) 'bus/listeners/file_logger.c'; else $(CYGPATH_W) '$(srcdir)/bus/listeners/file_logger.c'; fi` + +connection.o: config/connections/connection.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT connection.o -MD -MP -MF "$(DEPDIR)/connection.Tpo" -c -o connection.o `test -f 'config/connections/connection.c' || echo '$(srcdir)/'`config/connections/connection.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/connection.Tpo" "$(DEPDIR)/connection.Po"; else rm -f "$(DEPDIR)/connection.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/connection.c' object='connection.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o connection.o `test -f 'config/connections/connection.c' || echo '$(srcdir)/'`config/connections/connection.c + +connection.obj: config/connections/connection.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT connection.obj -MD -MP -MF "$(DEPDIR)/connection.Tpo" -c -o connection.obj `if test -f 'config/connections/connection.c'; then $(CYGPATH_W) 'config/connections/connection.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/connection.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/connection.Tpo" "$(DEPDIR)/connection.Po"; else rm -f "$(DEPDIR)/connection.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/connection.c' object='connection.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o connection.obj `if test -f 'config/connections/connection.c'; then $(CYGPATH_W) 'config/connections/connection.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/connection.c'; fi` + +local_connection_store.o: config/connections/local_connection_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_connection_store.o -MD -MP -MF "$(DEPDIR)/local_connection_store.Tpo" -c -o local_connection_store.o `test -f 'config/connections/local_connection_store.c' || echo '$(srcdir)/'`config/connections/local_connection_store.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_connection_store.Tpo" "$(DEPDIR)/local_connection_store.Po"; else rm -f "$(DEPDIR)/local_connection_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/local_connection_store.c' object='local_connection_store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_connection_store.o `test -f 'config/connections/local_connection_store.c' || echo '$(srcdir)/'`config/connections/local_connection_store.c + +local_connection_store.obj: config/connections/local_connection_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_connection_store.obj -MD -MP -MF "$(DEPDIR)/local_connection_store.Tpo" -c -o local_connection_store.obj `if test -f 'config/connections/local_connection_store.c'; then $(CYGPATH_W) 'config/connections/local_connection_store.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/local_connection_store.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_connection_store.Tpo" "$(DEPDIR)/local_connection_store.Po"; else rm -f "$(DEPDIR)/local_connection_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/connections/local_connection_store.c' object='local_connection_store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_connection_store.obj `if test -f 'config/connections/local_connection_store.c'; then $(CYGPATH_W) 'config/connections/local_connection_store.c'; else $(CYGPATH_W) '$(srcdir)/config/connections/local_connection_store.c'; fi` + +policy.o: config/policies/policy.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT policy.o -MD -MP -MF "$(DEPDIR)/policy.Tpo" -c -o policy.o `test -f 'config/policies/policy.c' || echo '$(srcdir)/'`config/policies/policy.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/policy.Tpo" "$(DEPDIR)/policy.Po"; else rm -f "$(DEPDIR)/policy.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/policy.c' object='policy.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o policy.o `test -f 'config/policies/policy.c' || echo '$(srcdir)/'`config/policies/policy.c + +policy.obj: config/policies/policy.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT policy.obj -MD -MP -MF "$(DEPDIR)/policy.Tpo" -c -o policy.obj `if test -f 'config/policies/policy.c'; then $(CYGPATH_W) 'config/policies/policy.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/policy.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/policy.Tpo" "$(DEPDIR)/policy.Po"; else rm -f "$(DEPDIR)/policy.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/policy.c' object='policy.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o policy.obj `if test -f 'config/policies/policy.c'; then $(CYGPATH_W) 'config/policies/policy.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/policy.c'; fi` + +local_policy_store.o: config/policies/local_policy_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_policy_store.o -MD -MP -MF "$(DEPDIR)/local_policy_store.Tpo" -c -o local_policy_store.o `test -f 'config/policies/local_policy_store.c' || echo '$(srcdir)/'`config/policies/local_policy_store.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_policy_store.Tpo" "$(DEPDIR)/local_policy_store.Po"; else rm -f "$(DEPDIR)/local_policy_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/local_policy_store.c' object='local_policy_store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_policy_store.o `test -f 'config/policies/local_policy_store.c' || echo '$(srcdir)/'`config/policies/local_policy_store.c + +local_policy_store.obj: config/policies/local_policy_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_policy_store.obj -MD -MP -MF "$(DEPDIR)/local_policy_store.Tpo" -c -o local_policy_store.obj `if test -f 'config/policies/local_policy_store.c'; then $(CYGPATH_W) 'config/policies/local_policy_store.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/local_policy_store.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_policy_store.Tpo" "$(DEPDIR)/local_policy_store.Po"; else rm -f "$(DEPDIR)/local_policy_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/policies/local_policy_store.c' object='local_policy_store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_policy_store.obj `if test -f 'config/policies/local_policy_store.c'; then $(CYGPATH_W) 'config/policies/local_policy_store.c'; else $(CYGPATH_W) '$(srcdir)/config/policies/local_policy_store.c'; fi` + +local_credential_store.o: config/credentials/local_credential_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_credential_store.o -MD -MP -MF "$(DEPDIR)/local_credential_store.Tpo" -c -o local_credential_store.o `test -f 'config/credentials/local_credential_store.c' || echo '$(srcdir)/'`config/credentials/local_credential_store.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_credential_store.Tpo" "$(DEPDIR)/local_credential_store.Po"; else rm -f "$(DEPDIR)/local_credential_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/credentials/local_credential_store.c' object='local_credential_store.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_credential_store.o `test -f 'config/credentials/local_credential_store.c' || echo '$(srcdir)/'`config/credentials/local_credential_store.c + +local_credential_store.obj: config/credentials/local_credential_store.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT local_credential_store.obj -MD -MP -MF "$(DEPDIR)/local_credential_store.Tpo" -c -o local_credential_store.obj `if test -f 'config/credentials/local_credential_store.c'; then $(CYGPATH_W) 'config/credentials/local_credential_store.c'; else $(CYGPATH_W) '$(srcdir)/config/credentials/local_credential_store.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/local_credential_store.Tpo" "$(DEPDIR)/local_credential_store.Po"; else rm -f "$(DEPDIR)/local_credential_store.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/credentials/local_credential_store.c' object='local_credential_store.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o local_credential_store.obj `if test -f 'config/credentials/local_credential_store.c'; then $(CYGPATH_W) 'config/credentials/local_credential_store.c'; else $(CYGPATH_W) '$(srcdir)/config/credentials/local_credential_store.c'; fi` + +traffic_selector.o: config/traffic_selector.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector.o -MD -MP -MF "$(DEPDIR)/traffic_selector.Tpo" -c -o traffic_selector.o `test -f 'config/traffic_selector.c' || echo '$(srcdir)/'`config/traffic_selector.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector.Tpo" "$(DEPDIR)/traffic_selector.Po"; else rm -f "$(DEPDIR)/traffic_selector.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/traffic_selector.c' object='traffic_selector.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector.o `test -f 'config/traffic_selector.c' || echo '$(srcdir)/'`config/traffic_selector.c + +traffic_selector.obj: config/traffic_selector.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector.obj -MD -MP -MF "$(DEPDIR)/traffic_selector.Tpo" -c -o traffic_selector.obj `if test -f 'config/traffic_selector.c'; then $(CYGPATH_W) 'config/traffic_selector.c'; else $(CYGPATH_W) '$(srcdir)/config/traffic_selector.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector.Tpo" "$(DEPDIR)/traffic_selector.Po"; else rm -f "$(DEPDIR)/traffic_selector.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/traffic_selector.c' object='traffic_selector.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector.obj `if test -f 'config/traffic_selector.c'; then $(CYGPATH_W) 'config/traffic_selector.c'; else $(CYGPATH_W) '$(srcdir)/config/traffic_selector.c'; fi` + +proposal.o: config/proposal.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal.o -MD -MP -MF "$(DEPDIR)/proposal.Tpo" -c -o proposal.o `test -f 'config/proposal.c' || echo '$(srcdir)/'`config/proposal.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal.Tpo" "$(DEPDIR)/proposal.Po"; else rm -f "$(DEPDIR)/proposal.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/proposal.c' object='proposal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal.o `test -f 'config/proposal.c' || echo '$(srcdir)/'`config/proposal.c + +proposal.obj: config/proposal.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal.obj -MD -MP -MF "$(DEPDIR)/proposal.Tpo" -c -o proposal.obj `if test -f 'config/proposal.c'; then $(CYGPATH_W) 'config/proposal.c'; else $(CYGPATH_W) '$(srcdir)/config/proposal.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal.Tpo" "$(DEPDIR)/proposal.Po"; else rm -f "$(DEPDIR)/proposal.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/proposal.c' object='proposal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal.obj `if test -f 'config/proposal.c'; then $(CYGPATH_W) 'config/proposal.c'; else $(CYGPATH_W) '$(srcdir)/config/proposal.c'; fi` + +configuration.o: config/configuration.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration.o -MD -MP -MF "$(DEPDIR)/configuration.Tpo" -c -o configuration.o `test -f 'config/configuration.c' || echo '$(srcdir)/'`config/configuration.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration.Tpo" "$(DEPDIR)/configuration.Po"; else rm -f "$(DEPDIR)/configuration.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/configuration.c' object='configuration.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration.o `test -f 'config/configuration.c' || echo '$(srcdir)/'`config/configuration.c + +configuration.obj: config/configuration.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration.obj -MD -MP -MF "$(DEPDIR)/configuration.Tpo" -c -o configuration.obj `if test -f 'config/configuration.c'; then $(CYGPATH_W) 'config/configuration.c'; else $(CYGPATH_W) '$(srcdir)/config/configuration.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration.Tpo" "$(DEPDIR)/configuration.Po"; else rm -f "$(DEPDIR)/configuration.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/configuration.c' object='configuration.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration.obj `if test -f 'config/configuration.c'; then $(CYGPATH_W) 'config/configuration.c'; else $(CYGPATH_W) '$(srcdir)/config/configuration.c'; fi` + +eap_authenticator.o: sa/authenticators/eap_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_authenticator.o -MD -MP -MF "$(DEPDIR)/eap_authenticator.Tpo" -c -o eap_authenticator.o `test -f 'sa/authenticators/eap_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/eap_authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_authenticator.Tpo" "$(DEPDIR)/eap_authenticator.Po"; else rm -f "$(DEPDIR)/eap_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap_authenticator.c' object='eap_authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_authenticator.o `test -f 'sa/authenticators/eap_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/eap_authenticator.c + +eap_authenticator.obj: sa/authenticators/eap_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_authenticator.obj -MD -MP -MF "$(DEPDIR)/eap_authenticator.Tpo" -c -o eap_authenticator.obj `if test -f 'sa/authenticators/eap_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/eap_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap_authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_authenticator.Tpo" "$(DEPDIR)/eap_authenticator.Po"; else rm -f "$(DEPDIR)/eap_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap_authenticator.c' object='eap_authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_authenticator.obj `if test -f 'sa/authenticators/eap_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/eap_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap_authenticator.c'; fi` + +eap_method.o: sa/authenticators/eap/eap_method.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_method.o -MD -MP -MF "$(DEPDIR)/eap_method.Tpo" -c -o eap_method.o `test -f 'sa/authenticators/eap/eap_method.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_method.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_method.Tpo" "$(DEPDIR)/eap_method.Po"; else rm -f "$(DEPDIR)/eap_method.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_method.c' object='eap_method.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_method.o `test -f 'sa/authenticators/eap/eap_method.c' || echo '$(srcdir)/'`sa/authenticators/eap/eap_method.c + +eap_method.obj: sa/authenticators/eap/eap_method.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_method.obj -MD -MP -MF "$(DEPDIR)/eap_method.Tpo" -c -o eap_method.obj `if test -f 'sa/authenticators/eap/eap_method.c'; then $(CYGPATH_W) 'sa/authenticators/eap/eap_method.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap/eap_method.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_method.Tpo" "$(DEPDIR)/eap_method.Po"; else rm -f "$(DEPDIR)/eap_method.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/eap/eap_method.c' object='eap_method.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_method.obj `if test -f 'sa/authenticators/eap/eap_method.c'; then $(CYGPATH_W) 'sa/authenticators/eap/eap_method.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/eap/eap_method.c'; fi` + +child_sa.o: sa/child_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_sa.o -MD -MP -MF "$(DEPDIR)/child_sa.Tpo" -c -o child_sa.o `test -f 'sa/child_sa.c' || echo '$(srcdir)/'`sa/child_sa.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_sa.Tpo" "$(DEPDIR)/child_sa.Po"; else rm -f "$(DEPDIR)/child_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/child_sa.c' object='child_sa.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_sa.o `test -f 'sa/child_sa.c' || echo '$(srcdir)/'`sa/child_sa.c + +child_sa.obj: sa/child_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_sa.obj -MD -MP -MF "$(DEPDIR)/child_sa.Tpo" -c -o child_sa.obj `if test -f 'sa/child_sa.c'; then $(CYGPATH_W) 'sa/child_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/child_sa.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_sa.Tpo" "$(DEPDIR)/child_sa.Po"; else rm -f "$(DEPDIR)/child_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/child_sa.c' object='child_sa.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_sa.obj `if test -f 'sa/child_sa.c'; then $(CYGPATH_W) 'sa/child_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/child_sa.c'; fi` + +ike_sa.o: sa/ike_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa.o -MD -MP -MF "$(DEPDIR)/ike_sa.Tpo" -c -o ike_sa.o `test -f 'sa/ike_sa.c' || echo '$(srcdir)/'`sa/ike_sa.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa.Tpo" "$(DEPDIR)/ike_sa.Po"; else rm -f "$(DEPDIR)/ike_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa.c' object='ike_sa.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa.o `test -f 'sa/ike_sa.c' || echo '$(srcdir)/'`sa/ike_sa.c + +ike_sa.obj: sa/ike_sa.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa.obj -MD -MP -MF "$(DEPDIR)/ike_sa.Tpo" -c -o ike_sa.obj `if test -f 'sa/ike_sa.c'; then $(CYGPATH_W) 'sa/ike_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa.Tpo" "$(DEPDIR)/ike_sa.Po"; else rm -f "$(DEPDIR)/ike_sa.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa.c' object='ike_sa.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa.obj `if test -f 'sa/ike_sa.c'; then $(CYGPATH_W) 'sa/ike_sa.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa.c'; fi` + +ike_sa_manager.o: sa/ike_sa_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_manager.o -MD -MP -MF "$(DEPDIR)/ike_sa_manager.Tpo" -c -o ike_sa_manager.o `test -f 'sa/ike_sa_manager.c' || echo '$(srcdir)/'`sa/ike_sa_manager.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_manager.Tpo" "$(DEPDIR)/ike_sa_manager.Po"; else rm -f "$(DEPDIR)/ike_sa_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_manager.c' object='ike_sa_manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_manager.o `test -f 'sa/ike_sa_manager.c' || echo '$(srcdir)/'`sa/ike_sa_manager.c + +ike_sa_manager.obj: sa/ike_sa_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_manager.obj -MD -MP -MF "$(DEPDIR)/ike_sa_manager.Tpo" -c -o ike_sa_manager.obj `if test -f 'sa/ike_sa_manager.c'; then $(CYGPATH_W) 'sa/ike_sa_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_manager.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_manager.Tpo" "$(DEPDIR)/ike_sa_manager.Po"; else rm -f "$(DEPDIR)/ike_sa_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_manager.c' object='ike_sa_manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_manager.obj `if test -f 'sa/ike_sa_manager.c'; then $(CYGPATH_W) 'sa/ike_sa_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_manager.c'; fi` + +ike_sa_id.o: sa/ike_sa_id.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_id.o -MD -MP -MF "$(DEPDIR)/ike_sa_id.Tpo" -c -o ike_sa_id.o `test -f 'sa/ike_sa_id.c' || echo '$(srcdir)/'`sa/ike_sa_id.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_id.Tpo" "$(DEPDIR)/ike_sa_id.Po"; else rm -f "$(DEPDIR)/ike_sa_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_id.c' object='ike_sa_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_id.o `test -f 'sa/ike_sa_id.c' || echo '$(srcdir)/'`sa/ike_sa_id.c + +ike_sa_id.obj: sa/ike_sa_id.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_sa_id.obj -MD -MP -MF "$(DEPDIR)/ike_sa_id.Tpo" -c -o ike_sa_id.obj `if test -f 'sa/ike_sa_id.c'; then $(CYGPATH_W) 'sa/ike_sa_id.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_id.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_sa_id.Tpo" "$(DEPDIR)/ike_sa_id.Po"; else rm -f "$(DEPDIR)/ike_sa_id.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/ike_sa_id.c' object='ike_sa_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_sa_id.obj `if test -f 'sa/ike_sa_id.c'; then $(CYGPATH_W) 'sa/ike_sa_id.c'; else $(CYGPATH_W) '$(srcdir)/sa/ike_sa_id.c'; fi` + +task.o: sa/tasks/task.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task.o -MD -MP -MF "$(DEPDIR)/task.Tpo" -c -o task.o `test -f 'sa/tasks/task.c' || echo '$(srcdir)/'`sa/tasks/task.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task.Tpo" "$(DEPDIR)/task.Po"; else rm -f "$(DEPDIR)/task.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/task.c' object='task.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task.o `test -f 'sa/tasks/task.c' || echo '$(srcdir)/'`sa/tasks/task.c + +task.obj: sa/tasks/task.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task.obj -MD -MP -MF "$(DEPDIR)/task.Tpo" -c -o task.obj `if test -f 'sa/tasks/task.c'; then $(CYGPATH_W) 'sa/tasks/task.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/task.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task.Tpo" "$(DEPDIR)/task.Po"; else rm -f "$(DEPDIR)/task.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/task.c' object='task.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task.obj `if test -f 'sa/tasks/task.c'; then $(CYGPATH_W) 'sa/tasks/task.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/task.c'; fi` + +ike_init.o: sa/tasks/ike_init.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_init.o -MD -MP -MF "$(DEPDIR)/ike_init.Tpo" -c -o ike_init.o `test -f 'sa/tasks/ike_init.c' || echo '$(srcdir)/'`sa/tasks/ike_init.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_init.Tpo" "$(DEPDIR)/ike_init.Po"; else rm -f "$(DEPDIR)/ike_init.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_init.c' object='ike_init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_init.o `test -f 'sa/tasks/ike_init.c' || echo '$(srcdir)/'`sa/tasks/ike_init.c + +ike_init.obj: sa/tasks/ike_init.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_init.obj -MD -MP -MF "$(DEPDIR)/ike_init.Tpo" -c -o ike_init.obj `if test -f 'sa/tasks/ike_init.c'; then $(CYGPATH_W) 'sa/tasks/ike_init.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_init.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_init.Tpo" "$(DEPDIR)/ike_init.Po"; else rm -f "$(DEPDIR)/ike_init.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_init.c' object='ike_init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_init.obj `if test -f 'sa/tasks/ike_init.c'; then $(CYGPATH_W) 'sa/tasks/ike_init.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_init.c'; fi` + +ike_natd.o: sa/tasks/ike_natd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_natd.o -MD -MP -MF "$(DEPDIR)/ike_natd.Tpo" -c -o ike_natd.o `test -f 'sa/tasks/ike_natd.c' || echo '$(srcdir)/'`sa/tasks/ike_natd.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_natd.Tpo" "$(DEPDIR)/ike_natd.Po"; else rm -f "$(DEPDIR)/ike_natd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_natd.c' object='ike_natd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_natd.o `test -f 'sa/tasks/ike_natd.c' || echo '$(srcdir)/'`sa/tasks/ike_natd.c + +ike_natd.obj: sa/tasks/ike_natd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_natd.obj -MD -MP -MF "$(DEPDIR)/ike_natd.Tpo" -c -o ike_natd.obj `if test -f 'sa/tasks/ike_natd.c'; then $(CYGPATH_W) 'sa/tasks/ike_natd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_natd.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_natd.Tpo" "$(DEPDIR)/ike_natd.Po"; else rm -f "$(DEPDIR)/ike_natd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_natd.c' object='ike_natd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_natd.obj `if test -f 'sa/tasks/ike_natd.c'; then $(CYGPATH_W) 'sa/tasks/ike_natd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_natd.c'; fi` + +ike_auth.o: sa/tasks/ike_auth.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_auth.o -MD -MP -MF "$(DEPDIR)/ike_auth.Tpo" -c -o ike_auth.o `test -f 'sa/tasks/ike_auth.c' || echo '$(srcdir)/'`sa/tasks/ike_auth.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_auth.Tpo" "$(DEPDIR)/ike_auth.Po"; else rm -f "$(DEPDIR)/ike_auth.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_auth.c' object='ike_auth.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_auth.o `test -f 'sa/tasks/ike_auth.c' || echo '$(srcdir)/'`sa/tasks/ike_auth.c + +ike_auth.obj: sa/tasks/ike_auth.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_auth.obj -MD -MP -MF "$(DEPDIR)/ike_auth.Tpo" -c -o ike_auth.obj `if test -f 'sa/tasks/ike_auth.c'; then $(CYGPATH_W) 'sa/tasks/ike_auth.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_auth.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_auth.Tpo" "$(DEPDIR)/ike_auth.Po"; else rm -f "$(DEPDIR)/ike_auth.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_auth.c' object='ike_auth.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_auth.obj `if test -f 'sa/tasks/ike_auth.c'; then $(CYGPATH_W) 'sa/tasks/ike_auth.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_auth.c'; fi` + +ike_config.o: sa/tasks/ike_config.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_config.o -MD -MP -MF "$(DEPDIR)/ike_config.Tpo" -c -o ike_config.o `test -f 'sa/tasks/ike_config.c' || echo '$(srcdir)/'`sa/tasks/ike_config.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_config.Tpo" "$(DEPDIR)/ike_config.Po"; else rm -f "$(DEPDIR)/ike_config.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_config.c' object='ike_config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_config.o `test -f 'sa/tasks/ike_config.c' || echo '$(srcdir)/'`sa/tasks/ike_config.c + +ike_config.obj: sa/tasks/ike_config.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_config.obj -MD -MP -MF "$(DEPDIR)/ike_config.Tpo" -c -o ike_config.obj `if test -f 'sa/tasks/ike_config.c'; then $(CYGPATH_W) 'sa/tasks/ike_config.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_config.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_config.Tpo" "$(DEPDIR)/ike_config.Po"; else rm -f "$(DEPDIR)/ike_config.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_config.c' object='ike_config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_config.obj `if test -f 'sa/tasks/ike_config.c'; then $(CYGPATH_W) 'sa/tasks/ike_config.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_config.c'; fi` + +ike_cert.o: sa/tasks/ike_cert.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_cert.o -MD -MP -MF "$(DEPDIR)/ike_cert.Tpo" -c -o ike_cert.o `test -f 'sa/tasks/ike_cert.c' || echo '$(srcdir)/'`sa/tasks/ike_cert.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_cert.Tpo" "$(DEPDIR)/ike_cert.Po"; else rm -f "$(DEPDIR)/ike_cert.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_cert.c' object='ike_cert.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_cert.o `test -f 'sa/tasks/ike_cert.c' || echo '$(srcdir)/'`sa/tasks/ike_cert.c + +ike_cert.obj: sa/tasks/ike_cert.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_cert.obj -MD -MP -MF "$(DEPDIR)/ike_cert.Tpo" -c -o ike_cert.obj `if test -f 'sa/tasks/ike_cert.c'; then $(CYGPATH_W) 'sa/tasks/ike_cert.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_cert.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_cert.Tpo" "$(DEPDIR)/ike_cert.Po"; else rm -f "$(DEPDIR)/ike_cert.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_cert.c' object='ike_cert.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_cert.obj `if test -f 'sa/tasks/ike_cert.c'; then $(CYGPATH_W) 'sa/tasks/ike_cert.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_cert.c'; fi` + +ike_rekey.o: sa/tasks/ike_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_rekey.o -MD -MP -MF "$(DEPDIR)/ike_rekey.Tpo" -c -o ike_rekey.o `test -f 'sa/tasks/ike_rekey.c' || echo '$(srcdir)/'`sa/tasks/ike_rekey.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_rekey.Tpo" "$(DEPDIR)/ike_rekey.Po"; else rm -f "$(DEPDIR)/ike_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_rekey.c' object='ike_rekey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_rekey.o `test -f 'sa/tasks/ike_rekey.c' || echo '$(srcdir)/'`sa/tasks/ike_rekey.c + +ike_rekey.obj: sa/tasks/ike_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_rekey.obj -MD -MP -MF "$(DEPDIR)/ike_rekey.Tpo" -c -o ike_rekey.obj `if test -f 'sa/tasks/ike_rekey.c'; then $(CYGPATH_W) 'sa/tasks/ike_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_rekey.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_rekey.Tpo" "$(DEPDIR)/ike_rekey.Po"; else rm -f "$(DEPDIR)/ike_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_rekey.c' object='ike_rekey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_rekey.obj `if test -f 'sa/tasks/ike_rekey.c'; then $(CYGPATH_W) 'sa/tasks/ike_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_rekey.c'; fi` + +ike_delete.o: sa/tasks/ike_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_delete.o -MD -MP -MF "$(DEPDIR)/ike_delete.Tpo" -c -o ike_delete.o `test -f 'sa/tasks/ike_delete.c' || echo '$(srcdir)/'`sa/tasks/ike_delete.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_delete.Tpo" "$(DEPDIR)/ike_delete.Po"; else rm -f "$(DEPDIR)/ike_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_delete.c' object='ike_delete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_delete.o `test -f 'sa/tasks/ike_delete.c' || echo '$(srcdir)/'`sa/tasks/ike_delete.c + +ike_delete.obj: sa/tasks/ike_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_delete.obj -MD -MP -MF "$(DEPDIR)/ike_delete.Tpo" -c -o ike_delete.obj `if test -f 'sa/tasks/ike_delete.c'; then $(CYGPATH_W) 'sa/tasks/ike_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_delete.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_delete.Tpo" "$(DEPDIR)/ike_delete.Po"; else rm -f "$(DEPDIR)/ike_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_delete.c' object='ike_delete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_delete.obj `if test -f 'sa/tasks/ike_delete.c'; then $(CYGPATH_W) 'sa/tasks/ike_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_delete.c'; fi` + +ike_dpd.o: sa/tasks/ike_dpd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_dpd.o -MD -MP -MF "$(DEPDIR)/ike_dpd.Tpo" -c -o ike_dpd.o `test -f 'sa/tasks/ike_dpd.c' || echo '$(srcdir)/'`sa/tasks/ike_dpd.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_dpd.Tpo" "$(DEPDIR)/ike_dpd.Po"; else rm -f "$(DEPDIR)/ike_dpd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_dpd.c' object='ike_dpd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_dpd.o `test -f 'sa/tasks/ike_dpd.c' || echo '$(srcdir)/'`sa/tasks/ike_dpd.c + +ike_dpd.obj: sa/tasks/ike_dpd.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_dpd.obj -MD -MP -MF "$(DEPDIR)/ike_dpd.Tpo" -c -o ike_dpd.obj `if test -f 'sa/tasks/ike_dpd.c'; then $(CYGPATH_W) 'sa/tasks/ike_dpd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_dpd.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_dpd.Tpo" "$(DEPDIR)/ike_dpd.Po"; else rm -f "$(DEPDIR)/ike_dpd.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/ike_dpd.c' object='ike_dpd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_dpd.obj `if test -f 'sa/tasks/ike_dpd.c'; then $(CYGPATH_W) 'sa/tasks/ike_dpd.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/ike_dpd.c'; fi` + +child_create.o: sa/tasks/child_create.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_create.o -MD -MP -MF "$(DEPDIR)/child_create.Tpo" -c -o child_create.o `test -f 'sa/tasks/child_create.c' || echo '$(srcdir)/'`sa/tasks/child_create.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_create.Tpo" "$(DEPDIR)/child_create.Po"; else rm -f "$(DEPDIR)/child_create.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_create.c' object='child_create.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_create.o `test -f 'sa/tasks/child_create.c' || echo '$(srcdir)/'`sa/tasks/child_create.c + +child_create.obj: sa/tasks/child_create.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_create.obj -MD -MP -MF "$(DEPDIR)/child_create.Tpo" -c -o child_create.obj `if test -f 'sa/tasks/child_create.c'; then $(CYGPATH_W) 'sa/tasks/child_create.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_create.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_create.Tpo" "$(DEPDIR)/child_create.Po"; else rm -f "$(DEPDIR)/child_create.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_create.c' object='child_create.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_create.obj `if test -f 'sa/tasks/child_create.c'; then $(CYGPATH_W) 'sa/tasks/child_create.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_create.c'; fi` + +child_delete.o: sa/tasks/child_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_delete.o -MD -MP -MF "$(DEPDIR)/child_delete.Tpo" -c -o child_delete.o `test -f 'sa/tasks/child_delete.c' || echo '$(srcdir)/'`sa/tasks/child_delete.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_delete.Tpo" "$(DEPDIR)/child_delete.Po"; else rm -f "$(DEPDIR)/child_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_delete.c' object='child_delete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_delete.o `test -f 'sa/tasks/child_delete.c' || echo '$(srcdir)/'`sa/tasks/child_delete.c + +child_delete.obj: sa/tasks/child_delete.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_delete.obj -MD -MP -MF "$(DEPDIR)/child_delete.Tpo" -c -o child_delete.obj `if test -f 'sa/tasks/child_delete.c'; then $(CYGPATH_W) 'sa/tasks/child_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_delete.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_delete.Tpo" "$(DEPDIR)/child_delete.Po"; else rm -f "$(DEPDIR)/child_delete.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_delete.c' object='child_delete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_delete.obj `if test -f 'sa/tasks/child_delete.c'; then $(CYGPATH_W) 'sa/tasks/child_delete.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_delete.c'; fi` + +child_rekey.o: sa/tasks/child_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_rekey.o -MD -MP -MF "$(DEPDIR)/child_rekey.Tpo" -c -o child_rekey.o `test -f 'sa/tasks/child_rekey.c' || echo '$(srcdir)/'`sa/tasks/child_rekey.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_rekey.Tpo" "$(DEPDIR)/child_rekey.Po"; else rm -f "$(DEPDIR)/child_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_rekey.c' object='child_rekey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_rekey.o `test -f 'sa/tasks/child_rekey.c' || echo '$(srcdir)/'`sa/tasks/child_rekey.c + +child_rekey.obj: sa/tasks/child_rekey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT child_rekey.obj -MD -MP -MF "$(DEPDIR)/child_rekey.Tpo" -c -o child_rekey.obj `if test -f 'sa/tasks/child_rekey.c'; then $(CYGPATH_W) 'sa/tasks/child_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_rekey.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/child_rekey.Tpo" "$(DEPDIR)/child_rekey.Po"; else rm -f "$(DEPDIR)/child_rekey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/tasks/child_rekey.c' object='child_rekey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o child_rekey.obj `if test -f 'sa/tasks/child_rekey.c'; then $(CYGPATH_W) 'sa/tasks/child_rekey.c'; else $(CYGPATH_W) '$(srcdir)/sa/tasks/child_rekey.c'; fi` + +authenticator.o: sa/authenticators/authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT authenticator.o -MD -MP -MF "$(DEPDIR)/authenticator.Tpo" -c -o authenticator.o `test -f 'sa/authenticators/authenticator.c' || echo '$(srcdir)/'`sa/authenticators/authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/authenticator.Tpo" "$(DEPDIR)/authenticator.Po"; else rm -f "$(DEPDIR)/authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/authenticator.c' object='authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o authenticator.o `test -f 'sa/authenticators/authenticator.c' || echo '$(srcdir)/'`sa/authenticators/authenticator.c + +authenticator.obj: sa/authenticators/authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT authenticator.obj -MD -MP -MF "$(DEPDIR)/authenticator.Tpo" -c -o authenticator.obj `if test -f 'sa/authenticators/authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/authenticator.Tpo" "$(DEPDIR)/authenticator.Po"; else rm -f "$(DEPDIR)/authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/authenticator.c' object='authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o authenticator.obj `if test -f 'sa/authenticators/authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/authenticator.c'; fi` + +rsa_authenticator.o: sa/authenticators/rsa_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsa_authenticator.o -MD -MP -MF "$(DEPDIR)/rsa_authenticator.Tpo" -c -o rsa_authenticator.o `test -f 'sa/authenticators/rsa_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/rsa_authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rsa_authenticator.Tpo" "$(DEPDIR)/rsa_authenticator.Po"; else rm -f "$(DEPDIR)/rsa_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/rsa_authenticator.c' object='rsa_authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsa_authenticator.o `test -f 'sa/authenticators/rsa_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/rsa_authenticator.c + +rsa_authenticator.obj: sa/authenticators/rsa_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsa_authenticator.obj -MD -MP -MF "$(DEPDIR)/rsa_authenticator.Tpo" -c -o rsa_authenticator.obj `if test -f 'sa/authenticators/rsa_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/rsa_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/rsa_authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rsa_authenticator.Tpo" "$(DEPDIR)/rsa_authenticator.Po"; else rm -f "$(DEPDIR)/rsa_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/rsa_authenticator.c' object='rsa_authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsa_authenticator.obj `if test -f 'sa/authenticators/rsa_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/rsa_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/rsa_authenticator.c'; fi` + +psk_authenticator.o: sa/authenticators/psk_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT psk_authenticator.o -MD -MP -MF "$(DEPDIR)/psk_authenticator.Tpo" -c -o psk_authenticator.o `test -f 'sa/authenticators/psk_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/psk_authenticator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/psk_authenticator.Tpo" "$(DEPDIR)/psk_authenticator.Po"; else rm -f "$(DEPDIR)/psk_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/psk_authenticator.c' object='psk_authenticator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o psk_authenticator.o `test -f 'sa/authenticators/psk_authenticator.c' || echo '$(srcdir)/'`sa/authenticators/psk_authenticator.c + +psk_authenticator.obj: sa/authenticators/psk_authenticator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT psk_authenticator.obj -MD -MP -MF "$(DEPDIR)/psk_authenticator.Tpo" -c -o psk_authenticator.obj `if test -f 'sa/authenticators/psk_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/psk_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/psk_authenticator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/psk_authenticator.Tpo" "$(DEPDIR)/psk_authenticator.Po"; else rm -f "$(DEPDIR)/psk_authenticator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/authenticators/psk_authenticator.c' object='psk_authenticator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o psk_authenticator.obj `if test -f 'sa/authenticators/psk_authenticator.c'; then $(CYGPATH_W) 'sa/authenticators/psk_authenticator.c'; else $(CYGPATH_W) '$(srcdir)/sa/authenticators/psk_authenticator.c'; fi` + +task_manager.o: sa/task_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task_manager.o -MD -MP -MF "$(DEPDIR)/task_manager.Tpo" -c -o task_manager.o `test -f 'sa/task_manager.c' || echo '$(srcdir)/'`sa/task_manager.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task_manager.Tpo" "$(DEPDIR)/task_manager.Po"; else rm -f "$(DEPDIR)/task_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/task_manager.c' object='task_manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task_manager.o `test -f 'sa/task_manager.c' || echo '$(srcdir)/'`sa/task_manager.c + +task_manager.obj: sa/task_manager.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT task_manager.obj -MD -MP -MF "$(DEPDIR)/task_manager.Tpo" -c -o task_manager.obj `if test -f 'sa/task_manager.c'; then $(CYGPATH_W) 'sa/task_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/task_manager.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/task_manager.Tpo" "$(DEPDIR)/task_manager.Po"; else rm -f "$(DEPDIR)/task_manager.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sa/task_manager.c' object='task_manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o task_manager.obj `if test -f 'sa/task_manager.c'; then $(CYGPATH_W) 'sa/task_manager.c'; else $(CYGPATH_W) '$(srcdir)/sa/task_manager.c'; fi` + +encryption_payload.o: encoding/payloads/encryption_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encryption_payload.o -MD -MP -MF "$(DEPDIR)/encryption_payload.Tpo" -c -o encryption_payload.o `test -f 'encoding/payloads/encryption_payload.c' || echo '$(srcdir)/'`encoding/payloads/encryption_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encryption_payload.Tpo" "$(DEPDIR)/encryption_payload.Po"; else rm -f "$(DEPDIR)/encryption_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encryption_payload.c' object='encryption_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encryption_payload.o `test -f 'encoding/payloads/encryption_payload.c' || echo '$(srcdir)/'`encoding/payloads/encryption_payload.c + +encryption_payload.obj: encoding/payloads/encryption_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encryption_payload.obj -MD -MP -MF "$(DEPDIR)/encryption_payload.Tpo" -c -o encryption_payload.obj `if test -f 'encoding/payloads/encryption_payload.c'; then $(CYGPATH_W) 'encoding/payloads/encryption_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encryption_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encryption_payload.Tpo" "$(DEPDIR)/encryption_payload.Po"; else rm -f "$(DEPDIR)/encryption_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encryption_payload.c' object='encryption_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encryption_payload.obj `if test -f 'encoding/payloads/encryption_payload.c'; then $(CYGPATH_W) 'encoding/payloads/encryption_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encryption_payload.c'; fi` + +cert_payload.o: encoding/payloads/cert_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cert_payload.o -MD -MP -MF "$(DEPDIR)/cert_payload.Tpo" -c -o cert_payload.o `test -f 'encoding/payloads/cert_payload.c' || echo '$(srcdir)/'`encoding/payloads/cert_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cert_payload.Tpo" "$(DEPDIR)/cert_payload.Po"; else rm -f "$(DEPDIR)/cert_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cert_payload.c' object='cert_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cert_payload.o `test -f 'encoding/payloads/cert_payload.c' || echo '$(srcdir)/'`encoding/payloads/cert_payload.c + +cert_payload.obj: encoding/payloads/cert_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cert_payload.obj -MD -MP -MF "$(DEPDIR)/cert_payload.Tpo" -c -o cert_payload.obj `if test -f 'encoding/payloads/cert_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cert_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cert_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cert_payload.Tpo" "$(DEPDIR)/cert_payload.Po"; else rm -f "$(DEPDIR)/cert_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cert_payload.c' object='cert_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cert_payload.obj `if test -f 'encoding/payloads/cert_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cert_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cert_payload.c'; fi` + +traffic_selector_substructure.o: encoding/payloads/traffic_selector_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector_substructure.o -MD -MP -MF "$(DEPDIR)/traffic_selector_substructure.Tpo" -c -o traffic_selector_substructure.o `test -f 'encoding/payloads/traffic_selector_substructure.c' || echo '$(srcdir)/'`encoding/payloads/traffic_selector_substructure.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector_substructure.Tpo" "$(DEPDIR)/traffic_selector_substructure.Po"; else rm -f "$(DEPDIR)/traffic_selector_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/traffic_selector_substructure.c' object='traffic_selector_substructure.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector_substructure.o `test -f 'encoding/payloads/traffic_selector_substructure.c' || echo '$(srcdir)/'`encoding/payloads/traffic_selector_substructure.c + +traffic_selector_substructure.obj: encoding/payloads/traffic_selector_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT traffic_selector_substructure.obj -MD -MP -MF "$(DEPDIR)/traffic_selector_substructure.Tpo" -c -o traffic_selector_substructure.obj `if test -f 'encoding/payloads/traffic_selector_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/traffic_selector_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/traffic_selector_substructure.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/traffic_selector_substructure.Tpo" "$(DEPDIR)/traffic_selector_substructure.Po"; else rm -f "$(DEPDIR)/traffic_selector_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/traffic_selector_substructure.c' object='traffic_selector_substructure.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o traffic_selector_substructure.obj `if test -f 'encoding/payloads/traffic_selector_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/traffic_selector_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/traffic_selector_substructure.c'; fi` + +transform_attribute.o: encoding/payloads/transform_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_attribute.o -MD -MP -MF "$(DEPDIR)/transform_attribute.Tpo" -c -o transform_attribute.o `test -f 'encoding/payloads/transform_attribute.c' || echo '$(srcdir)/'`encoding/payloads/transform_attribute.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_attribute.Tpo" "$(DEPDIR)/transform_attribute.Po"; else rm -f "$(DEPDIR)/transform_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_attribute.c' object='transform_attribute.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_attribute.o `test -f 'encoding/payloads/transform_attribute.c' || echo '$(srcdir)/'`encoding/payloads/transform_attribute.c + +transform_attribute.obj: encoding/payloads/transform_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_attribute.obj -MD -MP -MF "$(DEPDIR)/transform_attribute.Tpo" -c -o transform_attribute.obj `if test -f 'encoding/payloads/transform_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/transform_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_attribute.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_attribute.Tpo" "$(DEPDIR)/transform_attribute.Po"; else rm -f "$(DEPDIR)/transform_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_attribute.c' object='transform_attribute.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_attribute.obj `if test -f 'encoding/payloads/transform_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/transform_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_attribute.c'; fi` + +configuration_attribute.o: encoding/payloads/configuration_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration_attribute.o -MD -MP -MF "$(DEPDIR)/configuration_attribute.Tpo" -c -o configuration_attribute.o `test -f 'encoding/payloads/configuration_attribute.c' || echo '$(srcdir)/'`encoding/payloads/configuration_attribute.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration_attribute.Tpo" "$(DEPDIR)/configuration_attribute.Po"; else rm -f "$(DEPDIR)/configuration_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/configuration_attribute.c' object='configuration_attribute.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration_attribute.o `test -f 'encoding/payloads/configuration_attribute.c' || echo '$(srcdir)/'`encoding/payloads/configuration_attribute.c + +configuration_attribute.obj: encoding/payloads/configuration_attribute.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration_attribute.obj -MD -MP -MF "$(DEPDIR)/configuration_attribute.Tpo" -c -o configuration_attribute.obj `if test -f 'encoding/payloads/configuration_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/configuration_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/configuration_attribute.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/configuration_attribute.Tpo" "$(DEPDIR)/configuration_attribute.Po"; else rm -f "$(DEPDIR)/configuration_attribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/configuration_attribute.c' object='configuration_attribute.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration_attribute.obj `if test -f 'encoding/payloads/configuration_attribute.c'; then $(CYGPATH_W) 'encoding/payloads/configuration_attribute.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/configuration_attribute.c'; fi` + +transform_substructure.o: encoding/payloads/transform_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_substructure.o -MD -MP -MF "$(DEPDIR)/transform_substructure.Tpo" -c -o transform_substructure.o `test -f 'encoding/payloads/transform_substructure.c' || echo '$(srcdir)/'`encoding/payloads/transform_substructure.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_substructure.Tpo" "$(DEPDIR)/transform_substructure.Po"; else rm -f "$(DEPDIR)/transform_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_substructure.c' object='transform_substructure.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_substructure.o `test -f 'encoding/payloads/transform_substructure.c' || echo '$(srcdir)/'`encoding/payloads/transform_substructure.c + +transform_substructure.obj: encoding/payloads/transform_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT transform_substructure.obj -MD -MP -MF "$(DEPDIR)/transform_substructure.Tpo" -c -o transform_substructure.obj `if test -f 'encoding/payloads/transform_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/transform_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_substructure.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/transform_substructure.Tpo" "$(DEPDIR)/transform_substructure.Po"; else rm -f "$(DEPDIR)/transform_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/transform_substructure.c' object='transform_substructure.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o transform_substructure.obj `if test -f 'encoding/payloads/transform_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/transform_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/transform_substructure.c'; fi` + +auth_payload.o: encoding/payloads/auth_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT auth_payload.o -MD -MP -MF "$(DEPDIR)/auth_payload.Tpo" -c -o auth_payload.o `test -f 'encoding/payloads/auth_payload.c' || echo '$(srcdir)/'`encoding/payloads/auth_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/auth_payload.Tpo" "$(DEPDIR)/auth_payload.Po"; else rm -f "$(DEPDIR)/auth_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/auth_payload.c' object='auth_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o auth_payload.o `test -f 'encoding/payloads/auth_payload.c' || echo '$(srcdir)/'`encoding/payloads/auth_payload.c + +auth_payload.obj: encoding/payloads/auth_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT auth_payload.obj -MD -MP -MF "$(DEPDIR)/auth_payload.Tpo" -c -o auth_payload.obj `if test -f 'encoding/payloads/auth_payload.c'; then $(CYGPATH_W) 'encoding/payloads/auth_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/auth_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/auth_payload.Tpo" "$(DEPDIR)/auth_payload.Po"; else rm -f "$(DEPDIR)/auth_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/auth_payload.c' object='auth_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o auth_payload.obj `if test -f 'encoding/payloads/auth_payload.c'; then $(CYGPATH_W) 'encoding/payloads/auth_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/auth_payload.c'; fi` + +ike_header.o: encoding/payloads/ike_header.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_header.o -MD -MP -MF "$(DEPDIR)/ike_header.Tpo" -c -o ike_header.o `test -f 'encoding/payloads/ike_header.c' || echo '$(srcdir)/'`encoding/payloads/ike_header.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_header.Tpo" "$(DEPDIR)/ike_header.Po"; else rm -f "$(DEPDIR)/ike_header.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ike_header.c' object='ike_header.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_header.o `test -f 'encoding/payloads/ike_header.c' || echo '$(srcdir)/'`encoding/payloads/ike_header.c + +ike_header.obj: encoding/payloads/ike_header.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_header.obj -MD -MP -MF "$(DEPDIR)/ike_header.Tpo" -c -o ike_header.obj `if test -f 'encoding/payloads/ike_header.c'; then $(CYGPATH_W) 'encoding/payloads/ike_header.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ike_header.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_header.Tpo" "$(DEPDIR)/ike_header.Po"; else rm -f "$(DEPDIR)/ike_header.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ike_header.c' object='ike_header.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_header.obj `if test -f 'encoding/payloads/ike_header.c'; then $(CYGPATH_W) 'encoding/payloads/ike_header.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ike_header.c'; fi` + +nonce_payload.o: encoding/payloads/nonce_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nonce_payload.o -MD -MP -MF "$(DEPDIR)/nonce_payload.Tpo" -c -o nonce_payload.o `test -f 'encoding/payloads/nonce_payload.c' || echo '$(srcdir)/'`encoding/payloads/nonce_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/nonce_payload.Tpo" "$(DEPDIR)/nonce_payload.Po"; else rm -f "$(DEPDIR)/nonce_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/nonce_payload.c' object='nonce_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nonce_payload.o `test -f 'encoding/payloads/nonce_payload.c' || echo '$(srcdir)/'`encoding/payloads/nonce_payload.c + +nonce_payload.obj: encoding/payloads/nonce_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nonce_payload.obj -MD -MP -MF "$(DEPDIR)/nonce_payload.Tpo" -c -o nonce_payload.obj `if test -f 'encoding/payloads/nonce_payload.c'; then $(CYGPATH_W) 'encoding/payloads/nonce_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/nonce_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/nonce_payload.Tpo" "$(DEPDIR)/nonce_payload.Po"; else rm -f "$(DEPDIR)/nonce_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/nonce_payload.c' object='nonce_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nonce_payload.obj `if test -f 'encoding/payloads/nonce_payload.c'; then $(CYGPATH_W) 'encoding/payloads/nonce_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/nonce_payload.c'; fi` + +eap_payload.o: encoding/payloads/eap_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_payload.o -MD -MP -MF "$(DEPDIR)/eap_payload.Tpo" -c -o eap_payload.o `test -f 'encoding/payloads/eap_payload.c' || echo '$(srcdir)/'`encoding/payloads/eap_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_payload.Tpo" "$(DEPDIR)/eap_payload.Po"; else rm -f "$(DEPDIR)/eap_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/eap_payload.c' object='eap_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_payload.o `test -f 'encoding/payloads/eap_payload.c' || echo '$(srcdir)/'`encoding/payloads/eap_payload.c + +eap_payload.obj: encoding/payloads/eap_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT eap_payload.obj -MD -MP -MF "$(DEPDIR)/eap_payload.Tpo" -c -o eap_payload.obj `if test -f 'encoding/payloads/eap_payload.c'; then $(CYGPATH_W) 'encoding/payloads/eap_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/eap_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/eap_payload.Tpo" "$(DEPDIR)/eap_payload.Po"; else rm -f "$(DEPDIR)/eap_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/eap_payload.c' object='eap_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o eap_payload.obj `if test -f 'encoding/payloads/eap_payload.c'; then $(CYGPATH_W) 'encoding/payloads/eap_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/eap_payload.c'; fi` + +ts_payload.o: encoding/payloads/ts_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ts_payload.o -MD -MP -MF "$(DEPDIR)/ts_payload.Tpo" -c -o ts_payload.o `test -f 'encoding/payloads/ts_payload.c' || echo '$(srcdir)/'`encoding/payloads/ts_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ts_payload.Tpo" "$(DEPDIR)/ts_payload.Po"; else rm -f "$(DEPDIR)/ts_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ts_payload.c' object='ts_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ts_payload.o `test -f 'encoding/payloads/ts_payload.c' || echo '$(srcdir)/'`encoding/payloads/ts_payload.c + +ts_payload.obj: encoding/payloads/ts_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ts_payload.obj -MD -MP -MF "$(DEPDIR)/ts_payload.Tpo" -c -o ts_payload.obj `if test -f 'encoding/payloads/ts_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ts_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ts_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ts_payload.Tpo" "$(DEPDIR)/ts_payload.Po"; else rm -f "$(DEPDIR)/ts_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ts_payload.c' object='ts_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ts_payload.obj `if test -f 'encoding/payloads/ts_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ts_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ts_payload.c'; fi` + +notify_payload.o: encoding/payloads/notify_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT notify_payload.o -MD -MP -MF "$(DEPDIR)/notify_payload.Tpo" -c -o notify_payload.o `test -f 'encoding/payloads/notify_payload.c' || echo '$(srcdir)/'`encoding/payloads/notify_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/notify_payload.Tpo" "$(DEPDIR)/notify_payload.Po"; else rm -f "$(DEPDIR)/notify_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/notify_payload.c' object='notify_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o notify_payload.o `test -f 'encoding/payloads/notify_payload.c' || echo '$(srcdir)/'`encoding/payloads/notify_payload.c + +notify_payload.obj: encoding/payloads/notify_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT notify_payload.obj -MD -MP -MF "$(DEPDIR)/notify_payload.Tpo" -c -o notify_payload.obj `if test -f 'encoding/payloads/notify_payload.c'; then $(CYGPATH_W) 'encoding/payloads/notify_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/notify_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/notify_payload.Tpo" "$(DEPDIR)/notify_payload.Po"; else rm -f "$(DEPDIR)/notify_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/notify_payload.c' object='notify_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o notify_payload.obj `if test -f 'encoding/payloads/notify_payload.c'; then $(CYGPATH_W) 'encoding/payloads/notify_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/notify_payload.c'; fi` + +id_payload.o: encoding/payloads/id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT id_payload.o -MD -MP -MF "$(DEPDIR)/id_payload.Tpo" -c -o id_payload.o `test -f 'encoding/payloads/id_payload.c' || echo '$(srcdir)/'`encoding/payloads/id_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/id_payload.Tpo" "$(DEPDIR)/id_payload.Po"; else rm -f "$(DEPDIR)/id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/id_payload.c' object='id_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o id_payload.o `test -f 'encoding/payloads/id_payload.c' || echo '$(srcdir)/'`encoding/payloads/id_payload.c + +id_payload.obj: encoding/payloads/id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT id_payload.obj -MD -MP -MF "$(DEPDIR)/id_payload.Tpo" -c -o id_payload.obj `if test -f 'encoding/payloads/id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/id_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/id_payload.Tpo" "$(DEPDIR)/id_payload.Po"; else rm -f "$(DEPDIR)/id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/id_payload.c' object='id_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o id_payload.obj `if test -f 'encoding/payloads/id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/id_payload.c'; fi` + +ke_payload.o: encoding/payloads/ke_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ke_payload.o -MD -MP -MF "$(DEPDIR)/ke_payload.Tpo" -c -o ke_payload.o `test -f 'encoding/payloads/ke_payload.c' || echo '$(srcdir)/'`encoding/payloads/ke_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ke_payload.Tpo" "$(DEPDIR)/ke_payload.Po"; else rm -f "$(DEPDIR)/ke_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ke_payload.c' object='ke_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ke_payload.o `test -f 'encoding/payloads/ke_payload.c' || echo '$(srcdir)/'`encoding/payloads/ke_payload.c + +ke_payload.obj: encoding/payloads/ke_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ke_payload.obj -MD -MP -MF "$(DEPDIR)/ke_payload.Tpo" -c -o ke_payload.obj `if test -f 'encoding/payloads/ke_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ke_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ke_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ke_payload.Tpo" "$(DEPDIR)/ke_payload.Po"; else rm -f "$(DEPDIR)/ke_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/ke_payload.c' object='ke_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ke_payload.obj `if test -f 'encoding/payloads/ke_payload.c'; then $(CYGPATH_W) 'encoding/payloads/ke_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/ke_payload.c'; fi` + +unknown_payload.o: encoding/payloads/unknown_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unknown_payload.o -MD -MP -MF "$(DEPDIR)/unknown_payload.Tpo" -c -o unknown_payload.o `test -f 'encoding/payloads/unknown_payload.c' || echo '$(srcdir)/'`encoding/payloads/unknown_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/unknown_payload.Tpo" "$(DEPDIR)/unknown_payload.Po"; else rm -f "$(DEPDIR)/unknown_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/unknown_payload.c' object='unknown_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unknown_payload.o `test -f 'encoding/payloads/unknown_payload.c' || echo '$(srcdir)/'`encoding/payloads/unknown_payload.c + +unknown_payload.obj: encoding/payloads/unknown_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unknown_payload.obj -MD -MP -MF "$(DEPDIR)/unknown_payload.Tpo" -c -o unknown_payload.obj `if test -f 'encoding/payloads/unknown_payload.c'; then $(CYGPATH_W) 'encoding/payloads/unknown_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/unknown_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/unknown_payload.Tpo" "$(DEPDIR)/unknown_payload.Po"; else rm -f "$(DEPDIR)/unknown_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/unknown_payload.c' object='unknown_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unknown_payload.obj `if test -f 'encoding/payloads/unknown_payload.c'; then $(CYGPATH_W) 'encoding/payloads/unknown_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/unknown_payload.c'; fi` + +encodings.o: encoding/payloads/encodings.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encodings.o -MD -MP -MF "$(DEPDIR)/encodings.Tpo" -c -o encodings.o `test -f 'encoding/payloads/encodings.c' || echo '$(srcdir)/'`encoding/payloads/encodings.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encodings.Tpo" "$(DEPDIR)/encodings.Po"; else rm -f "$(DEPDIR)/encodings.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encodings.c' object='encodings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encodings.o `test -f 'encoding/payloads/encodings.c' || echo '$(srcdir)/'`encoding/payloads/encodings.c + +encodings.obj: encoding/payloads/encodings.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT encodings.obj -MD -MP -MF "$(DEPDIR)/encodings.Tpo" -c -o encodings.obj `if test -f 'encoding/payloads/encodings.c'; then $(CYGPATH_W) 'encoding/payloads/encodings.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encodings.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/encodings.Tpo" "$(DEPDIR)/encodings.Po"; else rm -f "$(DEPDIR)/encodings.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/encodings.c' object='encodings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o encodings.obj `if test -f 'encoding/payloads/encodings.c'; then $(CYGPATH_W) 'encoding/payloads/encodings.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/encodings.c'; fi` + +cp_payload.o: encoding/payloads/cp_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cp_payload.o -MD -MP -MF "$(DEPDIR)/cp_payload.Tpo" -c -o cp_payload.o `test -f 'encoding/payloads/cp_payload.c' || echo '$(srcdir)/'`encoding/payloads/cp_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cp_payload.Tpo" "$(DEPDIR)/cp_payload.Po"; else rm -f "$(DEPDIR)/cp_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cp_payload.c' object='cp_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cp_payload.o `test -f 'encoding/payloads/cp_payload.c' || echo '$(srcdir)/'`encoding/payloads/cp_payload.c + +cp_payload.obj: encoding/payloads/cp_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cp_payload.obj -MD -MP -MF "$(DEPDIR)/cp_payload.Tpo" -c -o cp_payload.obj `if test -f 'encoding/payloads/cp_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cp_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cp_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cp_payload.Tpo" "$(DEPDIR)/cp_payload.Po"; else rm -f "$(DEPDIR)/cp_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/cp_payload.c' object='cp_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cp_payload.obj `if test -f 'encoding/payloads/cp_payload.c'; then $(CYGPATH_W) 'encoding/payloads/cp_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/cp_payload.c'; fi` + +delete_payload.o: encoding/payloads/delete_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_payload.o -MD -MP -MF "$(DEPDIR)/delete_payload.Tpo" -c -o delete_payload.o `test -f 'encoding/payloads/delete_payload.c' || echo '$(srcdir)/'`encoding/payloads/delete_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_payload.Tpo" "$(DEPDIR)/delete_payload.Po"; else rm -f "$(DEPDIR)/delete_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/delete_payload.c' object='delete_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_payload.o `test -f 'encoding/payloads/delete_payload.c' || echo '$(srcdir)/'`encoding/payloads/delete_payload.c + +delete_payload.obj: encoding/payloads/delete_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_payload.obj -MD -MP -MF "$(DEPDIR)/delete_payload.Tpo" -c -o delete_payload.obj `if test -f 'encoding/payloads/delete_payload.c'; then $(CYGPATH_W) 'encoding/payloads/delete_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/delete_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_payload.Tpo" "$(DEPDIR)/delete_payload.Po"; else rm -f "$(DEPDIR)/delete_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/delete_payload.c' object='delete_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_payload.obj `if test -f 'encoding/payloads/delete_payload.c'; then $(CYGPATH_W) 'encoding/payloads/delete_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/delete_payload.c'; fi` + +sa_payload.o: encoding/payloads/sa_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sa_payload.o -MD -MP -MF "$(DEPDIR)/sa_payload.Tpo" -c -o sa_payload.o `test -f 'encoding/payloads/sa_payload.c' || echo '$(srcdir)/'`encoding/payloads/sa_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sa_payload.Tpo" "$(DEPDIR)/sa_payload.Po"; else rm -f "$(DEPDIR)/sa_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/sa_payload.c' object='sa_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sa_payload.o `test -f 'encoding/payloads/sa_payload.c' || echo '$(srcdir)/'`encoding/payloads/sa_payload.c + +sa_payload.obj: encoding/payloads/sa_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sa_payload.obj -MD -MP -MF "$(DEPDIR)/sa_payload.Tpo" -c -o sa_payload.obj `if test -f 'encoding/payloads/sa_payload.c'; then $(CYGPATH_W) 'encoding/payloads/sa_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/sa_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sa_payload.Tpo" "$(DEPDIR)/sa_payload.Po"; else rm -f "$(DEPDIR)/sa_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/sa_payload.c' object='sa_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sa_payload.obj `if test -f 'encoding/payloads/sa_payload.c'; then $(CYGPATH_W) 'encoding/payloads/sa_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/sa_payload.c'; fi` + +certreq_payload.o: encoding/payloads/certreq_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT certreq_payload.o -MD -MP -MF "$(DEPDIR)/certreq_payload.Tpo" -c -o certreq_payload.o `test -f 'encoding/payloads/certreq_payload.c' || echo '$(srcdir)/'`encoding/payloads/certreq_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/certreq_payload.Tpo" "$(DEPDIR)/certreq_payload.Po"; else rm -f "$(DEPDIR)/certreq_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/certreq_payload.c' object='certreq_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o certreq_payload.o `test -f 'encoding/payloads/certreq_payload.c' || echo '$(srcdir)/'`encoding/payloads/certreq_payload.c + +certreq_payload.obj: encoding/payloads/certreq_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT certreq_payload.obj -MD -MP -MF "$(DEPDIR)/certreq_payload.Tpo" -c -o certreq_payload.obj `if test -f 'encoding/payloads/certreq_payload.c'; then $(CYGPATH_W) 'encoding/payloads/certreq_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/certreq_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/certreq_payload.Tpo" "$(DEPDIR)/certreq_payload.Po"; else rm -f "$(DEPDIR)/certreq_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/certreq_payload.c' object='certreq_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o certreq_payload.obj `if test -f 'encoding/payloads/certreq_payload.c'; then $(CYGPATH_W) 'encoding/payloads/certreq_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/certreq_payload.c'; fi` + +vendor_id_payload.o: encoding/payloads/vendor_id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT vendor_id_payload.o -MD -MP -MF "$(DEPDIR)/vendor_id_payload.Tpo" -c -o vendor_id_payload.o `test -f 'encoding/payloads/vendor_id_payload.c' || echo '$(srcdir)/'`encoding/payloads/vendor_id_payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/vendor_id_payload.Tpo" "$(DEPDIR)/vendor_id_payload.Po"; else rm -f "$(DEPDIR)/vendor_id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/vendor_id_payload.c' object='vendor_id_payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vendor_id_payload.o `test -f 'encoding/payloads/vendor_id_payload.c' || echo '$(srcdir)/'`encoding/payloads/vendor_id_payload.c + +vendor_id_payload.obj: encoding/payloads/vendor_id_payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT vendor_id_payload.obj -MD -MP -MF "$(DEPDIR)/vendor_id_payload.Tpo" -c -o vendor_id_payload.obj `if test -f 'encoding/payloads/vendor_id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/vendor_id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/vendor_id_payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/vendor_id_payload.Tpo" "$(DEPDIR)/vendor_id_payload.Po"; else rm -f "$(DEPDIR)/vendor_id_payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/vendor_id_payload.c' object='vendor_id_payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vendor_id_payload.obj `if test -f 'encoding/payloads/vendor_id_payload.c'; then $(CYGPATH_W) 'encoding/payloads/vendor_id_payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/vendor_id_payload.c'; fi` + +proposal_substructure.o: encoding/payloads/proposal_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal_substructure.o -MD -MP -MF "$(DEPDIR)/proposal_substructure.Tpo" -c -o proposal_substructure.o `test -f 'encoding/payloads/proposal_substructure.c' || echo '$(srcdir)/'`encoding/payloads/proposal_substructure.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal_substructure.Tpo" "$(DEPDIR)/proposal_substructure.Po"; else rm -f "$(DEPDIR)/proposal_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/proposal_substructure.c' object='proposal_substructure.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal_substructure.o `test -f 'encoding/payloads/proposal_substructure.c' || echo '$(srcdir)/'`encoding/payloads/proposal_substructure.c + +proposal_substructure.obj: encoding/payloads/proposal_substructure.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT proposal_substructure.obj -MD -MP -MF "$(DEPDIR)/proposal_substructure.Tpo" -c -o proposal_substructure.obj `if test -f 'encoding/payloads/proposal_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/proposal_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/proposal_substructure.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/proposal_substructure.Tpo" "$(DEPDIR)/proposal_substructure.Po"; else rm -f "$(DEPDIR)/proposal_substructure.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/proposal_substructure.c' object='proposal_substructure.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o proposal_substructure.obj `if test -f 'encoding/payloads/proposal_substructure.c'; then $(CYGPATH_W) 'encoding/payloads/proposal_substructure.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/proposal_substructure.c'; fi` + +payload.o: encoding/payloads/payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT payload.o -MD -MP -MF "$(DEPDIR)/payload.Tpo" -c -o payload.o `test -f 'encoding/payloads/payload.c' || echo '$(srcdir)/'`encoding/payloads/payload.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/payload.Tpo" "$(DEPDIR)/payload.Po"; else rm -f "$(DEPDIR)/payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/payload.c' object='payload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o payload.o `test -f 'encoding/payloads/payload.c' || echo '$(srcdir)/'`encoding/payloads/payload.c + +payload.obj: encoding/payloads/payload.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT payload.obj -MD -MP -MF "$(DEPDIR)/payload.Tpo" -c -o payload.obj `if test -f 'encoding/payloads/payload.c'; then $(CYGPATH_W) 'encoding/payloads/payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/payload.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/payload.Tpo" "$(DEPDIR)/payload.Po"; else rm -f "$(DEPDIR)/payload.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/payloads/payload.c' object='payload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o payload.obj `if test -f 'encoding/payloads/payload.c'; then $(CYGPATH_W) 'encoding/payloads/payload.c'; else $(CYGPATH_W) '$(srcdir)/encoding/payloads/payload.c'; fi` + +message.o: encoding/message.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT message.o -MD -MP -MF "$(DEPDIR)/message.Tpo" -c -o message.o `test -f 'encoding/message.c' || echo '$(srcdir)/'`encoding/message.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/message.Tpo" "$(DEPDIR)/message.Po"; else rm -f "$(DEPDIR)/message.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/message.c' object='message.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o message.o `test -f 'encoding/message.c' || echo '$(srcdir)/'`encoding/message.c + +message.obj: encoding/message.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT message.obj -MD -MP -MF "$(DEPDIR)/message.Tpo" -c -o message.obj `if test -f 'encoding/message.c'; then $(CYGPATH_W) 'encoding/message.c'; else $(CYGPATH_W) '$(srcdir)/encoding/message.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/message.Tpo" "$(DEPDIR)/message.Po"; else rm -f "$(DEPDIR)/message.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/message.c' object='message.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o message.obj `if test -f 'encoding/message.c'; then $(CYGPATH_W) 'encoding/message.c'; else $(CYGPATH_W) '$(srcdir)/encoding/message.c'; fi` + +generator.o: encoding/generator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT generator.o -MD -MP -MF "$(DEPDIR)/generator.Tpo" -c -o generator.o `test -f 'encoding/generator.c' || echo '$(srcdir)/'`encoding/generator.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/generator.Tpo" "$(DEPDIR)/generator.Po"; else rm -f "$(DEPDIR)/generator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/generator.c' object='generator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o generator.o `test -f 'encoding/generator.c' || echo '$(srcdir)/'`encoding/generator.c + +generator.obj: encoding/generator.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT generator.obj -MD -MP -MF "$(DEPDIR)/generator.Tpo" -c -o generator.obj `if test -f 'encoding/generator.c'; then $(CYGPATH_W) 'encoding/generator.c'; else $(CYGPATH_W) '$(srcdir)/encoding/generator.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/generator.Tpo" "$(DEPDIR)/generator.Po"; else rm -f "$(DEPDIR)/generator.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/generator.c' object='generator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o generator.obj `if test -f 'encoding/generator.c'; then $(CYGPATH_W) 'encoding/generator.c'; else $(CYGPATH_W) '$(srcdir)/encoding/generator.c'; fi` + +parser.o: encoding/parser.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser.o -MD -MP -MF "$(DEPDIR)/parser.Tpo" -c -o parser.o `test -f 'encoding/parser.c' || echo '$(srcdir)/'`encoding/parser.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/parser.Tpo" "$(DEPDIR)/parser.Po"; else rm -f "$(DEPDIR)/parser.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/parser.c' object='parser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser.o `test -f 'encoding/parser.c' || echo '$(srcdir)/'`encoding/parser.c + +parser.obj: encoding/parser.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parser.obj -MD -MP -MF "$(DEPDIR)/parser.Tpo" -c -o parser.obj `if test -f 'encoding/parser.c'; then $(CYGPATH_W) 'encoding/parser.c'; else $(CYGPATH_W) '$(srcdir)/encoding/parser.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/parser.Tpo" "$(DEPDIR)/parser.Po"; else rm -f "$(DEPDIR)/parser.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='encoding/parser.c' object='parser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parser.obj `if test -f 'encoding/parser.c'; then $(CYGPATH_W) 'encoding/parser.c'; else $(CYGPATH_W) '$(srcdir)/encoding/parser.c'; fi` + +packet.o: network/packet.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet.o -MD -MP -MF "$(DEPDIR)/packet.Tpo" -c -o packet.o `test -f 'network/packet.c' || echo '$(srcdir)/'`network/packet.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/packet.Tpo" "$(DEPDIR)/packet.Po"; else rm -f "$(DEPDIR)/packet.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/packet.c' object='packet.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet.o `test -f 'network/packet.c' || echo '$(srcdir)/'`network/packet.c + +packet.obj: network/packet.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT packet.obj -MD -MP -MF "$(DEPDIR)/packet.Tpo" -c -o packet.obj `if test -f 'network/packet.c'; then $(CYGPATH_W) 'network/packet.c'; else $(CYGPATH_W) '$(srcdir)/network/packet.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/packet.Tpo" "$(DEPDIR)/packet.Po"; else rm -f "$(DEPDIR)/packet.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/packet.c' object='packet.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o packet.obj `if test -f 'network/packet.c'; then $(CYGPATH_W) 'network/packet.c'; else $(CYGPATH_W) '$(srcdir)/network/packet.c'; fi` + +socket.o: network/socket.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket.o -MD -MP -MF "$(DEPDIR)/socket.Tpo" -c -o socket.o `test -f 'network/socket.c' || echo '$(srcdir)/'`network/socket.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/socket.Tpo" "$(DEPDIR)/socket.Po"; else rm -f "$(DEPDIR)/socket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/socket.c' object='socket.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket.o `test -f 'network/socket.c' || echo '$(srcdir)/'`network/socket.c + +socket.obj: network/socket.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket.obj -MD -MP -MF "$(DEPDIR)/socket.Tpo" -c -o socket.obj `if test -f 'network/socket.c'; then $(CYGPATH_W) 'network/socket.c'; else $(CYGPATH_W) '$(srcdir)/network/socket.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/socket.Tpo" "$(DEPDIR)/socket.Po"; else rm -f "$(DEPDIR)/socket.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='network/socket.c' object='socket.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket.obj `if test -f 'network/socket.c'; then $(CYGPATH_W) 'network/socket.c'; else $(CYGPATH_W) '$(srcdir)/network/socket.c'; fi` + +job.o: queues/jobs/job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job.o -MD -MP -MF "$(DEPDIR)/job.Tpo" -c -o job.o `test -f 'queues/jobs/job.c' || echo '$(srcdir)/'`queues/jobs/job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job.Tpo" "$(DEPDIR)/job.Po"; else rm -f "$(DEPDIR)/job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/job.c' object='job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job.o `test -f 'queues/jobs/job.c' || echo '$(srcdir)/'`queues/jobs/job.c + +job.obj: queues/jobs/job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job.obj -MD -MP -MF "$(DEPDIR)/job.Tpo" -c -o job.obj `if test -f 'queues/jobs/job.c'; then $(CYGPATH_W) 'queues/jobs/job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job.Tpo" "$(DEPDIR)/job.Po"; else rm -f "$(DEPDIR)/job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/job.c' object='job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job.obj `if test -f 'queues/jobs/job.c'; then $(CYGPATH_W) 'queues/jobs/job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/job.c'; fi` + +process_message_job.o: queues/jobs/process_message_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT process_message_job.o -MD -MP -MF "$(DEPDIR)/process_message_job.Tpo" -c -o process_message_job.o `test -f 'queues/jobs/process_message_job.c' || echo '$(srcdir)/'`queues/jobs/process_message_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/process_message_job.Tpo" "$(DEPDIR)/process_message_job.Po"; else rm -f "$(DEPDIR)/process_message_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/process_message_job.c' object='process_message_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o process_message_job.o `test -f 'queues/jobs/process_message_job.c' || echo '$(srcdir)/'`queues/jobs/process_message_job.c + +process_message_job.obj: queues/jobs/process_message_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT process_message_job.obj -MD -MP -MF "$(DEPDIR)/process_message_job.Tpo" -c -o process_message_job.obj `if test -f 'queues/jobs/process_message_job.c'; then $(CYGPATH_W) 'queues/jobs/process_message_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/process_message_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/process_message_job.Tpo" "$(DEPDIR)/process_message_job.Po"; else rm -f "$(DEPDIR)/process_message_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/process_message_job.c' object='process_message_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o process_message_job.obj `if test -f 'queues/jobs/process_message_job.c'; then $(CYGPATH_W) 'queues/jobs/process_message_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/process_message_job.c'; fi` + +delete_ike_sa_job.o: queues/jobs/delete_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_ike_sa_job.o -MD -MP -MF "$(DEPDIR)/delete_ike_sa_job.Tpo" -c -o delete_ike_sa_job.o `test -f 'queues/jobs/delete_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_ike_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_ike_sa_job.Tpo" "$(DEPDIR)/delete_ike_sa_job.Po"; else rm -f "$(DEPDIR)/delete_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_ike_sa_job.c' object='delete_ike_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_ike_sa_job.o `test -f 'queues/jobs/delete_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_ike_sa_job.c + +delete_ike_sa_job.obj: queues/jobs/delete_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_ike_sa_job.obj -MD -MP -MF "$(DEPDIR)/delete_ike_sa_job.Tpo" -c -o delete_ike_sa_job.obj `if test -f 'queues/jobs/delete_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_ike_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_ike_sa_job.Tpo" "$(DEPDIR)/delete_ike_sa_job.Po"; else rm -f "$(DEPDIR)/delete_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_ike_sa_job.c' object='delete_ike_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_ike_sa_job.obj `if test -f 'queues/jobs/delete_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_ike_sa_job.c'; fi` + +retransmit_job.o: queues/jobs/retransmit_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT retransmit_job.o -MD -MP -MF "$(DEPDIR)/retransmit_job.Tpo" -c -o retransmit_job.o `test -f 'queues/jobs/retransmit_job.c' || echo '$(srcdir)/'`queues/jobs/retransmit_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/retransmit_job.Tpo" "$(DEPDIR)/retransmit_job.Po"; else rm -f "$(DEPDIR)/retransmit_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/retransmit_job.c' object='retransmit_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o retransmit_job.o `test -f 'queues/jobs/retransmit_job.c' || echo '$(srcdir)/'`queues/jobs/retransmit_job.c + +retransmit_job.obj: queues/jobs/retransmit_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT retransmit_job.obj -MD -MP -MF "$(DEPDIR)/retransmit_job.Tpo" -c -o retransmit_job.obj `if test -f 'queues/jobs/retransmit_job.c'; then $(CYGPATH_W) 'queues/jobs/retransmit_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/retransmit_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/retransmit_job.Tpo" "$(DEPDIR)/retransmit_job.Po"; else rm -f "$(DEPDIR)/retransmit_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/retransmit_job.c' object='retransmit_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o retransmit_job.obj `if test -f 'queues/jobs/retransmit_job.c'; then $(CYGPATH_W) 'queues/jobs/retransmit_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/retransmit_job.c'; fi` + +initiate_job.o: queues/jobs/initiate_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initiate_job.o -MD -MP -MF "$(DEPDIR)/initiate_job.Tpo" -c -o initiate_job.o `test -f 'queues/jobs/initiate_job.c' || echo '$(srcdir)/'`queues/jobs/initiate_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/initiate_job.Tpo" "$(DEPDIR)/initiate_job.Po"; else rm -f "$(DEPDIR)/initiate_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/initiate_job.c' object='initiate_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o initiate_job.o `test -f 'queues/jobs/initiate_job.c' || echo '$(srcdir)/'`queues/jobs/initiate_job.c + +initiate_job.obj: queues/jobs/initiate_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initiate_job.obj -MD -MP -MF "$(DEPDIR)/initiate_job.Tpo" -c -o initiate_job.obj `if test -f 'queues/jobs/initiate_job.c'; then $(CYGPATH_W) 'queues/jobs/initiate_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/initiate_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/initiate_job.Tpo" "$(DEPDIR)/initiate_job.Po"; else rm -f "$(DEPDIR)/initiate_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/initiate_job.c' object='initiate_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o initiate_job.obj `if test -f 'queues/jobs/initiate_job.c'; then $(CYGPATH_W) 'queues/jobs/initiate_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/initiate_job.c'; fi` + +send_keepalive_job.o: queues/jobs/send_keepalive_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_keepalive_job.o -MD -MP -MF "$(DEPDIR)/send_keepalive_job.Tpo" -c -o send_keepalive_job.o `test -f 'queues/jobs/send_keepalive_job.c' || echo '$(srcdir)/'`queues/jobs/send_keepalive_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_keepalive_job.Tpo" "$(DEPDIR)/send_keepalive_job.Po"; else rm -f "$(DEPDIR)/send_keepalive_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_keepalive_job.c' object='send_keepalive_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_keepalive_job.o `test -f 'queues/jobs/send_keepalive_job.c' || echo '$(srcdir)/'`queues/jobs/send_keepalive_job.c + +send_keepalive_job.obj: queues/jobs/send_keepalive_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_keepalive_job.obj -MD -MP -MF "$(DEPDIR)/send_keepalive_job.Tpo" -c -o send_keepalive_job.obj `if test -f 'queues/jobs/send_keepalive_job.c'; then $(CYGPATH_W) 'queues/jobs/send_keepalive_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_keepalive_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_keepalive_job.Tpo" "$(DEPDIR)/send_keepalive_job.Po"; else rm -f "$(DEPDIR)/send_keepalive_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_keepalive_job.c' object='send_keepalive_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_keepalive_job.obj `if test -f 'queues/jobs/send_keepalive_job.c'; then $(CYGPATH_W) 'queues/jobs/send_keepalive_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_keepalive_job.c'; fi` + +rekey_child_sa_job.o: queues/jobs/rekey_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_child_sa_job.o -MD -MP -MF "$(DEPDIR)/rekey_child_sa_job.Tpo" -c -o rekey_child_sa_job.o `test -f 'queues/jobs/rekey_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_child_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_child_sa_job.Tpo" "$(DEPDIR)/rekey_child_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_child_sa_job.c' object='rekey_child_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_child_sa_job.o `test -f 'queues/jobs/rekey_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_child_sa_job.c + +rekey_child_sa_job.obj: queues/jobs/rekey_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_child_sa_job.obj -MD -MP -MF "$(DEPDIR)/rekey_child_sa_job.Tpo" -c -o rekey_child_sa_job.obj `if test -f 'queues/jobs/rekey_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_child_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_child_sa_job.Tpo" "$(DEPDIR)/rekey_child_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_child_sa_job.c' object='rekey_child_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_child_sa_job.obj `if test -f 'queues/jobs/rekey_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_child_sa_job.c'; fi` + +delete_child_sa_job.o: queues/jobs/delete_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_child_sa_job.o -MD -MP -MF "$(DEPDIR)/delete_child_sa_job.Tpo" -c -o delete_child_sa_job.o `test -f 'queues/jobs/delete_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_child_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_child_sa_job.Tpo" "$(DEPDIR)/delete_child_sa_job.Po"; else rm -f "$(DEPDIR)/delete_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_child_sa_job.c' object='delete_child_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_child_sa_job.o `test -f 'queues/jobs/delete_child_sa_job.c' || echo '$(srcdir)/'`queues/jobs/delete_child_sa_job.c + +delete_child_sa_job.obj: queues/jobs/delete_child_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT delete_child_sa_job.obj -MD -MP -MF "$(DEPDIR)/delete_child_sa_job.Tpo" -c -o delete_child_sa_job.obj `if test -f 'queues/jobs/delete_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_child_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/delete_child_sa_job.Tpo" "$(DEPDIR)/delete_child_sa_job.Po"; else rm -f "$(DEPDIR)/delete_child_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/delete_child_sa_job.c' object='delete_child_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o delete_child_sa_job.obj `if test -f 'queues/jobs/delete_child_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/delete_child_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/delete_child_sa_job.c'; fi` + +send_dpd_job.o: queues/jobs/send_dpd_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_dpd_job.o -MD -MP -MF "$(DEPDIR)/send_dpd_job.Tpo" -c -o send_dpd_job.o `test -f 'queues/jobs/send_dpd_job.c' || echo '$(srcdir)/'`queues/jobs/send_dpd_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_dpd_job.Tpo" "$(DEPDIR)/send_dpd_job.Po"; else rm -f "$(DEPDIR)/send_dpd_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_dpd_job.c' object='send_dpd_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_dpd_job.o `test -f 'queues/jobs/send_dpd_job.c' || echo '$(srcdir)/'`queues/jobs/send_dpd_job.c + +send_dpd_job.obj: queues/jobs/send_dpd_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT send_dpd_job.obj -MD -MP -MF "$(DEPDIR)/send_dpd_job.Tpo" -c -o send_dpd_job.obj `if test -f 'queues/jobs/send_dpd_job.c'; then $(CYGPATH_W) 'queues/jobs/send_dpd_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_dpd_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/send_dpd_job.Tpo" "$(DEPDIR)/send_dpd_job.Po"; else rm -f "$(DEPDIR)/send_dpd_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/send_dpd_job.c' object='send_dpd_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o send_dpd_job.obj `if test -f 'queues/jobs/send_dpd_job.c'; then $(CYGPATH_W) 'queues/jobs/send_dpd_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/send_dpd_job.c'; fi` + +route_job.o: queues/jobs/route_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT route_job.o -MD -MP -MF "$(DEPDIR)/route_job.Tpo" -c -o route_job.o `test -f 'queues/jobs/route_job.c' || echo '$(srcdir)/'`queues/jobs/route_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/route_job.Tpo" "$(DEPDIR)/route_job.Po"; else rm -f "$(DEPDIR)/route_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/route_job.c' object='route_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o route_job.o `test -f 'queues/jobs/route_job.c' || echo '$(srcdir)/'`queues/jobs/route_job.c + +route_job.obj: queues/jobs/route_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT route_job.obj -MD -MP -MF "$(DEPDIR)/route_job.Tpo" -c -o route_job.obj `if test -f 'queues/jobs/route_job.c'; then $(CYGPATH_W) 'queues/jobs/route_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/route_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/route_job.Tpo" "$(DEPDIR)/route_job.Po"; else rm -f "$(DEPDIR)/route_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/route_job.c' object='route_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o route_job.obj `if test -f 'queues/jobs/route_job.c'; then $(CYGPATH_W) 'queues/jobs/route_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/route_job.c'; fi` + +acquire_job.o: queues/jobs/acquire_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acquire_job.o -MD -MP -MF "$(DEPDIR)/acquire_job.Tpo" -c -o acquire_job.o `test -f 'queues/jobs/acquire_job.c' || echo '$(srcdir)/'`queues/jobs/acquire_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/acquire_job.Tpo" "$(DEPDIR)/acquire_job.Po"; else rm -f "$(DEPDIR)/acquire_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/acquire_job.c' object='acquire_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acquire_job.o `test -f 'queues/jobs/acquire_job.c' || echo '$(srcdir)/'`queues/jobs/acquire_job.c + +acquire_job.obj: queues/jobs/acquire_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT acquire_job.obj -MD -MP -MF "$(DEPDIR)/acquire_job.Tpo" -c -o acquire_job.obj `if test -f 'queues/jobs/acquire_job.c'; then $(CYGPATH_W) 'queues/jobs/acquire_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/acquire_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/acquire_job.Tpo" "$(DEPDIR)/acquire_job.Po"; else rm -f "$(DEPDIR)/acquire_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/acquire_job.c' object='acquire_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acquire_job.obj `if test -f 'queues/jobs/acquire_job.c'; then $(CYGPATH_W) 'queues/jobs/acquire_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/acquire_job.c'; fi` + +rekey_ike_sa_job.o: queues/jobs/rekey_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_ike_sa_job.o -MD -MP -MF "$(DEPDIR)/rekey_ike_sa_job.Tpo" -c -o rekey_ike_sa_job.o `test -f 'queues/jobs/rekey_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_ike_sa_job.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_ike_sa_job.Tpo" "$(DEPDIR)/rekey_ike_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_ike_sa_job.c' object='rekey_ike_sa_job.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_ike_sa_job.o `test -f 'queues/jobs/rekey_ike_sa_job.c' || echo '$(srcdir)/'`queues/jobs/rekey_ike_sa_job.c + +rekey_ike_sa_job.obj: queues/jobs/rekey_ike_sa_job.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rekey_ike_sa_job.obj -MD -MP -MF "$(DEPDIR)/rekey_ike_sa_job.Tpo" -c -o rekey_ike_sa_job.obj `if test -f 'queues/jobs/rekey_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_ike_sa_job.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rekey_ike_sa_job.Tpo" "$(DEPDIR)/rekey_ike_sa_job.Po"; else rm -f "$(DEPDIR)/rekey_ike_sa_job.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/jobs/rekey_ike_sa_job.c' object='rekey_ike_sa_job.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rekey_ike_sa_job.obj `if test -f 'queues/jobs/rekey_ike_sa_job.c'; then $(CYGPATH_W) 'queues/jobs/rekey_ike_sa_job.c'; else $(CYGPATH_W) '$(srcdir)/queues/jobs/rekey_ike_sa_job.c'; fi` + +job_queue.o: queues/job_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job_queue.o -MD -MP -MF "$(DEPDIR)/job_queue.Tpo" -c -o job_queue.o `test -f 'queues/job_queue.c' || echo '$(srcdir)/'`queues/job_queue.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job_queue.Tpo" "$(DEPDIR)/job_queue.Po"; else rm -f "$(DEPDIR)/job_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/job_queue.c' object='job_queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job_queue.o `test -f 'queues/job_queue.c' || echo '$(srcdir)/'`queues/job_queue.c + +job_queue.obj: queues/job_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT job_queue.obj -MD -MP -MF "$(DEPDIR)/job_queue.Tpo" -c -o job_queue.obj `if test -f 'queues/job_queue.c'; then $(CYGPATH_W) 'queues/job_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/job_queue.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/job_queue.Tpo" "$(DEPDIR)/job_queue.Po"; else rm -f "$(DEPDIR)/job_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/job_queue.c' object='job_queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o job_queue.obj `if test -f 'queues/job_queue.c'; then $(CYGPATH_W) 'queues/job_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/job_queue.c'; fi` + +event_queue.o: queues/event_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT event_queue.o -MD -MP -MF "$(DEPDIR)/event_queue.Tpo" -c -o event_queue.o `test -f 'queues/event_queue.c' || echo '$(srcdir)/'`queues/event_queue.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/event_queue.Tpo" "$(DEPDIR)/event_queue.Po"; else rm -f "$(DEPDIR)/event_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/event_queue.c' object='event_queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o event_queue.o `test -f 'queues/event_queue.c' || echo '$(srcdir)/'`queues/event_queue.c + +event_queue.obj: queues/event_queue.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT event_queue.obj -MD -MP -MF "$(DEPDIR)/event_queue.Tpo" -c -o event_queue.obj `if test -f 'queues/event_queue.c'; then $(CYGPATH_W) 'queues/event_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/event_queue.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/event_queue.Tpo" "$(DEPDIR)/event_queue.Po"; else rm -f "$(DEPDIR)/event_queue.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='queues/event_queue.c' object='event_queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o event_queue.obj `if test -f 'queues/event_queue.c'; then $(CYGPATH_W) 'queues/event_queue.c'; else $(CYGPATH_W) '$(srcdir)/queues/event_queue.c'; fi` + +kernel_interface.o: threads/kernel_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT kernel_interface.o -MD -MP -MF "$(DEPDIR)/kernel_interface.Tpo" -c -o kernel_interface.o `test -f 'threads/kernel_interface.c' || echo '$(srcdir)/'`threads/kernel_interface.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/kernel_interface.Tpo" "$(DEPDIR)/kernel_interface.Po"; else rm -f "$(DEPDIR)/kernel_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/kernel_interface.c' object='kernel_interface.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o kernel_interface.o `test -f 'threads/kernel_interface.c' || echo '$(srcdir)/'`threads/kernel_interface.c + +kernel_interface.obj: threads/kernel_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT kernel_interface.obj -MD -MP -MF "$(DEPDIR)/kernel_interface.Tpo" -c -o kernel_interface.obj `if test -f 'threads/kernel_interface.c'; then $(CYGPATH_W) 'threads/kernel_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/kernel_interface.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/kernel_interface.Tpo" "$(DEPDIR)/kernel_interface.Po"; else rm -f "$(DEPDIR)/kernel_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/kernel_interface.c' object='kernel_interface.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o kernel_interface.obj `if test -f 'threads/kernel_interface.c'; then $(CYGPATH_W) 'threads/kernel_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/kernel_interface.c'; fi` + +thread_pool.o: threads/thread_pool.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT thread_pool.o -MD -MP -MF "$(DEPDIR)/thread_pool.Tpo" -c -o thread_pool.o `test -f 'threads/thread_pool.c' || echo '$(srcdir)/'`threads/thread_pool.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/thread_pool.Tpo" "$(DEPDIR)/thread_pool.Po"; else rm -f "$(DEPDIR)/thread_pool.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/thread_pool.c' object='thread_pool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o thread_pool.o `test -f 'threads/thread_pool.c' || echo '$(srcdir)/'`threads/thread_pool.c + +thread_pool.obj: threads/thread_pool.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT thread_pool.obj -MD -MP -MF "$(DEPDIR)/thread_pool.Tpo" -c -o thread_pool.obj `if test -f 'threads/thread_pool.c'; then $(CYGPATH_W) 'threads/thread_pool.c'; else $(CYGPATH_W) '$(srcdir)/threads/thread_pool.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/thread_pool.Tpo" "$(DEPDIR)/thread_pool.Po"; else rm -f "$(DEPDIR)/thread_pool.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/thread_pool.c' object='thread_pool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o thread_pool.obj `if test -f 'threads/thread_pool.c'; then $(CYGPATH_W) 'threads/thread_pool.c'; else $(CYGPATH_W) '$(srcdir)/threads/thread_pool.c'; fi` + +scheduler.o: threads/scheduler.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scheduler.o -MD -MP -MF "$(DEPDIR)/scheduler.Tpo" -c -o scheduler.o `test -f 'threads/scheduler.c' || echo '$(srcdir)/'`threads/scheduler.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/scheduler.Tpo" "$(DEPDIR)/scheduler.Po"; else rm -f "$(DEPDIR)/scheduler.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/scheduler.c' object='scheduler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scheduler.o `test -f 'threads/scheduler.c' || echo '$(srcdir)/'`threads/scheduler.c + +scheduler.obj: threads/scheduler.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scheduler.obj -MD -MP -MF "$(DEPDIR)/scheduler.Tpo" -c -o scheduler.obj `if test -f 'threads/scheduler.c'; then $(CYGPATH_W) 'threads/scheduler.c'; else $(CYGPATH_W) '$(srcdir)/threads/scheduler.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/scheduler.Tpo" "$(DEPDIR)/scheduler.Po"; else rm -f "$(DEPDIR)/scheduler.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/scheduler.c' object='scheduler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scheduler.obj `if test -f 'threads/scheduler.c'; then $(CYGPATH_W) 'threads/scheduler.c'; else $(CYGPATH_W) '$(srcdir)/threads/scheduler.c'; fi` + +sender.o: threads/sender.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sender.o -MD -MP -MF "$(DEPDIR)/sender.Tpo" -c -o sender.o `test -f 'threads/sender.c' || echo '$(srcdir)/'`threads/sender.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sender.Tpo" "$(DEPDIR)/sender.Po"; else rm -f "$(DEPDIR)/sender.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/sender.c' object='sender.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sender.o `test -f 'threads/sender.c' || echo '$(srcdir)/'`threads/sender.c + +sender.obj: threads/sender.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sender.obj -MD -MP -MF "$(DEPDIR)/sender.Tpo" -c -o sender.obj `if test -f 'threads/sender.c'; then $(CYGPATH_W) 'threads/sender.c'; else $(CYGPATH_W) '$(srcdir)/threads/sender.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sender.Tpo" "$(DEPDIR)/sender.Po"; else rm -f "$(DEPDIR)/sender.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/sender.c' object='sender.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sender.obj `if test -f 'threads/sender.c'; then $(CYGPATH_W) 'threads/sender.c'; else $(CYGPATH_W) '$(srcdir)/threads/sender.c'; fi` + +receiver.o: threads/receiver.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT receiver.o -MD -MP -MF "$(DEPDIR)/receiver.Tpo" -c -o receiver.o `test -f 'threads/receiver.c' || echo '$(srcdir)/'`threads/receiver.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/receiver.Tpo" "$(DEPDIR)/receiver.Po"; else rm -f "$(DEPDIR)/receiver.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/receiver.c' object='receiver.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o receiver.o `test -f 'threads/receiver.c' || echo '$(srcdir)/'`threads/receiver.c + +receiver.obj: threads/receiver.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT receiver.obj -MD -MP -MF "$(DEPDIR)/receiver.Tpo" -c -o receiver.obj `if test -f 'threads/receiver.c'; then $(CYGPATH_W) 'threads/receiver.c'; else $(CYGPATH_W) '$(srcdir)/threads/receiver.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/receiver.Tpo" "$(DEPDIR)/receiver.Po"; else rm -f "$(DEPDIR)/receiver.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/receiver.c' object='receiver.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o receiver.obj `if test -f 'threads/receiver.c'; then $(CYGPATH_W) 'threads/receiver.c'; else $(CYGPATH_W) '$(srcdir)/threads/receiver.c'; fi` + +stroke_interface.o: threads/stroke_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT stroke_interface.o -MD -MP -MF "$(DEPDIR)/stroke_interface.Tpo" -c -o stroke_interface.o `test -f 'threads/stroke_interface.c' || echo '$(srcdir)/'`threads/stroke_interface.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/stroke_interface.Tpo" "$(DEPDIR)/stroke_interface.Po"; else rm -f "$(DEPDIR)/stroke_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/stroke_interface.c' object='stroke_interface.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o stroke_interface.o `test -f 'threads/stroke_interface.c' || echo '$(srcdir)/'`threads/stroke_interface.c + +stroke_interface.obj: threads/stroke_interface.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT stroke_interface.obj -MD -MP -MF "$(DEPDIR)/stroke_interface.Tpo" -c -o stroke_interface.obj `if test -f 'threads/stroke_interface.c'; then $(CYGPATH_W) 'threads/stroke_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/stroke_interface.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/stroke_interface.Tpo" "$(DEPDIR)/stroke_interface.Po"; else rm -f "$(DEPDIR)/stroke_interface.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='threads/stroke_interface.c' object='stroke_interface.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o stroke_interface.obj `if test -f 'threads/stroke_interface.c'; then $(CYGPATH_W) 'threads/stroke_interface.c'; else $(CYGPATH_W) '$(srcdir)/threads/stroke_interface.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(eapdir)" "$(DESTDIR)$(ipsecdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-eapLTLIBRARIES clean-generic clean-ipsecPROGRAMS \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-eapLTLIBRARIES install-ipsecPROGRAMS + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-eapLTLIBRARIES uninstall-info-am \ + uninstall-ipsecPROGRAMS + +.PHONY: CTAGS GTAGS all all-am check check-am clean \ + clean-eapLTLIBRARIES clean-generic clean-ipsecPROGRAMS \ + clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-eapLTLIBRARIES \ + install-exec install-exec-am install-info install-info-am \ + install-ipsecPROGRAMS install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-eapLTLIBRARIES \ + uninstall-info-am uninstall-ipsecPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/charon/bus/bus.c b/src/charon/bus/bus.c new file mode 100644 index 000000000..740663d5c --- /dev/null +++ b/src/charon/bus/bus.c @@ -0,0 +1,397 @@ +/** + * @file bus.c + * + * @brief Implementation of bus_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "bus.h" + +#include + +ENUM(signal_names, SIG_ANY, SIG_MAX, + /** should not get printed */ + "SIG_ANY", + /** debugging message types */ + "DMN", + "MGR", + "IKE", + "CHD", + "JOB", + "CFG", + "KNL", + "NET", + "ENC", + "LIB", + /** should not get printed */ + "SIG_DBG_MAX", + /** all level0 signals are AUDIT signals */ + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + "AUD", "AUD", "AUD", + /** should not get printed */ + "SIG_MAX", +); + +typedef struct active_listener_t active_listener_t; + +/** + * information for a active listener + */ +struct active_listener_t { + + /** + * associated thread + */ + pthread_t id; + + /** + * condvar to wait for a signal + */ + pthread_cond_t cond; + + /** + * state of the thread + */ + enum { + /** not registered, do not wait for thread */ + UNREGISTERED, + /** registered, if a signal occurs, wait until it is LISTENING */ + REGISTERED, + /** listening, deliver signal */ + LISTENING, + } state; + + /** + * currently processed signals type + */ + signal_t signal; + + /** + * verbosity level of the signal + */ + level_t level; + + /** + * current processed signals thread number + */ + int thread; + + /** + * currently processed signals ike_sa + */ + ike_sa_t *ike_sa; + + /** + * currently processed signals format string + */ + char *format; + + /** + * currently processed signals format varargs + */ + va_list args; + +}; + +typedef struct private_bus_t private_bus_t; + +/** + * Private data of a bus_t object. + */ +struct private_bus_t { + /** + * Public part of a bus_t object. + */ + bus_t public; + + /** + * List of registered listeners implementing the bus_t interface + */ + linked_list_t *listeners; + + /** + * List of active listeners with listener_state TRUE + */ + linked_list_t *active_listeners; + + /** + * mutex to synchronize active listeners + */ + pthread_mutex_t mutex; + + /** + * Thread local storage for a unique, simple thread ID + */ + pthread_key_t thread_id; + + /** + * Thread local storage the threads IKE_SA + */ + pthread_key_t thread_sa; + +}; + +/** + * Get a unique thread number for a calling thread. Since + * pthread_self returns large and ugly numbers, use this function + * for logging; these numbers are incremental starting at 1 + */ +static int get_thread_number(private_bus_t *this) +{ + static long current_num = 0; + static long stored_num; + + stored_num = (long)pthread_getspecific(this->thread_id); + if (stored_num == 0) + { /* first call of current thread */ + pthread_setspecific(this->thread_id, (void*)++current_num); + return current_num; + } + else + { + return stored_num; + } +} + +/** + * Implementation of bus_t.add_listener. + */ +static void add_listener(private_bus_t *this, bus_listener_t *listener) +{ + pthread_mutex_lock(&this->mutex); + this->listeners->insert_last(this->listeners, listener); + pthread_mutex_unlock(&this->mutex); +} + +/** + * Get the listener object for the calling thread + */ +static active_listener_t *get_active_listener(private_bus_t *this) +{ + active_listener_t *current, *found = NULL; + iterator_t *iterator; + + /* if the thread was here once before, we have a active_listener record */ + iterator = this->active_listeners->create_iterator(this->active_listeners, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->id == pthread_self()) + { + found = current; + break; + } + } + iterator->destroy(iterator); + + if (found == NULL) + { + /* create a new object for a never-seen thread */ + found = malloc_thing(active_listener_t); + found->id = pthread_self(); + pthread_cond_init(&found->cond, NULL); + this->active_listeners->insert_last(this->active_listeners, found); + } + + return found; +} + +/** + * Implementation of bus_t.listen. + */ +static signal_t listen_(private_bus_t *this, level_t *level, int *thread, + ike_sa_t **ike_sa, char** format, va_list* args) +{ + active_listener_t *listener; + + pthread_mutex_lock(&this->mutex); + listener = get_active_listener(this); + /* go "listening", say hello to a thread which have a signal for us */ + listener->state = LISTENING; + pthread_cond_broadcast(&listener->cond); + /* wait until it has us delivered a signal, and go back to "registered" */ + pthread_cond_wait(&listener->cond, &this->mutex); + pthread_mutex_unlock(&this->mutex); + + /* return signal values */ + *level = listener->level; + *thread = listener->thread; + *ike_sa = listener->ike_sa; + *format = listener->format; + va_copy(*args, listener->args); + va_end(listener->args); + + return listener->signal; +} + +/** + * Implementation of bus_t.set_listen_state. + */ +static void set_listen_state(private_bus_t *this, bool active) +{ + active_listener_t *listener; + + pthread_mutex_lock(&this->mutex); + + listener = get_active_listener(this); + if (active) + { + listener->state = REGISTERED; + } + else + { + listener->state = UNREGISTERED; + /* say hello to signal emitter; we are finished processing the signal */ + pthread_cond_signal(&listener->cond); + } + + pthread_mutex_unlock(&this->mutex); +} + + +/** + * Implementation of bus_t.set_sa. + */ +static void set_sa(private_bus_t *this, ike_sa_t *ike_sa) +{ + pthread_setspecific(this->thread_sa, ike_sa); +} + +/** + * Implementation of bus_t.vsignal. + */ +static void vsignal(private_bus_t *this, signal_t signal, level_t level, + char* format, va_list args) +{ + iterator_t *iterator; + bus_listener_t *listener; + active_listener_t *active_listener; + ike_sa_t *ike_sa; + long thread; + + ike_sa = pthread_getspecific(this->thread_sa); + thread = get_thread_number(this); + + pthread_mutex_lock(&this->mutex); + + /* do the job for all passive bus_listeners */ + iterator = this->listeners->create_iterator(this->listeners, TRUE); + while (iterator->iterate(iterator, (void**)&listener)) + { + va_list args_copy; + + va_copy(args_copy, args); + if (!listener->signal(listener, signal, level, thread, + ike_sa, format, args_copy)) + { + /* unregister listener if requested */ + iterator->remove(iterator); + } + va_end(args_copy); + } + iterator->destroy(iterator); + + /* wake up all active listeners */ + iterator = this->active_listeners->create_iterator(this->active_listeners, TRUE); + while (iterator->iterate(iterator, (void**)&active_listener)) + { + /* wait until it is back */ + while (active_listener->state == REGISTERED) + { + pthread_cond_wait(&active_listener->cond, &this->mutex); + } + /* if thread is listening now, give it the signal to process */ + if (active_listener->state == LISTENING) + { + active_listener->level = level; + active_listener->thread = thread; + active_listener->ike_sa = ike_sa; + active_listener->signal = signal; + active_listener->format = format; + va_copy(active_listener->args, args); + active_listener->state = REGISTERED; + pthread_cond_signal(&active_listener->cond); + } + } + + /* we must wait now until all are not in state REGISTERED, + * as they may still use our arguments */ + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&active_listener)) + { + while (active_listener->state == REGISTERED) + { + pthread_cond_wait(&active_listener->cond, &this->mutex); + } + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&this->mutex); +} + +/** + * Implementation of bus_t.signal. + */ +static void signal_(private_bus_t *this, signal_t signal, level_t level, + char* format, ...) +{ + va_list args; + + va_start(args, format); + vsignal(this, signal, level, format, args); + va_end(args); +} + +/** + * Implementation of bus_t.destroy. + */ +static void destroy(private_bus_t *this) +{ + this->active_listeners->destroy_function(this->active_listeners, free); + this->listeners->destroy(this->listeners); + free(this); +} + +/* + * Described in header. + */ +bus_t *bus_create() +{ + private_bus_t *this = malloc_thing(private_bus_t); + + this->public.add_listener = (void(*)(bus_t*,bus_listener_t*))add_listener; + this->public.listen = (signal_t(*)(bus_t*,level_t*,int*,ike_sa_t**,char**,va_list*))listen_; + this->public.set_listen_state = (void(*)(bus_t*,bool))set_listen_state; + this->public.set_sa = (void(*)(bus_t*,ike_sa_t*))set_sa; + this->public.signal = (void(*)(bus_t*,signal_t,level_t,char*,...))signal_; + this->public.vsignal = (void(*)(bus_t*,signal_t,level_t,char*,va_list))vsignal; + this->public.destroy = (void(*)(bus_t*)) destroy; + + this->listeners = linked_list_create(); + this->active_listeners = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + pthread_key_create(&this->thread_id, NULL); + pthread_key_create(&this->thread_sa, NULL); + + return &(this->public); +} diff --git a/src/charon/bus/bus.h b/src/charon/bus/bus.h new file mode 100644 index 000000000..200525fb7 --- /dev/null +++ b/src/charon/bus/bus.h @@ -0,0 +1,366 @@ +/** + * @file bus.h + * + * @brief Interface of bus_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef BUS_H_ +#define BUS_H_ + +typedef enum signal_t signal_t; +typedef enum level_t level_t; +typedef struct bus_listener_t bus_listener_t; +typedef struct bus_t bus_t; + +#include + +#include +#include + + +/** + * @brief signals emitted by the daemon. + * + * Signaling is for different purporses. First, it allows debugging via + * "debugging signal messages", sencondly, it allows to follow certain + * mechanisms currently going on in the daemon. As we are multithreaded, + * and of multiple transactions are involved, it's not possible to follow + * one connection setup without further infrastructure. These infrastructure + * is provided by the bus and the signals the daemon emits to the bus. + * + * There are different scenarios to follow these signals, but all have + * the same scheme. First, a START signal is emitted to indicate the daemon + * has started to + * + * @ingroup bus + */ +enum signal_t { + /** pseudo signal, representing any other signal */ + SIG_ANY, + + /** debugging message from daemon main loop */ + DBG_DMN, + /** debugging message from IKE_SA_MANAGER */ + DBG_MGR, + /** debugging message from an IKE_SA */ + DBG_IKE, + /** debugging message from a CHILD_SA */ + DBG_CHD, + /** debugging message from job processing */ + DBG_JOB, + /** debugging message from configuration backends */ + DBG_CFG, + /** debugging message from kernel interface */ + DBG_KNL, + /** debugging message from networking */ + DBG_NET, + /** debugging message from message encoding/decoding */ + DBG_ENC, + /** debugging message from libstrongswan via logging hook */ + DBG_LIB, + + /** number of debug signals */ + DBG_MAX, + + /** signals for IKE_SA establishment */ + IKE_UP_START, + IKE_UP_SUCCESS, + IKE_UP_FAILED, + + /** signals for IKE_SA delete */ + IKE_DOWN_START, + IKE_DOWN_SUCCESS, + IKE_DOWN_FAILED, + + /** signals for IKE_SA rekeying */ + IKE_REKEY_START, + IKE_REKEY_SUCCESS, + IKE_REKEY_FAILED, + + /** signals for CHILD_SA establishment */ + CHILD_UP_START, + CHILD_UP_SUCCESS, + CHILD_UP_FAILED, + + /** signals for CHILD_SA delete */ + CHILD_DOWN_START, + CHILD_DOWN_SUCCESS, + CHILD_DOWN_FAILED, + + /** signals for CHILD_SA rekeying */ + CHILD_REKEY_START, + CHILD_REKEY_SUCCESS, + CHILD_REKEY_FAILED, + + /** signals for CHILD_SA routing */ + CHILD_ROUTE_START, + CHILD_ROUTE_SUCCESS, + CHILD_ROUTE_FAILED, + + /** signals for CHILD_SA routing */ + CHILD_UNROUTE_START, + CHILD_UNROUTE_SUCCESS, + CHILD_UNROUTE_FAILED, + + SIG_MAX +}; + +/** + * short names of signals using 3 chars + */ +extern enum_name_t *signal_names; + +/** + * Signal levels used to control output verbosity. + */ +enum level_t { + /** numerical levels from 0 to 4 */ + LEVEL_0 = 0, + LEVEL_1 = 1, + LEVEL_2 = 2, + LEVEL_3 = 3, + LEVEL_4 = 4, + /** absolutely silent, no signal is emitted with this level */ + LEVEL_SILENT = -1, + /** alias for numberical levels */ + LEVEL_AUDIT = LEVEL_0, + LEVEL_CTRL = LEVEL_1, + LEVEL_CTRLMORE = LEVEL_2, + LEVEL_RAW = LEVEL_3, + LEVEL_PRIVATE = LEVEL_4, +}; + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 4 +#endif /* DEBUG_LEVEL */ + +#if DEBUG_LEVEL >= 1 +/** + * @brief Log a debug message via the signal bus. + * + * @param signal signal_t signal description + * @param format printf() style format string + * @param ... printf() style agument list + */ +# define DBG1(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_1, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 2 +#define DBG2(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_2, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 3 +#define DBG3(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_3, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 4 +#define DBG4(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_4, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ + +#ifndef DBG1 +# define DBG1(...) {} +#endif /* DBG1 */ +#ifndef DBG2 +# define DBG2(...) {} +#endif /* DBG2 */ +#ifndef DBG3 +# define DBG3(...) {} +#endif /* DBG3 */ +#ifndef DBG4 +# define DBG4(...) {} +#endif /* DBG4 */ + +/** + * @brief Raise a signal for an occured event. + * + * @param sig signal_t signal description + * @param format printf() style format string + * @param ... printf() style agument list + */ +#define SIG(sig, format, ...) charon->bus->signal(charon->bus, sig, LEVEL_0, format, ##__VA_ARGS__) + +/** + * @brief Get the type of a signal. + * + * A signal may be a debugging signal with a specific context. They have + * a level specific for their context > 0. All audit signals use the + * type 0. This allows filtering of singals by their type. + * + * @param signal signal to get the type from + * @return type of the signal, between 0..(DBG_MAX-1) + */ +#define SIG_TYPE(sig) (sig > DBG_MAX ? SIG_ANY : sig) + + +/** + * @brief Interface for registering at the signal bus. + * + * To receive signals from the bus, the client implementing the + * bus_listener_t interface registers itself at the signal bus. + * + * @ingroup bus + */ +struct bus_listener_t { + + /** + * @brief Send a signal to a bus listener. + * + * A numerical identification for the thread is included, as the + * associated IKE_SA, if any. Signal specifies the type of + * the event occured. The format string specifies + * an additional informational or error message with a printf() like + * variable argument list. This is in the va_list form, as forwarding + * a "..." parameters to functions is not (cleanly) possible. + * The implementing signal function returns TRUE to stay registered + * to the bus, or FALSE to unregister itself. + * + * @param this listener + * @param singal kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param thread ID of the thread raised this signal + * @param ike_sa IKE_SA associated to the event + * @param format printf() style format string + * @param args vprintf() style va_list argument list + " @return TRUE to stay registered, FALSE to unregister + */ + bool (*signal) (bus_listener_t *this, signal_t signal, level_t level, + int thread, ike_sa_t *ike_sa, char* format, va_list args); +}; + +/** + * @brief Signal bus which sends signals to registered listeners. + * + * The signal bus is not much more than a multiplexer. A listener interested + * in receiving event signals registers at the bus. Any signals sent to + * are delivered to all registered listeners. + * To deliver signals to threads, the blocking listen() call may be used + * to wait for a signal. + * + * @ingroup bus + */ +struct bus_t { + + /** + * @brief Register a listener to the bus. + * + * A registered listener receives all signals which are sent to the bus. + * The listener is passive; the thread which emitted the signal + * processes the listener routine. + * + * @param this bus + * @param listener listener to register. + */ + void (*add_listener) (bus_t *this, bus_listener_t *listener); + + /** + * @brief Listen actively on the bus. + * + * As we are fully multithreaded, we must provide a mechanism + * for active threads to listen to the bus. With the listen() method, + * a thread waits until a signal occurs, and then processes it. + * To prevent the listen() calling thread to miss signals emitted while + * it processes a signal, registration is required. This is done through + * the set_listen_state() method, see below. + * + * @param this bus + * @param level verbosity level of the signal + * @param thread receives thread number emitted the signal + * @param ike_sa receives the IKE_SA involved in the signal, or NULL + * @param format receives the format string supplied with the signal + * @param va_list receives the variable argument list for format + * @return the emitted signal type + */ + signal_t (*listen) (bus_t *this, level_t* level, int *thread, + ike_sa_t **ike_sa, char** format, va_list* args); + + /** + * @brief Set the listening state of the calling thread. + * + * To prevent message loss for active listeners using listen(), threads + * must register themself to the bus before starting to listen(). When + * a signal occurs, the emitter waits until all threads with listen_state + * TRUE are waiting in the listen() method to process the signal. + * It is important that a thread with liste_state TRUE calls listen() + * periodically, or sets it's listening state to FALSE; otherwise + * all signal emitting threads get blocked on the bus. + * + * @param this bus + * @param active TRUE to set to listening + */ + void (*set_listen_state) (bus_t *this, bool active); + + /** + * @brief Set the IKE_SA the calling thread is using. + * + * To associate an received signal to an IKE_SA without passing it as + * parameter each time, the thread registers it's used IKE_SA each + * time it checked it out. Before checking it in, the thread unregisters + * the IKE_SA (by passing NULL). This IKE_SA is stored per-thread, so each + * thread has one IKE_SA registered (or not). + * + * @param this bus + * @param ike_sa ike_sa to register, or NULL to unregister + */ + void (*set_sa) (bus_t *this, ike_sa_t *ike_sa); + + /** + * @brief Send a signal to the bus. + * + * The signal specifies the type of the event occured. The format string + * specifies an additional informational or error message with a + * printf() like variable argument list. + * Some useful macros are available to shorten this call. + * @see SIG(), DBG1() + * + * @param this bus + * @param singal kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param format printf() style format string + * @param ... printf() style argument list + */ + void (*signal) (bus_t *this, signal_t signal, level_t level, char* format, ...); + + /** + * @brief Send a signal to the bus using va_list arguments. + * + * Same as bus_t.signal(), but uses va_list argument list. + * + * @param this bus + * @param singal kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param format printf() style format string + * @param args va_list arguments + */ + void (*vsignal) (bus_t *this, signal_t signal, level_t level, char* format, va_list args); + + /** + * @brief Destroy the signal bus. + * + * @param this bus to destroy + */ + void (*destroy) (bus_t *this); +}; + +/** + * @brief Create the signal bus which multiplexes signals to its listeners. + * + * @return signal bus instance + * + * @ingroup bus + */ +bus_t *bus_create(); + +#endif /* BUS_H_ */ diff --git a/src/charon/bus/listeners/file_logger.c b/src/charon/bus/listeners/file_logger.c new file mode 100644 index 000000000..14f9f72cf --- /dev/null +++ b/src/charon/bus/listeners/file_logger.c @@ -0,0 +1,128 @@ +/** + * @file file_logger.c + * + * @brief Implementation of file_logger_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "file_logger.h" + + +typedef struct private_file_logger_t private_file_logger_t; + +/** + * Private data of a file_logger_t object + */ +struct private_file_logger_t { + + /** + * Public data. + */ + file_logger_t public; + + /** + * output file + */ + FILE *out; + + /** + * Maximum level to log + */ + level_t levels[DBG_MAX]; +}; + + +/** + * Implementation of bus_listener_t.signal. + */ +static bool signal_(private_file_logger_t *this, signal_t signal, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->levels[SIG_TYPE(signal)]) + { + char buffer[8192]; + char *current = buffer, *next; + + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), format, args); + + /* prepend a prefix in front of every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + fprintf(this->out, "%.2d[%N] %s\n", thread, signal_names, signal, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +/** + * Implementation of file_logger_t.set_level. + */ +static void set_level(private_file_logger_t *this, signal_t signal, level_t level) +{ + if (signal == SIG_ANY) + { + int i; + for (i = 0; i < DBG_MAX; i++) + { + this->levels[i] = level; + } + } + else + { + + this->levels[SIG_TYPE(signal)] = level; + } +} + +/** + * Implementation of file_logger_t.destroy. + */ +static void destroy(private_file_logger_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +file_logger_t *file_logger_create(FILE *out) +{ + private_file_logger_t *this = malloc_thing(private_file_logger_t); + + /* public functions */ + this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; + this->public.set_level = (void(*)(file_logger_t*,signal_t,level_t))set_level; + this->public.destroy = (void(*)(file_logger_t*))destroy; + + /* private variables */ + this->out = out; + set_level(this, SIG_ANY, LEVEL_SILENT); + + return &this->public; +} diff --git a/src/charon/bus/listeners/file_logger.h b/src/charon/bus/listeners/file_logger.h new file mode 100644 index 000000000..d67daba25 --- /dev/null +++ b/src/charon/bus/listeners/file_logger.h @@ -0,0 +1,73 @@ +/** + * @file file_logger.h + * + * @brief Interface of file_logger_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef FILE_LOGGER_H_ +#define FILE_LOGGER_H_ + +typedef struct file_logger_t file_logger_t; + +#include + +/** + * @brief Logger to files which implements bus_listener_t. + * + * @b Constructors: + * - file_logger_create() + * + * @ingroup listeners + */ +struct file_logger_t { + + /** + * Implements the bus_listener_t interface. + */ + bus_listener_t listener; + + /** + * @brief Set the loglevel for a signal type. + * + * @param this stream_logger_t object + * @param singal type of signal + * @param level max level to log (0..4) + */ + void (*set_level) (file_logger_t *this, signal_t signal, level_t level); + + /** + * @brief Destroys a file_logger_t object. + * + * @param this file_logger_t object + */ + void (*destroy) (file_logger_t *this); +}; + +/** + * @brief Constructor to create a file_logger_t object. + * + * @param out FILE to write to + * @return file_logger_t object + * + * @ingroup listeners + */ +file_logger_t *file_logger_create(FILE *out); + + +#endif /* FILE_LOGGER_H_ */ diff --git a/src/charon/bus/listeners/sys_logger.c b/src/charon/bus/listeners/sys_logger.c new file mode 100644 index 000000000..d26d14dc0 --- /dev/null +++ b/src/charon/bus/listeners/sys_logger.c @@ -0,0 +1,131 @@ +/** + * @file sys_logger.c + * + * @brief Implementation of sys_logger_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include + +#include "sys_logger.h" + + +typedef struct private_sys_logger_t private_sys_logger_t; + +/** + * Private data of a sys_logger_t object + */ +struct private_sys_logger_t { + + /** + * Public data. + */ + sys_logger_t public; + + /** + * syslog facility to use + */ + int facility; + + /** + * Maximum level to log + */ + level_t levels[DBG_MAX]; +}; + + +/** + * Implementation of bus_listener_t.signal. + */ +static bool signal_(private_sys_logger_t *this, signal_t signal, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->levels[SIG_TYPE(signal)]) + { + char buffer[8192]; + char *current = buffer, *next; + + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), format, args); + + /* do a syslog with every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + syslog(this->facility|LOG_INFO, "%.2d[%N] %s\n", + thread, signal_names, signal, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +/** + * Implementation of sys_logger_t.set_level. + */ +static void set_level(private_sys_logger_t *this, signal_t signal, level_t level) +{ + if (signal == SIG_ANY) + { + int i; + for (i = 0; i < DBG_MAX; i++) + { + this->levels[i] = level; + } + } + else + { + + this->levels[SIG_TYPE(signal)] = level; + } +} + +/** + * Implementation of sys_logger_t.destroy. + */ +static void destroy(private_sys_logger_t *this) +{ + closelog(); + free(this); +} + +/* + * Described in header. + */ +sys_logger_t *sys_logger_create(int facility) +{ + private_sys_logger_t *this = malloc_thing(private_sys_logger_t); + + /* public functions */ + this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_; + this->public.set_level = (void(*)(sys_logger_t*,signal_t,level_t))set_level; + this->public.destroy = (void(*)(sys_logger_t*))destroy; + + /* private variables */ + this->facility = facility; + set_level(this, SIG_ANY, LEVEL_SILENT); + + return &this->public; +} diff --git a/src/charon/bus/listeners/sys_logger.h b/src/charon/bus/listeners/sys_logger.h new file mode 100644 index 000000000..091217313 --- /dev/null +++ b/src/charon/bus/listeners/sys_logger.h @@ -0,0 +1,75 @@ +/** + * @file sys_logger.h + * + * @brief Interface of sys_logger_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SYS_LOGGER_H_ +#define SYS_LOGGER_H_ + +typedef struct sys_logger_t sys_logger_t; + +#include + +#include + +/** + * @brief Logger for syslog which implements bus_listener_t. + * + * @b Constructors: + * - sys_logger_create() + * + * @ingroup listeners + */ +struct sys_logger_t { + + /** + * Implements the bus_listener_t interface. + */ + bus_listener_t listener; + + /** + * @brief Set the loglevel for a signal type. + * + * @param this stream_logger_t object + * @param singal type of signal + * @param level max level to log + */ + void (*set_level) (sys_logger_t *this, signal_t signal, level_t level); + + /** + * @brief Destroys a sys_logger_t object. + * + * @param this sys_logger_t object + */ + void (*destroy) (sys_logger_t *this); +}; + +/** + * @brief Constructor to create a sys_logger_t object. + * + * @param facility syslog facility to use + * @return sys_logger_t object + * + * @ingroup listeners + */ +sys_logger_t *sys_logger_create(int facility); + + +#endif /* SYS_LOGGER_H_ */ diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c new file mode 100755 index 000000000..488ba9a5e --- /dev/null +++ b/src/charon/config/configuration.c @@ -0,0 +1,162 @@ +/** + * @file configuration.c + * + * @brief Implementation of configuration_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "configuration.h" + +#include + +/** + * Timeout in milliseconds after that a half open IKE_SA gets deleted. + */ +#define HALF_OPEN_IKE_SA_TIMEOUT 30000 + +/** + * Retransmission uses a backoff algorithm. The timeout is calculated using + * TIMEOUT * (BASE ** try). + * When try reaches TRIES, retransmission is given up. + * + * Using an initial TIMEOUT of 4s, a BASE of 1.8, and 5 TRIES gives us: + * + * | relative | absolute + * --------------------------------------------------------- + * 4s * (1.8 ** (0 % 5)) = 4s 4s + * 4s * (1.8 ** (1 % 5)) = 7s 11s + * 4s * (1.8 ** (2 % 5)) = 13s 24s + * 4s * (1.8 ** (3 % 5)) = 23s 47s + * 4s * (1.8 ** (4 % 5)) = 42s 89s + * 4s * (1.8 ** (5 % 5)) = 76s 165s + * + * The peer is considered dead after 2min 45s when no reply comes in. + */ + +/** + * First retransmit timeout in milliseconds. + * Timeout value is increasing in each retransmit round. + */ +#define RETRANSMIT_TIMEOUT 4000 + +/** + * Base which is raised to the power of the retransmission count. + */ +#define RETRANSMIT_BASE 1.8 + +/** + * Number of retransmits done in a retransmit sequence + */ +#define RETRANSMIT_TRIES 5 + +/** + * Keepalive interval in seconds. + */ +#define KEEPALIVE_INTERVAL 20 + +/** + * retry interval in seconds. + */ +#define RETRY_INTERVAL 30 + +/** + * jitter to user for retrying + */ +#define RETRY_JITTER 20 + + +typedef struct private_configuration_t private_configuration_t; + +/** + * Private data of an configuration_t object. + */ +struct private_configuration_t { + + /** + * Public part of configuration_t object. + */ + configuration_t public; + +}; + +/** + * Implementation of configuration_t.get_retransmit_timeout. + */ +static u_int32_t get_retransmit_timeout (private_configuration_t *this, + u_int32_t retransmit_count) +{ + if (retransmit_count > RETRANSMIT_TRIES) + { + /* give up */ + return 0; + } + return (u_int32_t) + (RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); +} + +/** + * Implementation of configuration_t.get_half_open_ike_sa_timeout. + */ +static u_int32_t get_half_open_ike_sa_timeout (private_configuration_t *this) +{ + return HALF_OPEN_IKE_SA_TIMEOUT; +} + +/** + * Implementation of configuration_t.get_keepalive_interval. + */ +static u_int32_t get_keepalive_interval (private_configuration_t *this) +{ + return KEEPALIVE_INTERVAL; +} + +/** + * Implementation of configuration_t.get_retry_interval. + */ +static u_int32_t get_retry_interval (private_configuration_t *this) +{ + return RETRY_INTERVAL - (random() % RETRY_JITTER); +} + +/** + * Implementation of configuration_t.destroy. + */ +static void destroy(private_configuration_t *this) +{ + free(this); +} + +/* + * Described in header-file + */ +configuration_t *configuration_create() +{ + private_configuration_t *this = malloc_thing(private_configuration_t); + + /* public functions */ + this->public.destroy = (void(*)(configuration_t*))destroy; + this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t))get_retransmit_timeout; + this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t*)) get_half_open_ike_sa_timeout; + this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t*)) get_keepalive_interval; + this->public.get_retry_interval = (u_int32_t (*) (configuration_t*)) get_retry_interval; + + return (&this->public); +} diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h new file mode 100755 index 000000000..c1207171d --- /dev/null +++ b/src/charon/config/configuration.h @@ -0,0 +1,102 @@ +/** + * @file configuration.h + * + * @brief Interface configuration_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ + +typedef struct configuration_t configuration_t; + +#include + +/** + * @brief The interface for various daemon related configs. + * + * @b Constructors: + * - configuration_create() + * + * @ingroup config + */ +struct configuration_t { + + /** + * @brief Returns the retransmit timeout. + * + * A return value of zero means the request should not be + * retransmitted again. + * + * @param this calling object + * @param retransmitted number of times a message was retransmitted so far + * @return time in milliseconds, when to do next retransmit + */ + u_int32_t (*get_retransmit_timeout) (configuration_t *this, + u_int32_t retransmitted); + + /** + * @brief Returns the timeout for an half open IKE_SA in ms. + * + * Half open means that the IKE_SA is still on a not established state + * + * @param this calling object + * @return timeout in milliseconds (ms) + */ + u_int32_t (*get_half_open_ike_sa_timeout) (configuration_t *this); + + /** + * @brief Returns the keepalive interval in s. + * + * The keepalive interval defines the idle time after which a + * NAT keepalive packet should be sent. + * + * @param this calling object + * @return interval in s + */ + u_int32_t (*get_keepalive_interval) (configuration_t *this); + + /** + * @brief Returns the interval to retry a failed action again. + * + * In some situations, the protocol may be in a state where processing + * is not possible and an action must be retried (e.g. rekeying). + * + * @param this calling object + * @return interval in s + */ + u_int32_t (*get_retry_interval) (configuration_t *this); + + /** + * @brief Destroys a configuration_t object. + * + * @param this calling object + */ + void (*destroy) (configuration_t *this); +}; + +/** + * @brief Creates a configuration backend. + * + * @return static_configuration_t object + * + * @ingroup config + */ +configuration_t *configuration_create(void); + +#endif /*CONFIGURATION_H_*/ diff --git a/src/charon/config/connections/connection.c b/src/charon/config/connections/connection.c new file mode 100644 index 000000000..ffe508992 --- /dev/null +++ b/src/charon/config/connections/connection.c @@ -0,0 +1,404 @@ +/** + * @file connection.c + * + * @brief Implementation of connection_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include +#include + +ENUM(cert_policy_names, CERT_ALWAYS_SEND, CERT_NEVER_SEND, + "CERT_ALWAYS_SEND", + "CERT_SEND_IF_ASKED", + "CERT_NEVER_SEND" +); + +typedef struct private_connection_t private_connection_t; + +/** + * Private data of an connection_t object + */ +struct private_connection_t { + + /** + * Public part + */ + connection_t public; + + /** + * Number of references hold by others to this connection + */ + refcount_t refcount; + + /** + * Name of the connection + */ + char *name; + + /** + * Does charon handle this connection? Or can he ignore it? + */ + bool ikev2; + + /** + * should we send a certificate request? + */ + cert_policy_t certreq_policy; + + /** + * should we send a certificates? + */ + cert_policy_t cert_policy; + + /** + * ID of us + */ + identification_t *my_id; + + /** + * Host information of my host. + */ + host_t *my_host; + + /** + * Host information of other host. + */ + host_t *other_host; + + /** + * Interval to send DPD liveness checks on inactivity + */ + u_int32_t dpd_delay; + + /** + * Number of retransmission sequences to send bevore giving up + */ + u_int32_t keyingtries; + + /** + * Supported proposals + */ + linked_list_t *proposals; + + /** + * Time before an SA gets invalid + */ + u_int32_t soft_lifetime; + + /** + * Time before an SA gets rekeyed + */ + u_int32_t hard_lifetime; + + /** + * Use full reauthentication instead of rekeying + */ + bool reauth; + + /** + * Time, which specifies the range of a random value + * substracted from soft_lifetime. + */ + u_int32_t jitter; +}; + +/** + * Implementation of connection_t.get_name. + */ +static char *get_name (private_connection_t *this) +{ + return this->name; +} + +/** + * Implementation of connection_t.is_ikev2. + */ +static bool is_ikev2 (private_connection_t *this) +{ + return this->ikev2; +} + +/** + * Implementation of connection_t.get_certreq_policy. + */ +static cert_policy_t get_certreq_policy (private_connection_t *this) +{ + return this->certreq_policy; +} + +/** + * Implementation of connection_t.get_cert_policy. + */ +static cert_policy_t get_cert_policy (private_connection_t *this) +{ + return this->cert_policy; +} + +/** + * Implementation of connection_t.get_my_host. + */ +static host_t *get_my_host (private_connection_t *this) +{ + return this->my_host; +} + +/** + * Implementation of connection_t.get_other_host. + */ +static host_t *get_other_host (private_connection_t *this) +{ + return this->other_host; +} + +/** + * Implementation of connection_t.get_proposals. + */ +static linked_list_t* get_proposals(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *current; + linked_list_t *proposals = linked_list_create(); + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + current = current->clone(current); + proposals->insert_last(proposals, (void*)current); + } + iterator->destroy(iterator); + + return proposals; +} + +/** + * Implementation of connection_t.select_proposal. + */ +static proposal_t *select_proposal(private_connection_t *this, linked_list_t *proposals) +{ + iterator_t *stored_iter, *supplied_iter; + proposal_t *stored, *supplied, *selected; + + stored_iter = this->proposals->create_iterator(this->proposals, TRUE); + supplied_iter = proposals->create_iterator(proposals, TRUE); + + /* compare all stored proposals with all supplied. Stored ones are preferred. */ + while (stored_iter->iterate(stored_iter, (void**)&stored)) + { + supplied_iter->reset(supplied_iter); + + while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) + { + selected = stored->select(stored, supplied); + if (selected) + { + /* they match, return */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + return selected; + } + } + } + /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return NULL; +} + +/** + * Implementation of connection_t.add_proposal. + */ +static void add_proposal(private_connection_t *this, proposal_t *proposal) +{ + this->proposals->insert_last(this->proposals, proposal); +} + +/** + * Implementation of connection_t.get_dpd_delay. + */ +static u_int32_t get_dpd_delay(private_connection_t *this) +{ + return this->dpd_delay; +} + +/** + * Implementation of connection_t.get_keyingtries. + */ +static u_int32_t get_keyingtries(private_connection_t *this) +{ + return this->keyingtries; +} + +/** + * Implementation of connection_t.get_dh_group. + */ +static diffie_hellman_group_t get_dh_group(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *proposal; + algorithm_t *algo; + diffie_hellman_group_t dh_group = MODP_NONE; + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &algo)) + { + dh_group = algo->algorithm; + break; + } + } + iterator->destroy(iterator); + return dh_group; +} + +/** + * Implementation of connection_t.check_dh_group. + */ +static bool check_dh_group(private_connection_t *this, diffie_hellman_group_t dh_group) +{ + iterator_t *prop_iter, *alg_iter; + proposal_t *proposal; + algorithm_t *algo; + + prop_iter = this->proposals->create_iterator(this->proposals, TRUE); + while (prop_iter->iterate(prop_iter, (void**)&proposal)) + { + alg_iter = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP); + while (alg_iter->iterate(alg_iter, (void**)&algo)) + { + if (algo->algorithm == dh_group) + { + prop_iter->destroy(prop_iter); + alg_iter->destroy(alg_iter); + return TRUE; + } + } + alg_iter->destroy(alg_iter); + } + prop_iter->destroy(prop_iter); + return FALSE; +} +/** + * Implementation of connection_t.get_soft_lifetime + */ +static u_int32_t get_soft_lifetime(private_connection_t *this) +{ + if (this->jitter == 0) + { + return this->soft_lifetime ; + } + return this->soft_lifetime - (random() % this->jitter); +} + +/** + * Implementation of connection_t.get_hard_lifetime. + */ +static u_int32_t get_hard_lifetime(private_connection_t *this) +{ + return this->hard_lifetime; +} + +/** + * Implementation of connection_t.get_reauth. + */ +static bool get_reauth(private_connection_t *this) +{ + return this->reauth; +} + +/** + * Implementation of connection_t.get_ref. + */ +static void get_ref(private_connection_t *this) +{ + ref_get(&this->refcount); +} + +/** + * Implementation of connection_t.destroy. + */ +static void destroy(private_connection_t *this) +{ + if (ref_put(&this->refcount)) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->my_host->destroy(this->my_host); + this->other_host->destroy(this->other_host); + free(this->name); + free(this); + } +} + +/** + * Described in header. + */ +connection_t * connection_create(char *name, bool ikev2, + cert_policy_t cert_policy, + cert_policy_t certreq_policy, + host_t *my_host, host_t *other_host, + u_int32_t dpd_delay, bool reauth, + u_int32_t keyingtries, + u_int32_t hard_lifetime, + u_int32_t soft_lifetime, u_int32_t jitter) +{ + private_connection_t *this = malloc_thing(private_connection_t); + + /* public functions */ + this->public.get_name = (char*(*)(connection_t*))get_name; + this->public.is_ikev2 = (bool(*)(connection_t*))is_ikev2; + this->public.get_cert_policy = (cert_policy_t(*)(connection_t*))get_cert_policy; + this->public.get_certreq_policy = (cert_policy_t(*)(connection_t*))get_certreq_policy; + this->public.get_my_host = (host_t*(*)(connection_t*))get_my_host; + this->public.get_other_host = (host_t*(*)(connection_t*))get_other_host; + this->public.get_proposals = (linked_list_t*(*)(connection_t*))get_proposals; + this->public.select_proposal = (proposal_t*(*)(connection_t*,linked_list_t*))select_proposal; + this->public.add_proposal = (void(*)(connection_t*, proposal_t*)) add_proposal; + this->public.get_dpd_delay = (u_int32_t(*)(connection_t*)) get_dpd_delay; + this->public.get_reauth = (bool(*)(connection_t*)) get_reauth; + this->public.get_keyingtries = (u_int32_t(*)(connection_t*)) get_keyingtries; + this->public.get_dh_group = (diffie_hellman_group_t(*)(connection_t*)) get_dh_group; + this->public.check_dh_group = (bool(*)(connection_t*,diffie_hellman_group_t)) check_dh_group; + this->public.get_soft_lifetime = (u_int32_t (*) (connection_t *))get_soft_lifetime; + this->public.get_hard_lifetime = (u_int32_t (*) (connection_t *))get_hard_lifetime; + this->public.get_ref = (void(*)(connection_t*))get_ref; + this->public.destroy = (void(*)(connection_t*))destroy; + + /* private variables */ + this->refcount = 1; + this->name = strdup(name); + this->ikev2 = ikev2; + this->cert_policy = cert_policy; + this->certreq_policy = certreq_policy; + this->my_host = my_host; + this->other_host = other_host; + this->dpd_delay = dpd_delay; + this->reauth = reauth; + this->keyingtries = keyingtries; + this->hard_lifetime = hard_lifetime; + this->soft_lifetime = soft_lifetime; + this->jitter = jitter; + + this->proposals = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/config/connections/connection.h b/src/charon/config/connections/connection.h new file mode 100644 index 000000000..d0788876f --- /dev/null +++ b/src/charon/config/connections/connection.h @@ -0,0 +1,292 @@ +/** + * @file connection.h + * + * @brief Interface of connection_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +typedef enum cert_policy_t cert_policy_t; +typedef struct connection_t connection_t; + +#include +#include +#include +#include +#include +#include + + +/** + * Certificate sending policy. This is also used for certificate + * requests when using this definition for the other peer. If + * it is CERT_NEVER_SEND, a certreq is omitted, otherwise its + * included. + * + * @ingroup config + * + * @warning These definitions must be the same as in pluto/starter, + * as they are sent over the stroke socket. + */ +enum cert_policy_t { + /** always send certificates, even when not requested */ + CERT_ALWAYS_SEND = 0, + /** send certificate upon cert request */ + CERT_SEND_IF_ASKED = 1, + /** never send a certificate, even when requested */ + CERT_NEVER_SEND = 2, +}; + +/** + * enum strings for cert_policy_t + * + * @ingroup config + */ +extern enum_name_t *cert_policy_names; + +/** + * @brief A connection_t defines the rules to set up an IKE_SA. + * + * @b Constructors: + * - connection_create() + * + * @ingroup config + */ +struct connection_t { + + /** + * @brief Get my address as host_t object. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as host_t object + */ + host_t *(*get_my_host) (connection_t *this); + + /** + * @brief Get others address as host_t object. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as host_t object + */ + host_t *(*get_other_host) (connection_t *this); + + /** + * @brief Returns a list of all supported proposals. + * + * Returned list and its proposals must be destroyed after usage. + * + * @param this calling object + * @return list containing all the proposals + */ + linked_list_t *(*get_proposals) (connection_t *this); + + /** + * @brief Adds a proposal to the list. + * + * The first added proposal has the highest priority, the last + * added the lowest. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (connection_t *this, proposal_t *proposal); + + /** + * @brief Select a proposed from suggested proposals. + * + * Returned proposal must be destroyed after usage. + * + * @param this calling object + * @param proposals list of proposals to select from + * @return selected proposal, or NULL if none matches. + */ + proposal_t *(*select_proposal) (connection_t *this, linked_list_t *proposals); + + /** + * @brief Get the DPD check interval. + * + * @param this calling object + * @return dpd_delay in seconds + */ + u_int32_t (*get_dpd_delay) (connection_t *this); + + /** + * @brief Should a full reauthentication be done instead of rekeying? + * + * @param this calling object + * @return TRUE to use full reauthentication + */ + bool (*get_reauth) (connection_t *this); + + /** + * @brief Get the max number of retransmission sequences. + * + * @param this calling object + * @return max number of retransmission sequences + */ + u_int32_t (*get_keyingtries) (connection_t *this); + + /** + * @brief Get the connection name. + * + * Name must not be freed, since it points to + * internal data. + * + * @param this calling object + * @return name of the connection + */ + char* (*get_name) (connection_t *this); + + /** + * @brief Check if the connection is marked as an IKEv2 connection. + * + * Since all connections (IKEv1+2) are loaded, but charon handles + * only those marked with IKEv2, this flag can tell us if we must + * ignore a connection on initiaton. Then pluto will do it for us. + * + * @param this calling object + * @return - TRUE, if this is an IKEv2 connection + */ + bool (*is_ikev2) (connection_t *this); + + /** + * @brief Should be sent a certificate request for this connection? + * + * A certificate request contains serials of our trusted CA certificates. + * This flag says if such a request is sent on connection setup to + * the peer. It should be omitted when CERT_SEND_NEVER, sended otherwise. + * + * @param this calling object + * @return certificate request sending policy + */ + cert_policy_t (*get_certreq_policy) (connection_t *this); + + /** + * @brief Should be sent a certificate for this connection? + * + * Return the policy used to send the certificate. + * + * @param this calling object + * @return certificate sending policy + */ + cert_policy_t (*get_cert_policy) (connection_t *this); + + /** + * @brief Get the DH group to use for connection initialization. + * + * @param this calling object + * @return dh group to use for initialization + */ + diffie_hellman_group_t (*get_dh_group) (connection_t *this); + + /** + * @brief Check if a suggested dh group is acceptable. + * + * If we guess a wrong DH group for IKE_SA_INIT, the other + * peer will send us a offer. But is this acceptable for us? + * + * @param this calling object + * @return TRUE if group acceptable + */ + bool (*check_dh_group) (connection_t *this, diffie_hellman_group_t dh_group); + + /** + * @brief Get the lifetime of a connection, before IKE_SA rekeying starts. + * + * A call to this function automatically adds a jitter to + * avoid simultanous rekeying. + * + * @param this calling object + * @return lifetime in seconds + */ + u_int32_t (*get_soft_lifetime) (connection_t *this); + + /** + * @brief Get the lifetime of a connection, before IKE_SA gets deleted. + * + * @param this calling object + * @return lifetime in seconds + */ + u_int32_t (*get_hard_lifetime) (connection_t *this); + + /** + * @brief Get a new reference to this connection. + * + * Get a new reference to this connection by increasing + * it's internal reference counter. + * Do not call get_ref or any other function until you + * already have a reference. Otherwise the object may get + * destroyed while calling get_ref(), + * + * @param this calling object + */ + void (*get_ref) (connection_t *this); + + /** + * @brief Destroys a connection_t object. + * + * Decrements the internal reference counter and + * destroys the connection when it reaches zero. + * + * @param this calling object + */ + void (*destroy) (connection_t *this); +}; + +/** + * @brief Creates a connection_t object. + * + * Supplied hosts become owned by connection, so + * do not modify or destroy them after a call to + * connection_create(). Name gets cloned internally. + * The retrasmit sequence number says how fast we give up when the peer + * does not respond. A high value may bridge-over temporary connection + * problems, a small value can detect dead peers faster. + * + * @param name connection identifier + * @param ikev2 TRUE if this is an IKEv2 connection + * @param cert_policy certificate send policy + * @param cert_req_policy certificate request send policy + * @param my_host host_t representing local address + * @param other_host host_t representing remote address + * @param dpd_delay interval of DPD liveness checks + * @param reauth use full reauthentication instead of rekeying + * @param keyingtries number of retransmit sequences to use + * @param hard_lifetime lifetime before deleting an IKE_SA + * @param soft_lifetime lifetime before rekeying an IKE_SA + * @param jitter range of randomization time + * @return connection_t object. + * + * @ingroup config + */ +connection_t * connection_create(char *name, bool ikev2, + cert_policy_t cert_pol, cert_policy_t req_pol, + host_t *my_host, host_t *other_host, + u_int32_t dpd_delay, bool reauth, + u_int32_t keyingtries, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter); + +#endif /* CONNECTION_H_ */ diff --git a/src/charon/config/connections/connection_store.h b/src/charon/config/connections/connection_store.h new file mode 100755 index 000000000..70f209d3b --- /dev/null +++ b/src/charon/config/connections/connection_store.h @@ -0,0 +1,118 @@ +/** + * @file connection_store.h + * + * @brief Interface connection_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CONNECTION_STORE_H_ +#define CONNECTION_STORE_H_ + +typedef struct connection_store_t connection_store_t; + +#include +#include +#include + +/** + * @brief The interface for a store of connection_t's. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct connection_store_t { + + /** + * @brief Returns a connection definition identified by two hosts. + * + * This call is usefull to get a connection identified by addresses. + * It may be used after kernel request for traffic protection. + * The returned connection gets created/cloned and therefore must + * be destroyed after usage. + * + * @param this calling object + * @param my_id own address of connection + * @param other_id others address of connection + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_hosts)(connection_store_t *this, + host_t *my_host, host_t *other_host); + + /** + * @brief Returns a connection identified by its name. + * + * This call is usefull to get a connection identified its + * name, as on an connection setup. + * + * @param this calling object + * @param name name of the connection to get + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_name) (connection_store_t *this, char *name); + + /** + * @brief Add a connection to the store. + * + * After a successful call, the connection is owned by the store and may + * not be manipulated nor destroyed. + * + * @param this calling object + * @param connection connection to add + * @return + * - SUCCESS, or + * - FAILED + */ + status_t (*add_connection) (connection_store_t *this, connection_t *connection); + + /** + * @brief Delete a connection from the store. + * + * Remove a connection from the connection store, identified + * by the connections name. + * + * @param this calling object + * @param name name of the connection to delete + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*delete_connection) (connection_store_t *this, char *name); + + /** + * @brief Get an iterator for the stored connections. + * + * @param this calling object + * @return iterator over all stored connections + */ + iterator_t* (*create_iterator) (connection_store_t *this); + + /** + * @brief Destroys a connection_store_t object. + * + * @param this calling object + */ + void (*destroy) (connection_store_t *this); +}; + +#endif /* CONNECTION_STORE_H_ */ diff --git a/src/charon/config/connections/local_connection_store.c b/src/charon/config/connections/local_connection_store.c new file mode 100644 index 000000000..df4ec230a --- /dev/null +++ b/src/charon/config/connections/local_connection_store.c @@ -0,0 +1,237 @@ +/** + * @file local_connection_store.c + * + * @brief Implementation of local_connection_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "local_connection_store.h" + +#include +#include + + +typedef struct private_local_connection_store_t private_local_connection_store_t; + +/** + * Private data of an local_connection_store_t object + */ +struct private_local_connection_store_t { + + /** + * Public part + */ + local_connection_store_t public; + + /** + * stored connection + */ + linked_list_t *connections; + + /** + * Mutex to exclusivly access connection list + */ + pthread_mutex_t mutex; +}; + + +/** + * Implementation of connection_store_t.get_connection_by_hosts. + */ +static connection_t *get_connection_by_hosts(private_local_connection_store_t *this, host_t *my_host, host_t *other_host) +{ + typedef enum { + PRIO_UNDEFINED= 0x00, + PRIO_ADDR_ANY= 0x01, + PRIO_ADDR_MATCH= 0x02 + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + + iterator_t *iterator; + connection_t *candidate; + connection_t *found = NULL; + + DBG2(DBG_CFG, "looking for connection for host pair %H...%H", + my_host, other_host); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + /* determine closest matching connection */ + while (iterator->iterate(iterator, (void**)&candidate)) + { + host_t *candidate_my_host; + host_t *candidate_other_host; + + candidate_my_host = candidate->get_my_host(candidate); + candidate_other_host = candidate->get_other_host(candidate); + + /* my_host addresses must match*/ + if (my_host->ip_equals(my_host, candidate_my_host)) + { + prio_t prio = PRIO_UNDEFINED; + + /* exact match of peer host address or wildcard address? */ + if (other_host->ip_equals(other_host, candidate_other_host)) + { + prio |= PRIO_ADDR_MATCH; + } + else if (candidate_other_host->is_anyaddr(candidate_other_host)) + { + prio |= PRIO_ADDR_ANY; + } + + DBG2(DBG_CFG, "candidate connection \"%s\": %H...%H (prio=%d)", + candidate->get_name(candidate), + candidate_my_host, candidate_other_host, prio); + + if (prio > best_prio) + { + found = candidate; + best_prio = prio; + } + } + } + iterator->destroy(iterator); + + if (found) + { + DBG2(DBG_CFG, "found matching connection \"%s\": %H...%H (prio=%d)", + found->get_name(found), found->get_my_host(found), + found->get_other_host(found), best_prio); + + /* give out a new reference to it */ + found->get_ref(found); + } + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implementation of connection_store_t.get_connection_by_name. + */ +static connection_t *get_connection_by_name(private_local_connection_store_t *this, char *name) +{ + iterator_t *iterator; + connection_t *current, *found = NULL; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (strcmp(name, current->get_name(current)) == 0) + { + found = current; + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + if (found) + { + /* get a new reference for it */ + found->get_ref(found); + } + return found; +} + +/** + * Implementation of connection_store_t.delete_connection. + */ +static status_t delete_connection(private_local_connection_store_t *this, char *name) +{ + iterator_t *iterator; + connection_t *current; + bool found = FALSE; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + /* remove connection from list, and destroy it */ + iterator->remove(iterator); + current->destroy(current); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + if (found) + { + return SUCCESS; + } + return NOT_FOUND; +} + +/** + * Implementation of connection_store_t.add_connection. + */ +static status_t add_connection(private_local_connection_store_t *this, connection_t *connection) +{ + pthread_mutex_lock(&(this->mutex)); + this->connections->insert_last(this->connections, connection); + pthread_mutex_unlock(&(this->mutex)); + return SUCCESS; +} + +/** + * Implementation of connection_store_t.create_iterator. + */ +static iterator_t* create_iterator(private_local_connection_store_t *this) +{ + return this->connections->create_iterator_locked(this->connections, + &this->mutex); +} + +/** + * Implementation of connection_store_t.destroy. + */ +static void destroy (private_local_connection_store_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + this->connections->destroy_offset(this->connections, offsetof(connection_t, destroy)); + pthread_mutex_unlock(&(this->mutex)); + free(this); +} + +/** + * Described in header. + */ +local_connection_store_t * local_connection_store_create(void) +{ + private_local_connection_store_t *this = malloc_thing(private_local_connection_store_t); + + this->public.connection_store.get_connection_by_hosts = (connection_t*(*)(connection_store_t*,host_t*,host_t*))get_connection_by_hosts; + this->public.connection_store.get_connection_by_name = (connection_t*(*)(connection_store_t*,char*))get_connection_by_name; + this->public.connection_store.delete_connection = (status_t(*)(connection_store_t*,char*))delete_connection; + this->public.connection_store.add_connection = (status_t(*)(connection_store_t*,connection_t*))add_connection; + this->public.connection_store.create_iterator = (iterator_t*(*)(connection_store_t*))create_iterator; + this->public.connection_store.destroy = (void(*)(connection_store_t*))destroy; + + /* private variables */ + this->connections = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + + return (&this->public); +} diff --git a/src/charon/config/connections/local_connection_store.h b/src/charon/config/connections/local_connection_store.h new file mode 100644 index 000000000..e78ed809a --- /dev/null +++ b/src/charon/config/connections/local_connection_store.h @@ -0,0 +1,62 @@ +/** + * @file local_connection_store.h + * + * @brief Interface of local_connection_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef LOCAL_CONNECTION_H_ +#define LOCAL_CONNECTION_H_ + +typedef struct local_connection_store_t local_connection_store_t; + +#include +#include + +/** + * @brief A connection_store_t implementation using a simple connection list. + * + * The local_connection_store_t class implements the connection_store_t interface + * as simple as possible. connection_t's are stored in an in-memory list. + * + * @b Constructors: + * - local_connection_store_create() + * + * @todo Make thread-save first + * @todo Add remove_connection method + * + * @ingroup config + */ +struct local_connection_store_t { + + /** + * Implements connection_store_t interface + */ + connection_store_t connection_store; +}; + +/** + * @brief Creates a local_connection_store_t instance. + * + * @return connection store instance. + * + * @ingroup config + */ +local_connection_store_t * local_connection_store_create(void); + +#endif /* LOCAL_CONNECTION_H_ */ diff --git a/src/charon/config/credentials/local_credential_store.c b/src/charon/config/credentials/local_credential_store.c new file mode 100644 index 000000000..b7b71b9e7 --- /dev/null +++ b/src/charon/config/credentials/local_credential_store.c @@ -0,0 +1,1363 @@ +/** + * @file local_credential_store.c + * + * @brief Implementation of local_credential_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "local_credential_store.h" + +#define PATH_BUF 256 +#define MAX_CA_PATH_LEN 7 + +typedef struct shared_key_t shared_key_t; + +/** + * Private date of a shared_key_t object + */ +struct shared_key_t { + + /** + * shared secret + */ + chunk_t secret; + + /** + * list of peer IDs + */ + linked_list_t *peers; +}; + + +/** + * Implementation of shared_key_t.destroy. + */ +static void shared_key_destroy(shared_key_t *this) +{ + this->peers->destroy_offset(this->peers, offsetof(identification_t, destroy)); + chunk_free(&this->secret); + free(this); +} + +/** + * @brief Creates a shared_key_t object. + * + * @param shared_key shared key value + * @return shared_key_t object + * + * @ingroup config + */ +static shared_key_t *shared_key_create(chunk_t secret) +{ + shared_key_t *this = malloc_thing(shared_key_t); + + /* private data */ + this->secret = chunk_clone(secret); + this->peers = linked_list_create(); + + return (this); +} + +/* ------------------------------------------------------------------------ * + * the ca_info_t object as a central control element + ++--------------------------------------------------------+ +| local_credential_store_t | ++--------------------------------------------------------+ + | | ++---------------------------+ +-------------------------+ +| linked_list_t *auth_certs | | linked_list_t *ca_infos | ++---------------------------+ +-------------------------+ + | | + | +------------------------- + + | | ca_info_t | + | +--------------------------+ ++---------------+ | char *name | +| x509_t |<--| x509_t *cacert | +----------------------+ ++---------------+ | linked_list_t *certinfos |-->| certinfo_t | +| chunk_t keyid | | linked_list_t *ocspuris | +----------------------+ ++---------------+ | crl_t *crl | | chunk_t serialNumber | + | | linked_list_t *crluris | | cert_status_t status | + | | pthread_mutex_t mutex | | time_t thisUpdate | ++---------------+ +--------------------------+ | time_t nextUpdate | +| x509_t | | | bool once | ++---------------+ | +----------------------+ +| chunk_t keyid | | | ++---------------+ +------------------------- + +----------------------+ + | | ca_info_t | | certinfo_t | + | +--------------------------+ +----------------------+ ++---------------+ | char *name | | chunk_t serialNumber | +| x509_t |<--| x509_t *cacert | | cert_status_t status | ++---------------+ | linked_list_t *certinfos | | time_t thisUpdate | +| chunk_t keyid | | linked_list_t *ocspuris | | time_t nextUpdate | ++---------------+ | crl_t *crl | | bool once | + | | linked_list_t *crluris | +----------------------+ + | | pthread_mutex_t mutex; | | + | +--------------------------+ + | | + + * ------------------------------------------------------------------------ */ + +typedef struct private_local_credential_store_t private_local_credential_store_t; + +/** + * Private data of an local_credential_store_t object + */ +struct private_local_credential_store_t { + + /** + * Public part + */ + local_credential_store_t public; + + /** + * list of shared keys + */ + linked_list_t *shared_keys; + + /** + * list of EAP keys + */ + linked_list_t *eap_keys; + + /** + * list of key_entry_t's with private keys + */ + linked_list_t *private_keys; + + /** + * list of X.509 certificates with public keys + */ + linked_list_t *certs; + + /** + * list of X.509 authority certificates with public keys + */ + linked_list_t *auth_certs; + + /** + * list of X.509 CA information records + */ + linked_list_t *ca_infos; + + /** + * enforce strict crl policy + */ + bool strict; +}; + + +/** + * Get a key from a list with shared_key_t's + */ +static status_t get_key(linked_list_t *keys, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + typedef enum { + PRIO_UNDEFINED= 0x00, + PRIO_ANY_MATCH= 0x01, + PRIO_MY_MATCH= 0x02, + PRIO_OTHER_MATCH= 0x04, + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + chunk_t found = chunk_empty; + shared_key_t *shared_key; + + iterator_t *iterator = keys->create_iterator(keys, TRUE); + + while (iterator->iterate(iterator, (void**)&shared_key)) + { + iterator_t *peer_iterator; + identification_t *peer_id; + prio_t prio = PRIO_UNDEFINED; + + peer_iterator = shared_key->peers->create_iterator(shared_key->peers, TRUE); + + if (peer_iterator->get_count(peer_iterator) == 0) + { + /* this is a wildcard shared key */ + prio = PRIO_ANY_MATCH; + } + else + { + while (peer_iterator->iterate(peer_iterator, (void**)&peer_id)) + { + if (my_id->equals(my_id, peer_id)) + { + prio |= PRIO_MY_MATCH; + } + if (other_id->equals(other_id, peer_id)) + { + prio |= PRIO_OTHER_MATCH; + } + } + } + peer_iterator->destroy(peer_iterator); + + if (prio > best_prio) + { + best_prio = prio; + found = shared_key->secret; + } + } + iterator->destroy(iterator); + + if (best_prio == PRIO_UNDEFINED) + { + return NOT_FOUND; + } + else + { + *secret = chunk_clone(found); + return SUCCESS; + } +} + + +/** + * Implementation of local_credential_store_t.get_shared_key. + */ +static status_t get_shared_key(private_local_credential_store_t *this, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + return get_key(this->shared_keys, my_id, other_id, secret); +} + +/** + * Implementation of local_credential_store_t.get_eap_key. + */ +static status_t get_eap_key(private_local_credential_store_t *this, + identification_t *my_id, + identification_t *other_id, chunk_t *secret) +{ + return get_key(this->eap_keys, my_id, other_id, secret); +} + +/** + * Implementation of credential_store_t.get_certificate. + */ +static x509_t* get_certificate(private_local_credential_store_t *this, + identification_t *id) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->certs->create_iterator(this->certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (id->equals(id, current_cert->get_subject(current_cert)) || + current_cert->equals_subjectAltName(current_cert, id)) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of local_credential_store_t.get_rsa_public_key. + */ +static rsa_public_key_t *get_rsa_public_key(private_local_credential_store_t *this, + identification_t *id) +{ + x509_t *cert = get_certificate(this, id); + + return (cert == NULL)? NULL:cert->get_public_key(cert); +} + +/** + * Implementation of local_credential_store_t.get_trusted_public_key. + */ +static rsa_public_key_t *get_trusted_public_key(private_local_credential_store_t *this, + identification_t *id) +{ + cert_status_t status; + err_t ugh; + + x509_t *cert = get_certificate(this, id); + + if (cert == NULL) + return NULL; + + ugh = cert->is_valid(cert, NULL); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return NULL; + } + + status = cert->get_status(cert); + if (status == CERT_REVOKED || status == CERT_UNTRUSTED || (this->strict && status != CERT_GOOD)) + { + DBG1(DBG_CFG, "certificate status: %N", cert_status_names, status); + return NULL; + } + if (status == CERT_GOOD && cert->get_until(cert) < time(NULL)) + { + DBG1(DBG_CFG, "certificate is good but crl is stale"); + return NULL; + } + + return cert->get_public_key(cert); +} + +/** + * Implementation of local_credential_store_t.get_rsa_private_key. + */ +static rsa_private_key_t *get_rsa_private_key(private_local_credential_store_t *this, + rsa_public_key_t *pubkey) +{ + rsa_private_key_t *found = NULL, *current; + + iterator_t *iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->belongs_to(current, pubkey)) + { + found = current->clone(current); + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of local_credential_store_t.has_rsa_private_key. + */ +static bool has_rsa_private_key(private_local_credential_store_t *this, rsa_public_key_t *pubkey) +{ + bool found = FALSE; + rsa_private_key_t *current; + + iterator_t *iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->belongs_to(current, pubkey)) + { + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of credential_store_t.get_auth_certificate. + */ +static x509_t* get_auth_certificate(private_local_credential_store_t *this, + u_int auth_flags, + identification_t *id) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->auth_certs->create_iterator(this->auth_certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (current_cert->has_authority_flag(current_cert, auth_flags) + && id->equals(id, current_cert->get_subject(current_cert))) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of credential_store_t.get_ca_certificate_by_keyid. + */ +static x509_t* get_ca_certificate_by_keyid(private_local_credential_store_t *this, + chunk_t keyid) +{ + x509_t *found = NULL; + x509_t *current_cert; + + iterator_t *iterator = this->auth_certs->create_iterator(this->auth_certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + rsa_public_key_t *pubkey = current_cert->get_public_key(current_cert); + + if (current_cert->has_authority_flag(current_cert, AUTH_CA) + && chunk_equals(keyid, pubkey->get_keyid(pubkey))) + { + found = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of credential_store_t.get_issuer. + */ +static ca_info_t* get_issuer(private_local_credential_store_t *this, const x509_t *cert) +{ + ca_info_t *found = NULL; + ca_info_t *ca_info; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->is_cert_issuer(ca_info, cert)) + { + found = ca_info; + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Find an exact copy of a certificate in a linked list + */ +static x509_t* find_certificate(linked_list_t *certs, x509_t *cert) +{ + x509_t *found_cert = NULL, *current_cert; + + iterator_t *iterator = certs->create_iterator(certs, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_cert)) + { + if (cert->equals(cert, current_cert)) + { + found_cert = current_cert; + break; + } + } + iterator->destroy(iterator); + + return found_cert; +} + +/** + * Adds crl and ocsp uris to the corresponding issuer info record + */ +static void add_uris(ca_info_t *issuer, x509_t *cert) +{ + iterator_t *iterator; + identification_t *uri; + + /* add any crl distribution points to the issuer ca info record */ + iterator = cert->create_crluri_iterator(cert); + + while (iterator->iterate(iterator, (void**)&uri)) + { + issuer->add_crluri(issuer, uri->get_encoding(uri)); + } + iterator->destroy(iterator); + + /* add any ocsp access points to the issuer ca info record */ + iterator = cert->create_ocspuri_iterator(cert); + + while (iterator->iterate(iterator, (void**)&uri)) + { + issuer->add_ocspuri(issuer, uri->get_encoding(uri)); + } + iterator->destroy(iterator); +} + +/** + * Implementation of credential_store_t.is_trusted + */ +static bool is_trusted(private_local_credential_store_t *this, x509_t *cert) +{ + int pathlen; + time_t until = UNDEFINED_TIME; + x509_t *cert_to_be_trusted = cert; + + DBG2(DBG_CFG, "establishing trust in certificate:"); + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + err_t ugh = NULL; + ca_info_t *issuer; + x509_t *issuer_cert; + rsa_public_key_t *issuer_public_key; + bool valid_signature; + + DBG2(DBG_CFG, "subject: '%D'", cert->get_subject(cert)); + DBG2(DBG_CFG, "issuer: '%D'", cert->get_issuer(cert)); + + ugh = cert->is_valid(cert, &until); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return FALSE; + } + DBG2(DBG_CFG, "certificate is valid"); + + issuer = get_issuer(this, cert); + if (issuer == NULL) + { + DBG1(DBG_CFG, "issuer not found"); + return FALSE; + } + DBG2(DBG_CFG, "issuer found"); + + issuer_cert = issuer->get_certificate(issuer); + issuer_public_key = issuer_cert->get_public_key(issuer_cert); + valid_signature = cert->verify(cert, issuer_public_key); + + if (!valid_signature) + { + DBG1(DBG_CFG, "certificate signature is invalid"); + return FALSE; + } + DBG2(DBG_CFG, "certificate signature is valid"); + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && cert->is_self_signed(cert)) + { + DBG2(DBG_CFG, "reached self-signed root ca"); + cert_to_be_trusted->set_until(cert_to_be_trusted, until); + cert_to_be_trusted->set_status(cert_to_be_trusted, CERT_GOOD); + return TRUE; + } + else + { + /* go up one step in the trust chain */ + cert = issuer_cert; + } + } + DBG1(DBG_CFG, "maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + return FALSE; +} + +/** + * Implementation of credential_store_t.verify. + */ +static bool verify(private_local_credential_store_t *this, x509_t *cert, bool *found) +{ + int pathlen; + time_t until = UNDEFINED_TIME; + + x509_t *end_cert = cert; + x509_t *cert_copy = find_certificate(this->certs, end_cert); + + DBG2(DBG_CFG, "verifying end entity certificate:"); + + *found = (cert_copy != NULL); + if (*found) + { + DBG2(DBG_CFG, + "end entitity certificate is already in credential store"); + } + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + err_t ugh = NULL; + ca_info_t *issuer; + x509_t *issuer_cert; + rsa_public_key_t *issuer_public_key; + bool valid_signature; + + DBG1(DBG_CFG, "subject: '%D'", cert->get_subject(cert)); + DBG1(DBG_CFG, "issuer: '%D'", cert->get_issuer(cert)); + + ugh = cert->is_valid(cert, &until); + if (ugh != NULL) + { + DBG1(DBG_CFG, "certificate %s", ugh); + return FALSE; + } + DBG2(DBG_CFG, "certificate is valid"); + + issuer = get_issuer(this, cert); + if (issuer == NULL) + { + DBG1(DBG_CFG, "issuer not found"); + return FALSE; + } + DBG2(DBG_CFG, "issuer found"); + + issuer_cert = issuer->get_certificate(issuer); + issuer_public_key = issuer_cert->get_public_key(issuer_cert); + valid_signature = cert->verify(cert, issuer_public_key); + + if (!valid_signature) + { + DBG1(DBG_CFG, "certificate signature is invalid"); + return FALSE; + } + DBG2(DBG_CFG, "certificate signature is valid"); + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && cert->is_self_signed(cert)) + { + DBG1(DBG_CFG, "reached self-signed root ca"); + + /* set the definite status and trust interval of the end entity certificate */ + end_cert->set_until(end_cert, until); + if (cert_copy) + { + cert_copy->set_status(cert_copy, end_cert->get_status(end_cert)); + cert_copy->set_until(cert_copy, until); + } + return TRUE; + } + else + { + time_t nextUpdate; + cert_status_t status; + certinfo_t *certinfo = certinfo_create(cert->get_serialNumber(cert)); + + certinfo->set_nextUpdate(certinfo, until); + + if (pathlen == 0) + { + /* add any crl and ocsp uris contained in the certificate under test */ + add_uris(issuer, cert); + } + + /* first check certificate revocation using ocsp */ + status = issuer->verify_by_ocsp(issuer, certinfo, &this->public.credential_store); + + /* if ocsp service is not available then fall back to crl */ + if ((status == CERT_UNDEFINED) || (status == CERT_UNKNOWN && this->strict)) + { + status = issuer->verify_by_crl(issuer, certinfo, CRL_DIR); + } + + nextUpdate = certinfo->get_nextUpdate(certinfo); + cert->set_status(cert, status); + + switch (status) + { + case CERT_GOOD: + /* set nextUpdate */ + cert->set_until(cert, nextUpdate); + + /* if status information is stale */ + if (this->strict && nextUpdate < time(NULL)) + { + DBG2(DBG_CFG, "certificate is good but status is stale"); + certinfo->destroy(certinfo); + return FALSE; + } + DBG1(DBG_CFG, "certificate is good"); + + /* with strict crl policy the public key must have the same + * lifetime as the validity of the ocsp status or crl lifetime + */ + if (this->strict && nextUpdate < until) + until = nextUpdate; + break; + case CERT_REVOKED: + { + time_t revocationTime = certinfo->get_revocationTime(certinfo); + DBG1(DBG_CFG, + "certificate was revoked on %T, reason: %N", + &revocationTime, crl_reason_names, + certinfo->get_revocationReason(certinfo)); + + /* set revocationTime */ + cert->set_until(cert, revocationTime); + + /* update status of end certificate in the credential store */ + if (cert_copy) + { + if (pathlen > 0) + { + cert_copy->set_status(cert_copy, CERT_UNTRUSTED); + } + else + { + cert_copy->set_status(cert_copy, CERT_REVOKED); + cert_copy->set_until(cert_copy, + certinfo->get_revocationTime(certinfo)); + } + } + certinfo->destroy(certinfo); + return FALSE; + } + case CERT_UNKNOWN: + case CERT_UNDEFINED: + default: + DBG1(DBG_CFG, "certificate status unknown"); + if (this->strict) + { + /* update status of end certificate in the credential store */ + if (cert_copy) + { + cert_copy->set_status(cert_copy, CERT_UNTRUSTED); + } + certinfo->destroy(certinfo); + return FALSE; + } + break; + } + certinfo->destroy(certinfo); + } + /* go up one step in the trust chain */ + cert = issuer_cert; + } + DBG1(DBG_CFG, "maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + return FALSE; +} + +/** + * Add a unique certificate to a linked list + */ +static x509_t* add_certificate(linked_list_t *certs, x509_t *cert) +{ + x509_t *found_cert = find_certificate(certs, cert); + + if (found_cert) + { + /* add the authority flags */ + found_cert->add_authority_flags(found_cert, cert->get_authority_flags(cert)); + + cert->destroy(cert); + return found_cert; + } + else + { + certs->insert_last(certs, (void*)cert); + return cert; + } +} + +/** + * Add a unique ca info record to a linked list + */ +static void add_ca_info(private_local_credential_store_t *this, ca_info_t *ca_info) +{ + ca_info_t *current_ca_info; + ca_info_t *found_ca_info = NULL; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_ca_info)) + { + if (current_ca_info->equals(current_ca_info, ca_info)) + { + found_ca_info = current_ca_info; + break; + } + } + iterator->destroy(iterator); + + if (found_ca_info) + { + current_ca_info->add_info(current_ca_info, ca_info); + ca_info->destroy(ca_info); + } + else + { + this->ca_infos->insert_last(this->ca_infos, (void*)ca_info); + } +} + +/** + * Release ca info record of a given name + */ +static status_t release_ca_info(private_local_credential_store_t *this, const char *name) +{ + status_t status = NOT_FOUND; + ca_info_t *ca_info; + + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->equals_name_release_info(ca_info, name)) + { + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implements local_credential_store_t.add_end_certificate + */ +static x509_t* add_end_certificate(private_local_credential_store_t *this, x509_t *cert) +{ + x509_t *ret_cert = add_certificate(this->certs, cert); + + /* add crl and ocsp uris the first time the certificate is added */ + if (ret_cert == cert) + { + ca_info_t *issuer = get_issuer(this, cert); + + if (issuer) + { + add_uris(issuer, cert); + } + } + return ret_cert; +} + +/** + * Implements local_credential_store_t.add_auth_certificate + */ +static x509_t* add_auth_certificate(private_local_credential_store_t *this, x509_t *cert, u_int auth_flags) +{ + cert->add_authority_flags(cert, auth_flags); + return add_certificate(this->auth_certs, cert); +} + +/** + * Implements local_credential_store_t.create_cert_iterator + */ +static iterator_t* create_cert_iterator(private_local_credential_store_t *this) +{ + return this->certs->create_iterator(this->certs, TRUE); +} + +/** + * Implements local_credential_store_t.create_cacert_iterator + */ +static iterator_t* create_auth_cert_iterator(private_local_credential_store_t *this) +{ + return this->auth_certs->create_iterator(this->auth_certs, TRUE); +} + +/** + * Implements local_credential_store_t.create_cainfo_iterator + */ +static iterator_t* create_cainfo_iterator(private_local_credential_store_t *this) +{ + return this->ca_infos->create_iterator(this->ca_infos, TRUE); +} + +/** + * Implements local_credential_store_t.load_auth_certificates + */ +static void load_auth_certificates(private_local_credential_store_t *this, + u_int auth_flag, + const char* label, + const char* path) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + + DBG1(DBG_CFG, "loading %s certificates from '%s/'", label, path); + + dir = opendir(path); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening %s certs directory %s'", label, path); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + char file[PATH_BUF]; + + snprintf(file, sizeof(file), "%s/%s", path, entry->d_name); + + if (stat(file, &stb) == -1) + { + continue; + } + /* try to parse all regular files */ + if (stb.st_mode & S_IFREG) + { + x509_t *cert = x509_create_from_file(file, label); + + if (cert) + { + err_t ugh = cert->is_valid(cert, NULL); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "warning: %s certificate %s", label, ugh); + } + + if (auth_flag == AUTH_CA && !cert->is_ca(cert)) + { + DBG1(DBG_CFG, " CA basic constraints flag not set, cert discarded"); + cert->destroy(cert); + } + else + { + x509_t *ret_cert; + + cert->add_authority_flags(cert, auth_flag); + + ret_cert = add_certificate(this->auth_certs, cert); + + if (auth_flag == AUTH_CA && ret_cert == cert) + { + ca_info_t *ca_info = ca_info_create(NULL, cert); + + add_ca_info(this, ca_info); + } + } + } + } + } + closedir(dir); +} + +/** + * Implements local_credential_store_t.load_ca_certificates + */ +static void load_ca_certificates(private_local_credential_store_t *this) +{ + load_auth_certificates(this, AUTH_CA, "ca", CA_CERTIFICATE_DIR); + + /* add any crl and ocsp uris found in the ca certificates to the + * corresponding issuer info record. We can do this only after all + * ca certificates have been loaded and the ca hierarchy is known. + */ + { + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + ca_info_t *ca_info; + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + x509_t *cacert = ca_info->get_certificate(ca_info); + ca_info_t *issuer = get_issuer(this, cacert); + + if (issuer) + { + add_uris(issuer, cacert); + } + } + iterator->destroy(iterator); + } +} + +/** + * Implements local_credential_store_t.load_ocsp_certificates + */ +static void load_ocsp_certificates(private_local_credential_store_t *this) +{ + load_auth_certificates(this, AUTH_OCSP, "ocsp", OCSP_CERTIFICATE_DIR); +} + +/** + * Add the latest crl to the issuing ca + */ +static void add_crl(private_local_credential_store_t *this, crl_t *crl, const char *path) +{ + iterator_t *iterator = this->ca_infos->create_iterator(this->ca_infos, TRUE); + ca_info_t *ca_info; + bool found = FALSE; + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + if (ca_info->is_crl_issuer(ca_info, crl)) + { + char buffer[BUF_LEN]; + chunk_t uri = { buffer, 7 + strlen(path) }; + + ca_info->add_crl(ca_info, crl); + if (uri.len < BUF_LEN) + { + snprintf(buffer, BUF_LEN, "file://%s", path); + ca_info->add_crluri(ca_info, uri); + } + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (!found) + { + crl->destroy(crl); + DBG2(DBG_CFG, " no issuing ca found for this crl - discarded"); + } +} + +/** + * Implements local_credential_store_t.load_crls + */ +static void load_crls(private_local_credential_store_t *this) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + crl_t *crl; + + DBG1(DBG_CFG, "loading crls from '%s/'", CRL_DIR); + + dir = opendir(CRL_DIR); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening crl directory %s'", CRL_DIR); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + char file[PATH_BUF]; + + snprintf(file, sizeof(file), "%s/%s", CRL_DIR, entry->d_name); + + if (stat(file, &stb) == -1) + { + continue; + } + /* try to parse all regular files */ + if (stb.st_mode & S_IFREG) + { + crl = crl_create_from_file(file); + if (crl) + { + DBG1(DBG_CFG, " crl is %s", crl->is_valid(crl)? "valid":"stale"); + add_crl(this, crl, file); + } + } + } + closedir(dir); +} + +/** + * Convert a string of characters into a binary secret + * A string between single or double quotes is treated as ASCII characters + * A string prepended by 0x is treated as HEX and prepended by 0s as Base64 + */ +static err_t extract_secret(chunk_t *secret, chunk_t *line) +{ + chunk_t raw_secret; + char delimiter = ' '; + bool quotes = FALSE; + + if (!eat_whitespace(line)) + { + return "missing secret"; + } + + if (*line->ptr == '\'' || *line->ptr == '"') + { + quotes = TRUE; + delimiter = *line->ptr; + line->ptr++; line->len--; + } + + if (!extract_token(&raw_secret, delimiter, line)) + { + if (delimiter == ' ') + { + raw_secret = *line; + } + else + { + return "missing second delimiter"; + } + } + + if (quotes) + { /* treat as an ASCII string */ + if (raw_secret.len > secret->len) + return "secret larger than buffer"; + memcpy(secret->ptr, raw_secret.ptr, raw_secret.len); + secret->len = raw_secret.len; + } + else + { /* convert from HEX or Base64 to binary */ + size_t len; + err_t ugh = ttodata(raw_secret.ptr, raw_secret.len, 0, secret->ptr, secret->len, &len); + + if (ugh != NULL) + return ugh; + if (len > secret->len) + return "secret larger than buffer"; + secret->len = len; + } + return NULL; +} + +/** + * Implements local_credential_store_t.load_secrets + */ +static void load_secrets(private_local_credential_store_t *this) +{ + FILE *fd = fopen(SECRETS_FILE, "r"); + + if (fd) + { + int bytes; + int line_nr = 0; + chunk_t chunk, src, line; + + DBG1(DBG_CFG, "loading secrets from \"%s\"", SECRETS_FILE); + + fseek(fd, 0, SEEK_END); + chunk.len = ftell(fd); + rewind(fd); + chunk.ptr = malloc(chunk.len); + bytes = fread(chunk.ptr, 1, chunk.len, fd); + fclose(fd); + + src = chunk; + + while (fetchline(&src, &line)) + { + chunk_t ids, token; + bool is_eap = FALSE; + + line_nr++; + + if (!eat_whitespace(&line)) + { + continue; + } + if (!extract_token(&ids, ':', &line)) + { + DBG1(DBG_CFG, "line %d: missing ':' separator", line_nr); + goto error; + } + /* NULL terminate the ids string by replacing the : separator */ + *(ids.ptr + ids.len) = '\0'; + + if (!eat_whitespace(&line) || !extract_token(&token, ' ', &line)) + { + DBG1(DBG_CFG, "line %d: missing token", line_nr); + goto error; + } + if (match("RSA", &token)) + { + char path[PATH_BUF]; + chunk_t filename; + + char buf[BUF_LEN]; + chunk_t secret = { buf, BUF_LEN }; + chunk_t *passphrase = NULL; + + rsa_private_key_t *key; + + err_t ugh = extract_value(&filename, &line); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: %s", line_nr, ugh); + goto error; + } + if (filename.len == 0) + { + DBG1(DBG_CFG, "line %d: empty filename", line_nr); + goto error; + } + if (*filename.ptr == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%.*s", filename.len, filename.ptr); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR, + filename.len, filename.ptr); + } + + /* check for optional passphrase */ + if (eat_whitespace(&line)) + { + ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh); + goto error; + } + if (secret.len > 0) + passphrase = &secret; + } + key = rsa_private_key_create_from_file(path, passphrase); + if (key) + { + this->private_keys->insert_last(this->private_keys, (void*)key); + } + } + else if ( match("PSK", &token) || + ((match("EAP", &token) || match("XAUTH", &token)) && (is_eap = TRUE))) + { + shared_key_t *shared_key; + + char buf[BUF_LEN]; + chunk_t secret = { buf, BUF_LEN }; + + err_t ugh = extract_secret(&secret, &line); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: malformed secret: %s", line_nr, ugh); + goto error; + } + + DBG1(DBG_CFG, " loading %s key for %s", + is_eap ? "EAP" : "shared", + ids.len > 0 ? (char*)ids.ptr : "%any"); + + DBG4(DBG_CFG, " secret:", secret); + + shared_key = shared_key_create(secret); + if (shared_key) + { + if (is_eap) + { + this->eap_keys->insert_last(this->eap_keys, (void*)shared_key); + } + else + { + this->shared_keys->insert_last(this->shared_keys, (void*)shared_key); + } + } + while (ids.len > 0) + { + chunk_t id; + identification_t *peer_id; + + ugh = extract_value(&id, &ids); + if (ugh != NULL) + { + DBG1(DBG_CFG, "line %d: %s", line_nr, ugh); + goto error; + } + if (id.len == 0) + { + continue; + } + + /* NULL terminate the ID string */ + *(id.ptr + id.len) = '\0'; + + peer_id = identification_create_from_string(id.ptr); + if (peer_id == NULL) + { + DBG1(DBG_CFG, "line %d: malformed ID: %s", line_nr, id.ptr); + goto error; + } + + if (peer_id->get_type(peer_id) == ID_ANY) + { + peer_id->destroy(peer_id); + continue; + } + shared_key->peers->insert_last(shared_key->peers, (void*)peer_id); + } + } + else if (match("PIN", &token)) + { + + } + else + { + DBG1(DBG_CFG, "line %d: token must be either " + "RSA, PSK, EAP, or PIN", line_nr, token.len); + goto error; + } + } +error: + free(chunk.ptr); + } + else + { + DBG1(DBG_CFG, "could not open file '%s'", SECRETS_FILE); + } +} + +/** + * Implementation of local_credential_store_t.destroy. + */ +static void destroy(private_local_credential_store_t *this) +{ + this->certs->destroy_offset(this->certs, offsetof(x509_t, destroy)); + this->auth_certs->destroy_offset(this->auth_certs, offsetof(x509_t, destroy)); + this->ca_infos->destroy_offset(this->ca_infos, offsetof(ca_info_t, destroy)); + this->private_keys->destroy_offset(this->private_keys, offsetof(rsa_private_key_t, destroy)); + this->shared_keys->destroy_function(this->shared_keys, (void*)shared_key_destroy); + this->eap_keys->destroy_function(this->eap_keys, (void*)shared_key_destroy); + free(this); +} + +/** + * Described in header. + */ +local_credential_store_t * local_credential_store_create(bool strict) +{ + private_local_credential_store_t *this = malloc_thing(private_local_credential_store_t); + + this->public.credential_store.get_shared_key = (status_t (*) (credential_store_t*,identification_t*,identification_t*,chunk_t*))get_shared_key; + this->public.credential_store.get_eap_key = (status_t (*) (credential_store_t*,identification_t*,identification_t*,chunk_t*))get_eap_key; + this->public.credential_store.get_rsa_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_rsa_public_key; + this->public.credential_store.get_rsa_private_key = (rsa_private_key_t* (*) (credential_store_t*,rsa_public_key_t*))get_rsa_private_key; + this->public.credential_store.has_rsa_private_key = (bool (*) (credential_store_t*,rsa_public_key_t*))has_rsa_private_key; + this->public.credential_store.get_trusted_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_trusted_public_key; + this->public.credential_store.get_certificate = (x509_t* (*) (credential_store_t*,identification_t*))get_certificate; + this->public.credential_store.get_auth_certificate = (x509_t* (*) (credential_store_t*,u_int,identification_t*))get_auth_certificate; + this->public.credential_store.get_ca_certificate_by_keyid = (x509_t* (*) (credential_store_t*,chunk_t))get_ca_certificate_by_keyid; + this->public.credential_store.get_issuer = (ca_info_t* (*) (credential_store_t*,const x509_t*))get_issuer; + this->public.credential_store.is_trusted = (bool (*) (credential_store_t*,x509_t*))is_trusted; + this->public.credential_store.verify = (bool (*) (credential_store_t*,x509_t*,bool*))verify; + this->public.credential_store.add_end_certificate = (x509_t* (*) (credential_store_t*,x509_t*))add_end_certificate; + this->public.credential_store.add_auth_certificate = (x509_t* (*) (credential_store_t*,x509_t*,u_int))add_auth_certificate; + this->public.credential_store.add_ca_info = (void (*) (credential_store_t*,ca_info_t*))add_ca_info; + this->public.credential_store.release_ca_info = (status_t (*) (credential_store_t*,const char*))release_ca_info; + this->public.credential_store.create_cert_iterator = (iterator_t* (*) (credential_store_t*))create_cert_iterator; + this->public.credential_store.create_auth_cert_iterator = (iterator_t* (*) (credential_store_t*))create_auth_cert_iterator; + this->public.credential_store.create_cainfo_iterator = (iterator_t* (*) (credential_store_t*))create_cainfo_iterator; + this->public.credential_store.load_ca_certificates = (void (*) (credential_store_t*))load_ca_certificates; + this->public.credential_store.load_ocsp_certificates = (void (*) (credential_store_t*))load_ocsp_certificates; + this->public.credential_store.load_crls = (void (*) (credential_store_t*))load_crls; + this->public.credential_store.load_secrets = (void (*) (credential_store_t*))load_secrets; + this->public.credential_store.destroy = (void (*) (credential_store_t*))destroy; + + /* private variables */ + this->shared_keys = linked_list_create(); + this->eap_keys = linked_list_create(); + this->private_keys = linked_list_create(); + this->certs = linked_list_create(); + this->auth_certs = linked_list_create(); + this->ca_infos = linked_list_create(); + this->strict = strict; + + return (&this->public); +} diff --git a/src/charon/config/credentials/local_credential_store.h b/src/charon/config/credentials/local_credential_store.h new file mode 100644 index 000000000..88a94d6f9 --- /dev/null +++ b/src/charon/config/credentials/local_credential_store.h @@ -0,0 +1,64 @@ +/** + * @file local_credential_store.h + * + * @brief Interface of local_credential_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef LOCAL_CREDENTIAL_H_ +#define LOCAL_CREDENTIAL_H_ + +typedef struct local_credential_store_t local_credential_store_t; + +#include +#include +#include + + +/** + * @brief A credential_store_t implementation using simple credentail lists. + * + * The local_credential_store_t class implements the credential_store_t interface + * as simple as possible. The credentials are stored in lists, and are loaded from + * files on the disk. + * Shared secret are not handled yet, so get_shared_secret always returns NOT_FOUND. + * + * @b Constructors: + * - local_credential_store_create(bool strict) + * + * @ingroup config + */ +struct local_credential_store_t { + + /** + * Implements credential_store_t interface + */ + credential_store_t credential_store; +}; + +/** + * @brief Creates a local_credential_store_t instance. + * + * @param strict enforce a strict crl policy + * @return credential store instance. + * + * @ingroup config + */ +local_credential_store_t *local_credential_store_create(bool strict); + +#endif /* LOCAL_CREDENTIAL_H_ */ diff --git a/src/charon/config/policies/local_policy_store.c b/src/charon/config/policies/local_policy_store.c new file mode 100644 index 000000000..dd22b43a0 --- /dev/null +++ b/src/charon/config/policies/local_policy_store.c @@ -0,0 +1,282 @@ +/** + * @file local_policy_store.c + * + * @brief Implementation of local_policy_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "local_policy_store.h" + +#include +#include + + +typedef struct private_local_policy_store_t private_local_policy_store_t; + +/** + * Private data of an local_policy_store_t object + */ +struct private_local_policy_store_t { + + /** + * Public part + */ + local_policy_store_t public; + + /** + * list of policy_t's + */ + linked_list_t *policies; + + /** + * Mutex to exclusivly access list + */ + pthread_mutex_t mutex; +}; + +/** + * Implementation of policy_store_t.add_policy. + */ +static void add_policy(private_local_policy_store_t *this, policy_t *policy) +{ + pthread_mutex_lock(&(this->mutex)); + this->policies->insert_last(this->policies, (void*)policy); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Check if a policy contains traffic selectors + */ +static bool contains_traffic_selectors(policy_t *policy, bool mine, + linked_list_t *ts, host_t *host) +{ + linked_list_t *selected; + bool contains = FALSE; + + if (mine) + { + selected = policy->select_my_traffic_selectors(policy, ts, host); + } + else + { + selected = policy->select_other_traffic_selectors(policy, ts, host); + } + if (selected->get_count(selected)) + { + contains = TRUE; + } + selected->destroy_offset(selected, offsetof(traffic_selector_t, destroy)); + return contains; +} + +/** + * Implementation of policy_store_t.get_policy. + */ +static policy_t *get_policy(private_local_policy_store_t *this, + identification_t *my_id, identification_t *other_id, + linked_list_t *my_ts, linked_list_t *other_ts, + host_t *my_host, host_t *other_host) +{ + typedef enum { + PRIO_UNDEFINED = 0x00, + PRIO_TS_MISMATCH = 0x01, + PRIO_ID_ANY = 0x02, + PRIO_ID_MATCH = PRIO_ID_ANY + MAX_WILDCARDS, + } prio_t; + + prio_t best_prio = PRIO_UNDEFINED; + + iterator_t *iterator; + policy_t *candidate; + policy_t *found = NULL; + traffic_selector_t *ts; + + DBG1(DBG_CFG, "searching policy for '%D'...'%D'", my_id, other_id); + iterator = my_ts->create_iterator(my_ts, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + DBG1(DBG_CFG, " local TS: %R", ts); + } + iterator->destroy(iterator); + iterator = other_ts->create_iterator(other_ts, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + DBG1(DBG_CFG, " remote TS: %R", ts); + } + iterator->destroy(iterator); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + + /* determine closest matching policy */ + while (iterator->iterate(iterator, (void**)&candidate)) + { + identification_t *candidate_my_id; + identification_t *candidate_other_id; + int wildcards; + + candidate_my_id = candidate->get_my_id(candidate); + candidate_other_id = candidate->get_other_id(candidate); + + /* my_id is either %any or if set must match exactly */ + if (candidate_my_id->matches(candidate_my_id, my_id, &wildcards)) + { + prio_t prio = PRIO_UNDEFINED; + + /* wildcard match for other_id */ + if (!other_id->matches(other_id, candidate_other_id, &wildcards)) + { + continue; + } + prio = PRIO_ID_MATCH - wildcards; + + /* only accept if traffic selectors match */ + if (!contains_traffic_selectors(candidate, TRUE, my_ts, my_host) || + !contains_traffic_selectors(candidate, FALSE, other_ts, other_host)) + { + DBG2(DBG_CFG, "candidate '%s' inacceptable due traffic " + "selector mismatch", candidate->get_name(candidate)); + prio = PRIO_TS_MISMATCH; + } + + DBG2(DBG_CFG, "candidate policy '%s': '%D'...'%D' (prio=%d)", + candidate->get_name(candidate), + candidate_my_id, candidate_other_id, prio); + + if (prio > best_prio) + { + found = candidate; + best_prio = prio; + } + } + } + iterator->destroy(iterator); + + if (found) + { + DBG1(DBG_CFG, "found matching policy '%s': '%D'...'%D' (prio=%d)", + found->get_name(found), found->get_my_id(found), + found->get_other_id(found), best_prio); + /* give out a new reference to it */ + found->get_ref(found); + } + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implementation of policy_store_t.get_policy_by_name. + */ +static policy_t *get_policy_by_name(private_local_policy_store_t *this, char *name) +{ + iterator_t *iterator; + policy_t *current, *found = NULL; + + DBG2(DBG_CFG, "looking for policy '%s'", name); + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + found = current; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + /* give out a new reference */ + found->get_ref(found); + return found; +} + +/** + * Implementation of policy_store_t.delete_policy. + */ +static status_t delete_policy(private_local_policy_store_t *this, char *name) +{ + iterator_t *iterator; + policy_t *current; + bool found = FALSE; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void **)¤t)) + { + if (strcmp(current->get_name(current), name) == 0) + { + /* remove policy from list, and destroy it */ + iterator->remove(iterator); + current->destroy(current); + found = TRUE; + /* we do not break here, as there may be multipe policies */ + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + if (found) + { + return SUCCESS; + } + return NOT_FOUND; +} + +/** + * Implementation of policy_store_t.create_iterator. + */ +static iterator_t* create_iterator(private_local_policy_store_t *this) +{ + return this->policies->create_iterator_locked(this->policies, + &this->mutex); +} + +/** + * Implementation of policy_store_t.destroy. + */ +static void destroy(private_local_policy_store_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + this->policies->destroy_offset(this->policies, offsetof(policy_t, destroy)); + pthread_mutex_unlock(&(this->mutex)); + free(this); +} + +/** + * Described in header. + */ +local_policy_store_t *local_policy_store_create(void) +{ + private_local_policy_store_t *this = malloc_thing(private_local_policy_store_t); + + this->public.policy_store.add_policy = (void (*) (policy_store_t*,policy_t*))add_policy; + this->public.policy_store.get_policy = (policy_t* (*) (policy_store_t*,identification_t*,identification_t*, + linked_list_t*,linked_list_t*,host_t*,host_t*))get_policy; + this->public.policy_store.get_policy_by_name = (policy_t* (*) (policy_store_t*,char*))get_policy_by_name; + this->public.policy_store.delete_policy = (status_t (*) (policy_store_t*,char*))delete_policy; + this->public.policy_store.create_iterator = (iterator_t* (*) (policy_store_t*))create_iterator; + this->public.policy_store.destroy = (void (*) (policy_store_t*))destroy; + + /* private variables */ + this->policies = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + + return (&this->public); +} diff --git a/src/charon/config/policies/local_policy_store.h b/src/charon/config/policies/local_policy_store.h new file mode 100644 index 000000000..01d5d2d60 --- /dev/null +++ b/src/charon/config/policies/local_policy_store.h @@ -0,0 +1,60 @@ +/** + * @file local_policy_store.h + * + * @brief Interface of local_policy_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef LOCAL_POLICY_STORE_H_ +#define LOCAL_POLICY_STORE_H_ + +typedef struct local_policy_store_t local_policy_store_t; + +#include +#include + + +/** + * @brief A policy_store_t implementation using a simple policy lists. + * + * The local_policy_store_t class implements the policy_store_t interface + * as simple as possible. The policies are stored in a in-memory list. + * + * @b Constructors: + * - local_policy_store_create() + * + * @ingroup config + */ +struct local_policy_store_t { + + /** + * Implements policy_store_t interface + */ + policy_store_t policy_store; +}; + +/** + * @brief Creates a local_policy_store_t instance. + * + * @return policy store instance. + * + * @ingroup config + */ +local_policy_store_t *local_policy_store_create(void); + +#endif /* LOCAL_POLICY_STORE_H_ */ diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c new file mode 100644 index 000000000..363d1609f --- /dev/null +++ b/src/charon/config/policies/policy.c @@ -0,0 +1,635 @@ +/** + * @file policy.c + * + * @brief Implementation of policy_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include + +#include "policy.h" + +#include +#include +#include + +ENUM(dpd_action_names, DPD_NONE, DPD_RESTART, + "DPD_NONE", + "DPD_CLEAR", + "DPD_ROUTE", + "DPD_RESTART" +); + +ENUM(mode_names, MODE_TRANSPORT, MODE_BEET, + "TRANSPORT", + "TUNNEL", + "2", + "3", + "BEET" +); + +typedef struct private_policy_t private_policy_t; + +/** + * Private data of an policy_t object + */ +struct private_policy_t { + + /** + * Public part + */ + policy_t public; + + /** + * Number of references hold by others to this policy + */ + refcount_t refcount; + + /** + * Name of the policy, used to query it + */ + char *name; + + /** + * id to use to identify us + */ + identification_t *my_id; + + /** + * allowed id for other + */ + identification_t *other_id; + + /** + * virtual IP to use locally + */ + host_t *my_virtual_ip; + + /** + * virtual IP to use remotly + */ + host_t *other_virtual_ip; + + /** + * Method to use for own authentication data + */ + auth_method_t auth_method; + + /** + * EAP type to use for peer authentication + */ + eap_type_t eap_type; + + /** + * we have a cert issued by this CA + */ + identification_t *my_ca; + + /** + * we require the other end to have a cert issued by this CA + */ + identification_t *other_ca; + + /** + * updown script + */ + char *updown; + + /** + * allow host access + */ + bool hostaccess; + + /** + * list for all proposals + */ + linked_list_t *proposals; + + /** + * list for traffic selectors for my site + */ + linked_list_t *my_ts; + + /** + * list for traffic selectors for others site + */ + linked_list_t *other_ts; + + /** + * Time before an SA gets invalid + */ + u_int32_t soft_lifetime; + + /** + * Time before an SA gets rekeyed + */ + u_int32_t hard_lifetime; + + /** + * Time, which specifies the range of a random value + * substracted from soft_lifetime. + */ + u_int32_t jitter; + + /** + * What to do with an SA when other peer seams to be dead? + */ + bool dpd_action; + + /** + * Mode to propose for a initiated CHILD: tunnel/transport + */ + mode_t mode; +}; + +/** + * Implementation of policy_t.get_name + */ +static char *get_name(private_policy_t *this) +{ + return this->name; +} + +/** + * Implementation of policy_t.get_my_id + */ +static identification_t *get_my_id(private_policy_t *this) +{ + return this->my_id; +} + +/** + * Implementation of policy_t.get_other_id + */ +static identification_t *get_other_id(private_policy_t *this) +{ + return this->other_id; +} + +/** + * Implementation of policy_t.get_my_ca + */ +static identification_t *get_my_ca(private_policy_t *this) +{ + return this->my_ca; +} + +/** + * Implementation of policy_t.get_other_ca + */ +static identification_t *get_other_ca(private_policy_t *this) +{ + return this->other_ca; +} + +/** + * Implementation of connection_t.auth_method_t. + */ +static auth_method_t get_auth_method(private_policy_t *this) +{ + return this->auth_method; +} + +/** + * Implementation of connection_t.get_eap_type. + */ +static eap_type_t get_eap_type(private_policy_t *this) +{ + return this->eap_type; +} + +/** + * Get traffic selectors, with wildcard-address update + */ +static linked_list_t *get_traffic_selectors(private_policy_t *this, + linked_list_t *list, host_t *host) +{ + iterator_t *iterator; + traffic_selector_t *current; + linked_list_t *result = linked_list_create(); + + iterator = list->create_iterator(list, TRUE); + + while (iterator->iterate(iterator, (void**)¤t)) + { + /* we make a copy of the TS, this allows us to update wildcard + * addresses in it. We won't pollute the shared policy. */ + current = current->clone(current); + if (host) + { + current->set_address(current, host); + } + + result->insert_last(result, (void*)current); + } + iterator->destroy(iterator); + return result; +} + +/** + * Implementation of policy_t.get_my_traffic_selectors + */ +static linked_list_t *get_my_traffic_selectors(private_policy_t *this, host_t *me) +{ + return get_traffic_selectors(this, this->my_ts, me); +} + +/** + * Implementation of policy_t.get_other_traffic_selectors + */ +static linked_list_t *get_other_traffic_selectors(private_policy_t *this, host_t *other) +{ + return get_traffic_selectors(this, this->other_ts, other); +} + +/** + * Narrow traffic selectors, with wildcard-address update in "stored". + */ +static linked_list_t *select_traffic_selectors(private_policy_t *this, + linked_list_t *stored, + linked_list_t *supplied, + host_t *host) +{ + iterator_t *supplied_iter, *stored_iter, *i1, *i2; + traffic_selector_t *supplied_ts, *stored_ts, *selected_ts, *ts1, *ts2; + linked_list_t *selected = linked_list_create(); + + DBG2(DBG_CFG, "selecting traffic selectors"); + + stored_iter = stored->create_iterator(stored, TRUE); + supplied_iter = supplied->create_iterator(supplied, TRUE); + + /* iterate over all stored selectors */ + while (stored_iter->iterate(stored_iter, (void**)&stored_ts)) + { + /* we make a copy of the TS, this allows us to update wildcard + * addresses in it. We won't pollute the shared policy. */ + stored_ts = stored_ts->clone(stored_ts); + if (host) + { + stored_ts->set_address(stored_ts, host); + } + + supplied_iter->reset(supplied_iter); + /* iterate over all supplied traffic selectors */ + while (supplied_iter->iterate(supplied_iter, (void**)&supplied_ts)) + { + DBG2(DBG_CFG, "stored %R <=> %R received", + stored_ts, supplied_ts); + + selected_ts = stored_ts->get_subset(stored_ts, supplied_ts); + if (selected_ts) + { + /* got a match, add to list */ + selected->insert_last(selected, (void*)selected_ts); + + DBG2(DBG_CFG, "found traffic selector for %s: %R", + stored == this->my_ts ? "us" : "other", selected_ts); + } + } + stored_ts->destroy(stored_ts); + } + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + /* remove any redundant traffic selectors in the list */ + i1 = selected->create_iterator(selected, TRUE); + i2 = selected->create_iterator(selected, TRUE); + while (i1->iterate(i1, (void**)&ts1)) + { + while (i2->iterate(i2, (void**)&ts2)) + { + if (ts1 != ts2) + { + if (ts2->is_contained_in(ts2, ts1)) + { + i2->remove(i2); + ts2->destroy(ts2); + i1->reset(i1); + break; + } + if (ts1->is_contained_in(ts1, ts2)) + { + i1->remove(i1); + ts1->destroy(ts1); + i2->reset(i2); + break; + } + } + } + } + i1->destroy(i1); + i2->destroy(i2); + + return selected; +} + +/** + * Implementation of private_policy_t.select_my_traffic_selectors + */ +static linked_list_t *select_my_traffic_selectors(private_policy_t *this, + linked_list_t *supplied, + host_t *me) +{ + return select_traffic_selectors(this, this->my_ts, supplied, me); +} + +/** + * Implementation of private_policy_t.select_other_traffic_selectors + */ +static linked_list_t *select_other_traffic_selectors(private_policy_t *this, + linked_list_t *supplied, + host_t* other) +{ + return select_traffic_selectors(this, this->other_ts, supplied, other); +} + +/** + * Implementation of policy_t.get_proposal_iterator + */ +static linked_list_t *get_proposals(private_policy_t *this) +{ + iterator_t *iterator; + proposal_t *current; + linked_list_t *proposals = linked_list_create(); + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + current = current->clone(current); + proposals->insert_last(proposals, (void*)current); + } + iterator->destroy(iterator); + + return proposals; +} + +/** + * Implementation of policy_t.select_proposal + */ +static proposal_t *select_proposal(private_policy_t *this, linked_list_t *proposals) +{ + iterator_t *stored_iter, *supplied_iter; + proposal_t *stored, *supplied, *selected; + + stored_iter = this->proposals->create_iterator(this->proposals, TRUE); + supplied_iter = proposals->create_iterator(proposals, TRUE); + + /* compare all stored proposals with all supplied. Stored ones are preferred. */ + while (stored_iter->iterate(stored_iter, (void**)&stored)) + { + supplied_iter->reset(supplied_iter); + while (supplied_iter->iterate(supplied_iter, (void**)&supplied)) + { + selected = stored->select(stored, supplied); + if (selected) + { + /* they match, return */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + return selected; + } + } + } + + /* no proposal match :-(, will result in a NO_PROPOSAL_CHOSEN... */ + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return NULL; +} + +/** + * Implementation of policy_t.add_authorities + */ +static void add_authorities(private_policy_t *this, identification_t *my_ca, identification_t *other_ca) +{ + this->my_ca = my_ca; + this->other_ca = other_ca; +} + +/** + * Implementation of policy_t.get_updown + */ +static char* get_updown(private_policy_t *this) +{ + return this->updown; +} + +/** + * Implementation of policy_t.get_hostaccess + */ +static bool get_hostaccess(private_policy_t *this) +{ + return this->hostaccess; +} + +/** + * Implements policy_t.get_dpd_action + */ +static dpd_action_t get_dpd_action(private_policy_t *this) +{ + return this->dpd_action; +} + +/** + * Implementation of policy_t.add_my_traffic_selector + */ +static void add_my_traffic_selector(private_policy_t *this, traffic_selector_t *traffic_selector) +{ + this->my_ts->insert_last(this->my_ts, (void*)traffic_selector); +} + +/** + * Implementation of policy_t.add_other_traffic_selector + */ +static void add_other_traffic_selector(private_policy_t *this, traffic_selector_t *traffic_selector) +{ + this->other_ts->insert_last(this->other_ts, (void*)traffic_selector); +} + +/** + * Implementation of policy_t.add_proposal + */ +static void add_proposal(private_policy_t *this, proposal_t *proposal) +{ + this->proposals->insert_last(this->proposals, (void*)proposal); +} + +/** + * Implementation of policy_t.get_soft_lifetime + */ +static u_int32_t get_soft_lifetime(private_policy_t *this) +{ + if (this->jitter == 0) + { + return this->soft_lifetime ; + } + return this->soft_lifetime - (random() % this->jitter); +} + +/** + * Implementation of policy_t.get_hard_lifetime + */ +static u_int32_t get_hard_lifetime(private_policy_t *this) +{ + return this->hard_lifetime; +} + +/** + * Implementation of policy_t.get_mode. + */ +static mode_t get_mode(private_policy_t *this) +{ + return this->mode; +} + +/** + * Implementation of policy_t.get_virtual_ip. + */ +static host_t* get_virtual_ip(private_policy_t *this, host_t *suggestion) +{ + if (suggestion == NULL) + { + if (this->my_virtual_ip) + { + return this->my_virtual_ip->clone(this->my_virtual_ip); + } + return NULL; + } + if (this->other_virtual_ip) + { + return this->other_virtual_ip->clone(this->other_virtual_ip); + } + if (suggestion->is_anyaddr(suggestion)) + { + return NULL; + } + return suggestion->clone(suggestion); +} + +/** + * Implements policy_t.get_ref. + */ +static void get_ref(private_policy_t *this) +{ + ref_get(&this->refcount); +} + +/** + * Implements policy_t.destroy. + */ +static void destroy(private_policy_t *this) +{ + if (ref_put(&this->refcount)) + { + + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); + this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); + + /* delete certification authorities */ + DESTROY_IF(this->my_ca); + DESTROY_IF(this->other_ca); + + /* delete updown script */ + if (this->updown) + { + free(this->updown); + } + + /* delete ids */ + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + DESTROY_IF(this->my_virtual_ip); + DESTROY_IF(this->other_virtual_ip); + + free(this->name); + free(this); + } +} + +/* + * Described in header-file + */ +policy_t *policy_create(char *name, identification_t *my_id, identification_t *other_id, + host_t *my_virtual_ip, host_t *other_virtual_ip, + auth_method_t auth_method, eap_type_t eap_type, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter, char *updown, bool hostaccess, + mode_t mode, dpd_action_t dpd_action) +{ + private_policy_t *this = malloc_thing(private_policy_t); + + /* public functions */ + this->public.get_name = (char* (*) (policy_t*))get_name; + this->public.get_my_id = (identification_t* (*) (policy_t*))get_my_id; + this->public.get_other_id = (identification_t* (*) (policy_t*))get_other_id; + this->public.get_my_ca = (identification_t* (*) (policy_t*))get_my_ca; + this->public.get_other_ca = (identification_t* (*) (policy_t*))get_other_ca; + this->public.get_auth_method = (auth_method_t (*) (policy_t*)) get_auth_method; + this->public.get_eap_type = (eap_type_t (*) (policy_t*)) get_eap_type; + this->public.get_my_traffic_selectors = (linked_list_t* (*) (policy_t*,host_t*))get_my_traffic_selectors; + this->public.get_other_traffic_selectors = (linked_list_t* (*) (policy_t*,host_t*))get_other_traffic_selectors; + this->public.select_my_traffic_selectors = (linked_list_t* (*) (policy_t*,linked_list_t*,host_t*))select_my_traffic_selectors; + this->public.select_other_traffic_selectors = (linked_list_t* (*) (policy_t*,linked_list_t*,host_t*))select_other_traffic_selectors; + this->public.get_proposals = (linked_list_t* (*) (policy_t*))get_proposals; + this->public.select_proposal = (proposal_t* (*) (policy_t*,linked_list_t*))select_proposal; + this->public.add_my_traffic_selector = (void (*) (policy_t*,traffic_selector_t*))add_my_traffic_selector; + this->public.add_other_traffic_selector = (void (*) (policy_t*,traffic_selector_t*))add_other_traffic_selector; + this->public.add_proposal = (void (*) (policy_t*,proposal_t*))add_proposal; + this->public.add_authorities = (void (*) (policy_t*,identification_t*,identification_t*))add_authorities; + this->public.get_updown = (char* (*) (policy_t*))get_updown; + this->public.get_hostaccess = (bool (*) (policy_t*))get_hostaccess; + this->public.get_dpd_action = (dpd_action_t (*) (policy_t*))get_dpd_action; + this->public.get_soft_lifetime = (u_int32_t (*) (policy_t *))get_soft_lifetime; + this->public.get_hard_lifetime = (u_int32_t (*) (policy_t *))get_hard_lifetime; + this->public.get_mode = (mode_t (*) (policy_t *))get_mode; + this->public.get_virtual_ip = (host_t* (*)(policy_t*,host_t*))get_virtual_ip; + this->public.get_ref = (void (*) (policy_t*))get_ref; + this->public.destroy = (void (*) (policy_t*))destroy; + + /* apply init values */ + this->name = strdup(name); + this->my_id = my_id; + this->other_id = other_id; + this->my_virtual_ip = my_virtual_ip; + this->other_virtual_ip = other_virtual_ip; + this->auth_method = auth_method; + this->eap_type = eap_type; + this->hard_lifetime = hard_lifetime; + this->soft_lifetime = soft_lifetime; + this->jitter = jitter; + this->updown = (updown == NULL) ? NULL : strdup(updown); + this->hostaccess = hostaccess; + this->dpd_action = dpd_action; + this->mode = mode; + + /* initialize private members*/ + this->refcount = 1; + this->my_ca = NULL; + this->other_ca = NULL; + this->proposals = linked_list_create(); + this->my_ts = linked_list_create(); + this->other_ts = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/config/policies/policy.h b/src/charon/config/policies/policy.h new file mode 100644 index 000000000..d8916b29e --- /dev/null +++ b/src/charon/config/policies/policy.h @@ -0,0 +1,413 @@ +/** + * @file policy.h + * + * @brief Interface of policy_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef POLICY_H_ +#define POLICY_H_ + +typedef enum dpd_action_t dpd_action_t; +typedef struct policy_t policy_t; + +#include +#include +#include +#include +#include +#include + + +/** + * @brief Actions to take when a peer does not respond (dead peer detected). + * + * These values are the same as in pluto/starter, so do not modify them! + * + * @ingroup config + */ +enum dpd_action_t { + /** DPD disabled */ + DPD_NONE, + /** remove CHILD_SA without replacement */ + DPD_CLEAR, + /** route the CHILD_SA to resetup when needed */ + DPD_ROUTE, + /** restart CHILD_SA in a new IKE_SA, immediately */ + DPD_RESTART, +}; + +/** + * enum names for dpd_action_t. + */ +extern enum_name_t *dpd_action_names; + +/** + * @brief Mode of an IPsec SA. + * + * These are equal to those defined in XFRM, so don't change. + * + * @ingroup config + */ +enum mode_t { + /** transport mode, no inner address */ + MODE_TRANSPORT = 0, + /** tunnel mode, inner and outer addresses */ + MODE_TUNNEL = 1, + /** BEET mode, tunnel mode but fixed, bound inner addresses */ + MODE_BEET = 4, +}; + +/** + * enum names for mode_t. + */ +extern enum_name_t *mode_names; + +/** + * @brief A policy_t defines the policies to apply to CHILD_SAs. + * + * The given two IDs identify a policy. These rules define how + * child SAs may be set up and which traffic may be IPsec'ed. + * + * @b Constructors: + * - policy_create() + * + * @ingroup config + */ +struct policy_t { + + /** + * @brief Get the name of the policy. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return policy's name + */ + char *(*get_name) (policy_t *this); + + /** + * @brief Get own id. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own id + */ + identification_t *(*get_my_id) (policy_t *this); + + /** + * @brief Get peer id. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other id + */ + identification_t *(*get_other_id) (policy_t *this); + + /** + * @brief Get own ca. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own ca + */ + identification_t *(*get_my_ca) (policy_t *this); + + /** + * @brief Get peer ca. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other ca + */ + identification_t *(*get_other_ca) (policy_t *this); + + /** + * @brief Get the authentication method to use. + * + * @param this calling object + * @return authentication method + */ + auth_method_t (*get_auth_method) (policy_t *this); + + /** + * @brief Get the EAP type to use for peer authentication. + * + * @param this calling object + * @return authentication method + */ + eap_type_t (*get_eap_type) (policy_t *this); + + /** + * @brief Get configured traffic selectors for our site. + * + * Returns a list with all traffic selectors for the local + * site. List and items must be destroyed after usage. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_my_traffic_selectors) (policy_t *this, host_t *me); + + /** + * @brief Get configured traffic selectors for others site. + * + * Returns a list with all traffic selectors for the remote + * site. List and items must be destroyed after usage. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_other_traffic_selectors) (policy_t *this, host_t* other); + + /** + * @brief Select traffic selectors from a supplied list for local site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * As the traffic selectors may contain a wildcard address (0.0.0.0) for + * addresses we don't know in previous, an address may be supplied to + * replace these 0.0.0.0 addresses on-the-fly. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @param me host address used by us + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_my_traffic_selectors) (policy_t *this, + linked_list_t *supplied, + host_t *me); + + /** + * @brief Select traffic selectors from a supplied list for remote site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * As the traffic selectors may contain a wildcard address (0.0.0.0) for + * addresses we don't know in previous, an address may be supplied to + * replace these 0.0.0.0 addresses on-the-fly. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_other_traffic_selectors) (policy_t *this, + linked_list_t *supplied, + host_t *other); + + /** + * @brief Get the list of internally stored proposals. + * + * policy_t does store proposals for AH/ESP, IKE proposals are in + * the connection_t. + * Resulting list and all of its proposals must be freed after usage. + * + * @param this calling object + * @return lists with proposals + */ + linked_list_t *(*get_proposals) (policy_t *this); + + /** + * @brief Select a proposal from a supplied list. + * + * Returned propsal is newly created and must be destroyed after usage. + * + * @param this calling object + * @param proposals list from from wich proposals are selected + * @return selected proposal, or NULL if nothing matches + */ + proposal_t *(*select_proposal) (policy_t *this, linked_list_t *proposals); + + /** + * @brief Add a traffic selector to the list for local site. + * + * After add, traffic selector is owned by policy. + * + * @param this calling object + * @param traffic_selector traffic_selector to add + */ + void (*add_my_traffic_selector) (policy_t *this, traffic_selector_t *traffic_selector); + + /** + * @brief Add a traffic selector to the list for remote site. + * + * After add, traffic selector is owned by policy. + * + * @param this calling object + * @param traffic_selector traffic_selector to add + */ + void (*add_other_traffic_selector) (policy_t *this, traffic_selector_t *traffic_selector); + + /** + * @brief Add a proposal to the list. + * + * The proposals are stored by priority, first added + * is the most prefered. + * After add, proposal is owned by policy. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (policy_t *this, proposal_t *proposal); + + /** + * @brief Add certification authorities. + * + * @param this calling object + * @param my_ca issuer of my certificate + * @param other_ca required issuer of the peer's certificate + */ + void (*add_authorities) (policy_t *this, identification_t *my_ca, identification_t *other_ca); + + /** + * @brief Get updown script + * + * @param this calling object + * @return path to updown script + */ + char* (*get_updown) (policy_t *this); + + /** + * @brief Get hostaccess flag + * + * @param this calling object + * @return value of hostaccess flag + */ + bool (*get_hostaccess) (policy_t *this); + + /** + * @brief What should be done with a CHILD_SA, when other peer does not respond. + * + * @param this calling object + * @return dpd action + */ + dpd_action_t (*get_dpd_action) (policy_t *this); + + /** + * @brief Get the lifetime of a policy, before rekeying starts. + * + * A call to this function automatically adds a jitter to + * avoid simultanous rekeying. + * + * @param this policy + * @return lifetime in seconds + */ + u_int32_t (*get_soft_lifetime) (policy_t *this); + + /** + * @brief Get the lifetime of a policy, before SA gets deleted. + * + * @param this policy + * @return lifetime in seconds + */ + u_int32_t (*get_hard_lifetime) (policy_t *this); + + /** + * @brief Get the mode to use for the CHILD_SA, tunnel, transport or BEET. + * + * @param this policy + * @return lifetime in seconds + */ + mode_t (*get_mode) (policy_t *this); + + /** + * @brief Get a virtual IP for the local or the remote host. + * + * By supplying NULL as IP, an IP for the local host is requested. It + * may be %any or specific. + * By supplying %any as host, an IP from the pool is selected to be + * served to the peer. + * If a specified host is supplied, it is checked if this address + * is acceptable to serve to the peer. If so, it is returned. Otherwise, + * an alternative IP is returned. + * In any mode, this call may return NULL indicating virtual IP should + * not be used. + * + * @param this policy + * @param suggestion NULL, %any or specific, see description + * @return clone of an IP to use, or NULL + */ + host_t* (*get_virtual_ip) (policy_t *this, host_t *suggestion); + + /** + * @brief Get a new reference. + * + * Get a new reference to this policy by increasing + * it's internal reference counter. + * Do not call get_ref or any other function until you + * already have a reference. Otherwise the object may get + * destroyed while calling get_ref(), + * + * @param this calling object + */ + void (*get_ref) (policy_t *this); + + /** + * @brief Destroys the policy object. + * + * Decrements the internal reference counter and + * destroys the policy when it reaches zero. + * + * @param this calling object + */ + void (*destroy) (policy_t *this); +}; + +/** + * @brief Create a configuration object for IKE_AUTH and later. + * + * name-string gets cloned, ID's not. + * Virtual IPs are used if they are != NULL. A %any host means the virtual + * IP should be obtained from the other peer. + * Lifetimes are in seconds. To prevent to peers to start rekeying at the + * same time, a jitter may be specified. Rekeying of an SA starts at + * (soft_lifetime - random(0, jitter)). After a successful rekeying, + * the hard_lifetime limit counter is reset. You should specify + * hard_lifetime > soft_lifetime > jitter. + * After a call to create, a reference is obtained (refcount = 1). + * + * @param name name of the policy + * @param my_id identification_t for ourselves + * @param other_id identification_t for the remote guy + * @param my_virtual_ip virtual IP for local host, or NULL + * @param other_virtual_ip virtual IP for remote host, or NULL + * @param auth_method Authentication method to use for our(!) auth data + * @param eap_type EAP type to use for peer authentication + * @param hard_lifetime lifetime before deleting an SA + * @param soft_lifetime lifetime before rekeying an SA + * @param jitter range of randomization time + * @param updown updown script to execute on up/down event + * @param hostaccess allow access to the host itself (used by the updown script) + * @param mode mode to propose for CHILD_SA, transport, tunnel or BEET + * @param dpd_action what to to with a CHILD_SA when other peer does not respond + * @return policy_t object + * + * @ingroup config + */ +policy_t *policy_create(char *name, + identification_t *my_id, identification_t *other_id, + host_t *my_virtual_ip, host_t *other_virtual_ip, + auth_method_t auth_method, eap_type_t eap_type, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter, char *updown, bool hostaccess, + mode_t mode, dpd_action_t dpd_action); + +#endif /* POLICY_H_ */ diff --git a/src/charon/config/policies/policy_store.h b/src/charon/config/policies/policy_store.h new file mode 100755 index 000000000..cd8870953 --- /dev/null +++ b/src/charon/config/policies/policy_store.h @@ -0,0 +1,119 @@ +/** + * @file policy_store.h + * + * @brief Interface policy_store_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef POLICY_STORE_H_ +#define POLICY_STORE_H_ + +typedef struct policy_store_t policy_store_t; + +#include +#include +#include + + +/** + * @brief The interface for a store of policy_t's. + * + * The store uses reference counting to manage their lifetime. Call + * destroy() for a policy which is returned from the store after usage. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct policy_store_t { + + /** + * @brief Returns a policy identified by two IDs and a set of traffic selectors. + * + * other_id must be fully qualified. my_id may be %any, as the + * other peer may not include an IDr Request. + * + * @param this calling object + * @param my_id own ID of the policy + * @param other_id others ID of the policy + * @param my_ts traffic selectors requested for local host + * @param other_ts traffic selectors requested for remote host + * @param my_host host to use for wilcards in TS compare + * @param other_host host to use for wildcards in TS compare + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy) (policy_store_t *this, + identification_t *my_id, identification_t *other_id, + linked_list_t *my_ts, linked_list_t *other_ts, + host_t *my_host, host_t* other_host); + + /** + * @brief Returns a policy identified by a connection name. + * + * @param this calling object + * @param name name of the policy + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy_by_name) (policy_store_t *this, char *name); + + /** + * @brief Add a policy to the list. + * + * The policy is owned by the store after the call. Do + * not modify nor free. + * + * @param this calling object + * @param policy policy to add + */ + void (*add_policy) (policy_store_t *this, policy_t *policy); + + /** + * @brief Delete a policy from the store. + * + * Remove a policy from the store identified by its name. + * + * @param this calling object + * @param policy policy to add + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*delete_policy) (policy_store_t *this, char *name); + + /** + * @brief Get an iterator for the stored policies. + * + * @param this calling object + * @return iterator over all stored policies + */ + iterator_t* (*create_iterator) (policy_store_t *this); + + /** + * @brief Destroys a policy_store_t object. + * + * @param this calling object + */ + void (*destroy) (policy_store_t *this); +}; + +#endif /*POLICY_STORE_H_*/ diff --git a/src/charon/config/proposal.c b/src/charon/config/proposal.c new file mode 100644 index 000000000..dcab8cbdd --- /dev/null +++ b/src/charon/config/proposal.c @@ -0,0 +1,641 @@ +/** + * @file proposal.c + * + * @brief Implementation of proposal_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "proposal.h" + +#include +#include +#include +#include +#include +#include +#include + + +ENUM(protocol_id_names, PROTO_NONE, PROTO_ESP, + "PROTO_NONE", + "IKE", + "AH", + "ESP", +); + +ENUM_BEGIN(transform_type_names, UNDEFINED_TRANSFORM_TYPE, UNDEFINED_TRANSFORM_TYPE, + "UNDEFINED_TRANSFORM_TYPE"); +ENUM_NEXT(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS, UNDEFINED_TRANSFORM_TYPE, + "ENCRYPTION_ALGORITHM", + "PSEUDO_RANDOM_FUNCTION", + "INTEGRITY_ALGORITHM", + "DIFFIE_HELLMAN_GROUP", + "EXTENDED_SEQUENCE_NUMBERS"); +ENUM_END(transform_type_names, EXTENDED_SEQUENCE_NUMBERS); + +ENUM(extended_sequence_numbers_names, NO_EXT_SEQ_NUMBERS, EXT_SEQ_NUMBERS, + "NO_EXT_SEQ_NUMBERS", + "EXT_SEQ_NUMBERS", +); + +typedef struct private_proposal_t private_proposal_t; + +/** + * Private data of an proposal_t object + */ +struct private_proposal_t { + + /** + * Public part + */ + proposal_t public; + + /** + * protocol (ESP or AH) + */ + protocol_id_t protocol; + + /** + * priority ordered list of encryption algorithms + */ + linked_list_t *encryption_algos; + + /** + * priority ordered list of integrity algorithms + */ + linked_list_t *integrity_algos; + + /** + * priority ordered list of pseudo random functions + */ + linked_list_t *prf_algos; + + /** + * priority ordered list of dh groups + */ + linked_list_t *dh_groups; + + /** + * priority ordered list of extended sequence number flags + */ + linked_list_t *esns; + + /** + * senders SPI + */ + u_int64_t spi; +}; + +/** + * Add algorithm/keysize to a algorithm list + */ +static void add_algo(linked_list_t *list, u_int16_t algo, size_t key_size) +{ + algorithm_t *algo_key; + + algo_key = malloc_thing(algorithm_t); + algo_key->algorithm = algo; + algo_key->key_size = key_size; + list->insert_last(list, (void*)algo_key); +} + +/** + * Implements proposal_t.add_algorithm + */ +static void add_algorithm(private_proposal_t *this, transform_type_t type, u_int16_t algo, size_t key_size) +{ + switch (type) + { + case ENCRYPTION_ALGORITHM: + add_algo(this->encryption_algos, algo, key_size); + break; + case INTEGRITY_ALGORITHM: + add_algo(this->integrity_algos, algo, key_size); + break; + case PSEUDO_RANDOM_FUNCTION: + add_algo(this->prf_algos, algo, key_size); + break; + case DIFFIE_HELLMAN_GROUP: + add_algo(this->dh_groups, algo, 0); + break; + case EXTENDED_SEQUENCE_NUMBERS: + add_algo(this->esns, algo, 0); + break; + default: + break; + } +} + +/** + * Implements proposal_t.get_algorithm. + */ +static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo) +{ + linked_list_t *list; + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = this->encryption_algos; + break; + case INTEGRITY_ALGORITHM: + list = this->integrity_algos; + break; + case PSEUDO_RANDOM_FUNCTION: + list = this->prf_algos; + break; + case DIFFIE_HELLMAN_GROUP: + list = this->dh_groups; + break; + case EXTENDED_SEQUENCE_NUMBERS: + list = this->esns; + break; + default: + return FALSE; + } + if (list->get_first(list, (void**)algo) != SUCCESS) + { + return FALSE; + } + return TRUE; +} + +/** + * Implements proposal_t.create_algorithm_iterator. + */ +static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform_type_t type) +{ + switch (type) + { + case ENCRYPTION_ALGORITHM: + return this->encryption_algos->create_iterator(this->encryption_algos, TRUE); + case INTEGRITY_ALGORITHM: + return this->integrity_algos->create_iterator(this->integrity_algos, TRUE); + case PSEUDO_RANDOM_FUNCTION: + return this->prf_algos->create_iterator(this->prf_algos, TRUE); + case DIFFIE_HELLMAN_GROUP: + return this->dh_groups->create_iterator(this->dh_groups, TRUE); + case EXTENDED_SEQUENCE_NUMBERS: + return this->esns->create_iterator(this->esns, TRUE); + default: + break; + } + return NULL; +} + +/** + * Find a matching alg/keysize in two linked lists + */ +static bool select_algo(linked_list_t *first, linked_list_t *second, bool *add, u_int16_t *alg, size_t *key_size) +{ + iterator_t *first_iter, *second_iter; + algorithm_t *first_alg, *second_alg; + + /* if in both are zero algorithms specified, we HAVE a match */ + if (first->get_count(first) == 0 && second->get_count(second) == 0) + { + *add = FALSE; + return TRUE; + } + + first_iter = first->create_iterator(first, TRUE); + second_iter = second->create_iterator(second, TRUE); + /* compare algs, order of algs in "first" is preferred */ + while (first_iter->iterate(first_iter, (void**)&first_alg)) + { + second_iter->reset(second_iter); + while (second_iter->iterate(second_iter, (void**)&second_alg)) + { + if (first_alg->algorithm == second_alg->algorithm && + first_alg->key_size == second_alg->key_size) + { + /* ok, we have an algorithm */ + *alg = first_alg->algorithm; + *key_size = first_alg->key_size; + *add = TRUE; + first_iter->destroy(first_iter); + second_iter->destroy(second_iter); + return TRUE; + } + } + } + /* no match in all comparisons */ + first_iter->destroy(first_iter); + second_iter->destroy(second_iter); + return FALSE; +} + +/** + * Implements proposal_t.select. + */ +static proposal_t *select_proposal(private_proposal_t *this, private_proposal_t *other) +{ + proposal_t *selected; + u_int16_t algo; + size_t key_size; + bool add; + + DBG2(DBG_CFG, "selecting proposal:"); + + /* check protocol */ + if (this->protocol != other->protocol) + { + DBG2(DBG_CFG, " protocol mismatch, skipping"); + return NULL; + } + + selected = proposal_create(this->protocol); + + /* select encryption algorithm */ + if (select_algo(this->encryption_algos, other->encryption_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, ENCRYPTION_ALGORITHM, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable ENCRYPTION_ALGORITHM found, skipping"); + return NULL; + } + /* select integrity algorithm */ + if (select_algo(this->integrity_algos, other->integrity_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, INTEGRITY_ALGORITHM, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable INTEGRITY_ALGORITHM found, skipping"); + return NULL; + } + /* select prf algorithm */ + if (select_algo(this->prf_algos, other->prf_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, PSEUDO_RANDOM_FUNCTION, algo, key_size); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable PSEUDO_RANDOM_FUNCTION found, skipping"); + return NULL; + } + /* select a DH-group */ + if (select_algo(this->dh_groups, other->dh_groups, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, DIFFIE_HELLMAN_GROUP, algo, 0); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable DIFFIE_HELLMAN_GROUP found, skipping"); + return NULL; + } + /* select if we use ESNs */ + if (select_algo(this->esns, other->esns, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, EXTENDED_SEQUENCE_NUMBERS, algo, 0); + } + } + else + { + selected->destroy(selected); + DBG2(DBG_CFG, " no acceptable EXTENDED_SEQUENCE_NUMBERS found, skipping"); + return NULL; + } + DBG2(DBG_CFG, " proposal matches"); + + /* apply SPI from "other" */ + selected->set_spi(selected, other->spi); + + /* everything matched, return new proposal */ + return selected; +} + +/** + * Implements proposal_t.get_protocols. + */ +static protocol_id_t get_protocol(private_proposal_t *this) +{ + return this->protocol; +} + +/** + * Implements proposal_t.set_spi. + */ +static void set_spi(private_proposal_t *this, u_int64_t spi) +{ + this->spi = spi; +} + +/** + * Implements proposal_t.get_spi. + */ +static u_int64_t get_spi(private_proposal_t *this) +{ + return this->spi; +} + +/** + * Clone a algorithm list + */ +static void clone_algo_list(linked_list_t *list, linked_list_t *clone_list) +{ + algorithm_t *algo, *clone_algo; + iterator_t *iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void**)&algo)) + { + clone_algo = malloc_thing(algorithm_t); + memcpy(clone_algo, algo, sizeof(algorithm_t)); + clone_list->insert_last(clone_list, (void*)clone_algo); + } + iterator->destroy(iterator); +} + +/** + * Implements proposal_t.clone + */ +static proposal_t *clone_(private_proposal_t *this) +{ + private_proposal_t *clone = (private_proposal_t*)proposal_create(this->protocol); + + clone_algo_list(this->encryption_algos, clone->encryption_algos); + clone_algo_list(this->integrity_algos, clone->integrity_algos); + clone_algo_list(this->prf_algos, clone->prf_algos); + clone_algo_list(this->dh_groups, clone->dh_groups); + clone_algo_list(this->esns, clone->esns); + + clone->spi = this->spi; + + return &clone->public; +} + +static status_t add_string_algo(private_proposal_t *this, chunk_t alg) +{ + if (strncmp(alg.ptr, "null", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_NULL, 0); + } + else if (strncmp(alg.ptr, "aes128", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + } + else if (strncmp(alg.ptr, "aes192", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + } + else if (strncmp(alg.ptr, "aes256", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + } + else if (strncmp(alg.ptr, "3des", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + } + /* blowfish only uses some predefined key sizes yet */ + else if (strncmp(alg.ptr, "blowfish128", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 128); + } + else if (strncmp(alg.ptr, "blowfish192", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 192); + } + else if (strncmp(alg.ptr, "blowfish256", alg.len) == 0) + { + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + } + else if (strncmp(alg.ptr, "sha", alg.len) == 0 || + strncmp(alg.ptr, "sha1", alg.len) == 0) + { + /* sha means we use SHA for both, PRF and AUTH */ + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + } + } + else if (strncmp(alg.ptr, "sha256", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_256, 0); + } + } + else if (strncmp(alg.ptr, "sha384", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_384, 0); + } + } + else if (strncmp(alg.ptr, "sha512", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_512, 0); + } + } + else if (strncmp(alg.ptr, "md5", alg.len) == 0) + { + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + if (this->protocol == PROTO_IKE) + { + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + } + } + else if (strncmp(alg.ptr, "modp768", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_768_BIT, 0); + } + else if (strncmp(alg.ptr, "modp1024", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + } + else if (strncmp(alg.ptr, "modp1536", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + } + else if (strncmp(alg.ptr, "modp2048", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + } + else if (strncmp(alg.ptr, "modp4096", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + } + else if (strncmp(alg.ptr, "modp8192", alg.len) == 0) + { + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + } + else + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implements proposal_t.destroy. + */ +static void destroy(private_proposal_t *this) +{ + this->encryption_algos->destroy_function(this->encryption_algos, free); + this->integrity_algos->destroy_function(this->integrity_algos, free); + this->prf_algos->destroy_function(this->prf_algos, free); + this->dh_groups->destroy_function(this->dh_groups, free); + this->esns->destroy_function(this->esns, free); + free(this); +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create(protocol_id_t protocol) +{ + private_proposal_t *this = malloc_thing(private_proposal_t); + + this->public.add_algorithm = (void (*)(proposal_t*,transform_type_t,u_int16_t,size_t))add_algorithm; + this->public.create_algorithm_iterator = (iterator_t* (*)(proposal_t*,transform_type_t))create_algorithm_iterator; + this->public.get_algorithm = (bool (*)(proposal_t*,transform_type_t,algorithm_t**))get_algorithm; + this->public.select = (proposal_t* (*)(proposal_t*,proposal_t*))select_proposal; + this->public.get_protocol = (protocol_id_t(*)(proposal_t*))get_protocol; + this->public.set_spi = (void(*)(proposal_t*,u_int64_t))set_spi; + this->public.get_spi = (u_int64_t(*)(proposal_t*))get_spi; + this->public.clone = (proposal_t*(*)(proposal_t*))clone_; + this->public.destroy = (void(*)(proposal_t*))destroy; + + this->spi = 0; + this->protocol = protocol; + + this->encryption_algos = linked_list_create(); + this->integrity_algos = linked_list_create(); + this->prf_algos = linked_list_create(); + this->dh_groups = linked_list_create(); + this->esns = linked_list_create(); + + return &this->public; +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create_default(protocol_id_t protocol) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol); + + switch (protocol) + { + case PROTO_IKE: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_256, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_384, 0); + add_algorithm(this, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA2_512, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + add_algorithm(this, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + break; + case PROTO_ESP: + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 128); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 192); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 256); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + add_algorithm(this, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 256); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + case PROTO_AH: + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + add_algorithm(this, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + break; + default: + break; + } + + return &this->public; +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs) +{ + private_proposal_t *this = (private_proposal_t*)proposal_create(protocol); + chunk_t string = {(void*)algs, strlen(algs)}; + chunk_t alg; + status_t status = SUCCESS; + + eat_whitespace(&string); + if (string.len < 1) + { + destroy(this); + return NULL; + } + + /* get all tokens, separated by '-' */ + while (extract_token(&alg, '-', &string)) + { + status |= add_string_algo(this, alg); + } + if (string.len) + { + status |= add_string_algo(this, string); + } + if (status != SUCCESS) + { + destroy(this); + return NULL; + } + + if (protocol == PROTO_AH || protocol == PROTO_ESP) + { + add_algorithm(this, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0); + } + return &this->public; +} diff --git a/src/charon/config/proposal.h b/src/charon/config/proposal.h new file mode 100644 index 000000000..abcb40999 --- /dev/null +++ b/src/charon/config/proposal.h @@ -0,0 +1,266 @@ +/** + * @file proposal.h + * + * @brief Interface of proposal_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PROPOSAL_H_ +#define PROPOSAL_H_ + +typedef enum protocol_id_t protocol_id_t; +typedef enum transform_type_t transform_type_t; +typedef enum extended_sequence_numbers_t extended_sequence_numbers_t; +typedef struct algorithm_t algorithm_t; +typedef struct proposal_t proposal_t; + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Protocol ID of a proposal. + * + * @ingroup config + */ +enum protocol_id_t { + PROTO_NONE = 0, + PROTO_IKE = 1, + PROTO_AH = 2, + PROTO_ESP = 3, +}; + +/** + * enum names for protocol_id_t + * + * @ingroup config + */ +extern enum_name_t *protocol_id_names; + + +/** + * Type of a transform, as in IKEv2 RFC 3.3.2. + * + * @ingroup config + */ +enum transform_type_t { + UNDEFINED_TRANSFORM_TYPE = 241, + ENCRYPTION_ALGORITHM = 1, + PSEUDO_RANDOM_FUNCTION = 2, + INTEGRITY_ALGORITHM = 3, + DIFFIE_HELLMAN_GROUP = 4, + EXTENDED_SEQUENCE_NUMBERS = 5 +}; + +/** + * enum names for transform_type_t. + * + * @ingroup config + */ +extern enum_name_t *transform_type_names; + + +/** + * Extended sequence numbers, as in IKEv2 RFC 3.3.2. + * + * @ingroup config + */ +enum extended_sequence_numbers_t { + NO_EXT_SEQ_NUMBERS = 0, + EXT_SEQ_NUMBERS = 1 +}; + +/** + * enum strings for extended_sequence_numbers_t. + * + * @ingroup config + */ +extern enum_name_t *extended_sequence_numbers_names; + + + +/** + * Struct used to store different kinds of algorithms. The internal + * lists of algorithms contain such structures. + */ +struct algorithm_t { + /** + * Value from an encryption_algorithm_t/integrity_algorithm_t/... + */ + u_int16_t algorithm; + + /** + * the associated key size in bits, or zero if not needed + */ + u_int16_t key_size; +}; + +/** + * @brief Stores a set of algorithms used for an SA. + * + * A proposal stores algorithms for a specific + * protocol. It can store algorithms for one protocol. + * Proposals with multiple protocols are not supported, + * as it's not specified in RFC4301 anymore. + * + * @b Constructors: + * - proposal_create() + * + * @ingroup config + */ +struct proposal_t { + + /** + * @brief Add an algorithm to the proposal. + * + * The algorithms are stored by priority, first added + * is the most preferred. + * Key size is only needed for encryption algorithms + * with variable key size (such as AES). Must be set + * to zero if key size is not specified. + * The alg parameter accepts encryption_algorithm_t, + * integrity_algorithm_t, dh_group_number_t and + * extended_sequence_numbers_t. + * + * @param this calling object + * @param type kind of algorithm + * @param alg identifier for algorithm + * @param key_size key size to use + */ + void (*add_algorithm) (proposal_t *this, transform_type_t type, u_int16_t alg, size_t key_size); + + /** + * @brief Get an iterator over algorithms for a specifc algo type. + * + * @param this calling object + * @param type kind of algorithm + * @return iterator over algorithm_t's + */ + iterator_t *(*create_algorithm_iterator) (proposal_t *this, transform_type_t type); + + /** + * @brief Get the algorithm for a type to use. + * + * If there are multiple algorithms, only the first is returned. + * Result is still owned by proposal, do not modify! + * + * @param this calling object + * @param type kind of algorithm + * @param[out] algo pointer which receives algorithm and key size + * @return TRUE if algorithm of this kind available + */ + bool (*get_algorithm) (proposal_t *this, transform_type_t type, algorithm_t** algo); + + /** + * @brief Compare two proposal, and select a matching subset. + * + * If the proposals are for the same protocols (AH/ESP), they are + * compared. If they have at least one algorithm of each type + * in common, a resulting proposal of this kind is created. + * + * @param this calling object + * @param other proposal to compair agains + * @return + * - selected proposal, if possible + * - NULL, if proposals don't match + */ + proposal_t *(*select) (proposal_t *this, proposal_t *other); + + /** + * @brief Get the protocol ID of the proposal. + * + * @param this calling object + * @return protocol of the proposal + */ + protocol_id_t (*get_protocol) (proposal_t *this); + + /** + * @brief Get the SPI of the proposal. + * + * @param this calling object + * @return spi for proto + */ + u_int64_t (*get_spi) (proposal_t *this); + + /** + * @brief Set the SPI of the proposal. + * + * @param this calling object + * @param spi spi to set for proto + */ + void (*set_spi) (proposal_t *this, u_int64_t spi); + + /** + * @brief Clone a proposal. + * + * @param this proposal to clone + * @return clone of it + */ + proposal_t *(*clone) (proposal_t *this); + + /** + * @brief Destroys the proposal object. + * + * @param this calling object + */ + void (*destroy) (proposal_t *this); +}; + +/** + * @brief Create a child proposal for AH, ESP or IKE. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create(protocol_id_t protocol); + +/** + * @brief Create a default proposal if nothing further specified. + * + * @param protocol protocol, such as PROTO_ESP + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create_default(protocol_id_t protocol); + +/** + * @brief Create a proposal from a string identifying the algorithms. + * + * The string is in the same form as a in the ipsec.conf file. + * E.g.: aes128-sha2_256-modp2048 + * 3des-md5 + * An additional '!' at the end of the string forces this proposal, + * without it the peer may choose another algorithm we support. + * + * @param protocol protocol, such as PROTO_ESP + * @param algs algorithms as string + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create_from_string(protocol_id_t protocol, const char *algs); + +#endif /* PROPOSAL_H_ */ diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c new file mode 100644 index 000000000..2fb012e16 --- /dev/null +++ b/src/charon/config/traffic_selector.c @@ -0,0 +1,795 @@ +/** + * @file traffic_selector.c + * + * @brief Implementation of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "traffic_selector.h" + +#include +#include +#include + +ENUM(ts_type_name, TS_IPV4_ADDR_RANGE, TS_IPV6_ADDR_RANGE, + "TS_IPV4_ADDR_RANGE", + "TS_IPV6_ADDR_RANGE", +); + +typedef struct private_traffic_selector_t private_traffic_selector_t; + +/** + * Private data of an traffic_selector_t object + */ +struct private_traffic_selector_t { + + /** + * Public part + */ + traffic_selector_t public; + + /** + * Type of address + */ + ts_type_t type; + + /** + * IP protocol (UDP, TCP, ICMP, ...) + */ + u_int8_t protocol; + + /** + * narrow this traffic selector to hosts external ip + * if set, from and to have no meaning until set_address() is called + */ + bool dynamic; + + /** + * begin of address range, network order + */ + union { + /** dummy char for common address manipulation */ + char from[0]; + /** IPv4 address */ + u_int32_t from4[1]; + /** IPv6 address */ + u_int32_t from6[4]; + }; + + /** + * end of address range, network order + */ + union { + /** dummy char for common address manipulation */ + char to[0]; + /** IPv4 address */ + u_int32_t to4[1]; + /** IPv6 address */ + u_int32_t to6[4]; + }; + + /** + * begin of port range + */ + u_int16_t from_port; + + /** + * end of port range + */ + u_int16_t to_port; +}; + +/** + * calculate to "to"-address for the "from" address and a subnet size + */ +static void calc_range(private_traffic_selector_t *this, u_int8_t netbits) +{ + int byte; + size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + /* go through the from address, starting at the tail. While we + * have not processed the bits belonging to the host, set them to 1 on + * the to address. If we reach the bits for the net, copy them from "from". */ + for (byte = size - 1; byte >=0; byte--) + { + u_char mask = 0x00; + int shift; + + shift = (byte+1) * 8 - netbits; + if (shift > 0) + { + mask = 1 << shift; + if (mask != 0xFF) + { + mask--; + } + } + this->to[byte] = this->from[byte] | mask; + } +} + +/** + * calculate to subnet size from "to"- and "from"-address + */ +static u_int8_t calc_netbits(private_traffic_selector_t *this) +{ + int byte, bit; + size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + /* go trough all bits of the addresses, begging in the front. + * As longer as they equal, the subnet gets larger */ + for (byte = 0; byte < size; byte++) + { + for (bit = 7; bit >= 0; bit--) + { + if ((1<from[byte]) != (1<to[byte])) + { + return ((7 - bit) + (byte * 8)); + } + } + } + /* single host, netmask is 32/128 */ + return (size * 8); +} + +/** + * internal generic constructor + */ +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts_type_t type, u_int16_t from_port, u_int16_t to_port); + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_traffic_selector_t *this = *((private_traffic_selector_t**)(args[0])); + char addr_str[INET6_ADDRSTRLEN] = ""; + char *serv_proto = NULL; + u_int8_t mask; + bool has_proto; + bool has_ports; + size_t written = 0; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + if (this->type == TS_IPV4_ADDR_RANGE) + { + inet_ntop(AF_INET, &this->from4, addr_str, sizeof(addr_str)); + } + else + { + inet_ntop(AF_INET6, &this->from6, addr_str, sizeof(addr_str)); + } + mask = calc_netbits(this); + + written += fprintf(stream, "%s/%d", addr_str, mask); + + /* check if we have protocol and/or port selectors */ + has_proto = this->protocol != 0; + has_ports = !(this->from_port == 0 && this->to_port == 0xFFFF); + + if (!has_proto && !has_ports) + { + return written; + } + + written += fprintf(stream, "["); + + /* build protocol string */ + if (has_proto) + { + struct protoent *proto = getprotobynumber(this->protocol); + + if (proto) + { + written += fprintf(stream, "%s", proto->p_name); + serv_proto = proto->p_name; + } + else + { + written += fprintf(stream, "%d", this->protocol); + } + } + + if (has_proto && has_ports) + { + written += fprintf(stream, "/"); + } + + /* build port string */ + if (has_ports) + { + if (this->from_port == this->to_port) + { + struct servent *serv = getservbyport(htons(this->from_port), serv_proto); + + if (serv) + { + written += fprintf(stream, "%s", serv->s_name); + } + else + { + written += fprintf(stream, "%d", this->from_port); + } + } + else + { + written += fprintf(stream, "%d-%d", this->from_port, this->to_port); + } + } + + written += fprintf(stream, "]"); + + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_TRAFFIC_SELECTOR, print, arginfo_ptr); +} + +/** + * implements traffic_selector_t.get_subset + */ +static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type == other->type && (this->protocol == other->protocol || + this->protocol == 0 || other->protocol == 0)) + { + u_int16_t from_port, to_port; + u_char *from, *to; + u_int8_t protocol; + size_t size; + private_traffic_selector_t *new_ts; + + /* calculate the maximum port range allowed for both */ + from_port = max(this->from_port, other->from_port); + to_port = min(this->to_port, other->to_port); + if (from_port > to_port) + { + return NULL; + } + /* select protocol, which is not zero */ + protocol = max(this->protocol, other->protocol); + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + size = sizeof(this->from4); + break; + case TS_IPV6_ADDR_RANGE: + size = sizeof(this->from6); + break; + default: + return NULL; + } + + /* get higher from-address */ + if (memcmp(this->from, other->from, size) > 0) + { + from = this->from; + } + else + { + from = other->from; + } + /* get lower to-address */ + if (memcmp(this->to, other->to, size) > 0) + { + to = other->to; + } + else + { + to = this->to; + } + /* if "from" > "to", we don't have a match */ + if (memcmp(from, to, size) > 0) + { + return NULL; + } + + /* we have a match in protocol, port, and address: return it... */ + new_ts = traffic_selector_create(protocol, this->type, from_port, to_port); + new_ts->type = this->type; + memcpy(new_ts->from, from, size); + memcpy(new_ts->to, to, size); + + return &new_ts->public; + } + return NULL; +} + +/** + * implements traffic_selector_t.equals + */ +static bool equals(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type != other->type) + { + return FALSE; + } + if (!(this->from_port == other->from_port && + this->to_port == other->to_port && + this->protocol == other->protocol)) + { + return FALSE; + } + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + if (memeq(this->from4, other->from4, sizeof(this->from4))) + { + return TRUE; + } + break; + case TS_IPV6_ADDR_RANGE: + if (memeq(this->from6, other->from6, sizeof(this->from6))) + { + return TRUE; + } + break; + default: + break; + } + return FALSE; +} + +/** + * Implements traffic_selector_t.get_from_address. + */ +static chunk_t get_from_address(private_traffic_selector_t *this) +{ + chunk_t from = chunk_empty; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + from.len = sizeof(this->from4); + from.ptr = malloc(from.len); + memcpy(from.ptr, this->from4, from.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + from.len = sizeof(this->from6); + from.ptr = malloc(from.len); + memcpy(from.ptr, this->from6, from.len); + break; + } + } + return from; +} + +/** + * Implements traffic_selector_t.get_to_address. + */ +static chunk_t get_to_address(private_traffic_selector_t *this) +{ + chunk_t to = chunk_empty; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + to.len = sizeof(this->to4); + to.ptr = malloc(to.len); + memcpy(to.ptr, this->to4, to.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + to.len = sizeof(this->to6); + to.ptr = malloc(to.len); + memcpy(to.ptr, this->to6, to.len); + break; + } + } + return to; +} + +/** + * Implements traffic_selector_t.get_from_port. + */ +static u_int16_t get_from_port(private_traffic_selector_t *this) +{ + return this->from_port; +} + +/** + * Implements traffic_selector_t.get_to_port. + */ +static u_int16_t get_to_port(private_traffic_selector_t *this) +{ + return this->to_port; +} + +/** + * Implements traffic_selector_t.get_type. + */ +static ts_type_t get_type(private_traffic_selector_t *this) +{ + return this->type; +} + +/** + * Implements traffic_selector_t.get_protocol. + */ +static u_int8_t get_protocol(private_traffic_selector_t *this) +{ + return this->protocol; +} + +/** + * Implements traffic_selector_t.is_host. + */ +static bool is_host(private_traffic_selector_t *this, host_t *host) +{ + if (this->dynamic) + { + return TRUE; + } + + if (host) + { + chunk_t addr; + int family = host->get_family(host); + + if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) || + (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)) + { + addr = host->get_address(host); + if (memeq(addr.ptr, this->from, addr.len) && + memeq(addr.ptr, this->to, addr.len)) + { + return TRUE; + } + } + } + else + { + size_t length = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + if (memeq(this->from, this->to, length)) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Implements traffic_selector_t.set_address. + */ +static void set_address(private_traffic_selector_t *this, host_t *host) +{ + if (this->dynamic) + { + this->type = host->get_family(host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; + + chunk_t from = host->get_address(host); + memcpy(this->from, from.ptr, from.len); + memcpy(this->to, from.ptr, from.len); + } +} + +/** + * Implements traffic_selector_t.is_contained_in. + */ +static bool is_contained_in(private_traffic_selector_t *this, + private_traffic_selector_t *other) +{ + private_traffic_selector_t *subset; + bool contained_in = FALSE; + + subset = (private_traffic_selector_t*)get_subset(this, other); + + if (subset) + { + if (equals(subset, this)) + { + contained_in = TRUE; + } + free(subset); + } + return contained_in; +} + +/** + * Implements traffic_selector_t.includes. + */ +static bool includes(private_traffic_selector_t *this, host_t *host) +{ + chunk_t addr; + int family = host->get_family(host); + + if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) || + (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE)) + { + addr = host->get_address(host); + + return memcmp(this->from, addr.ptr, addr.len) <= 0 && + memcmp(this->to, addr.ptr, addr.len) >= 0; + } + + return FALSE; +} + +/** + * Implements traffic_selector_t.clone. + */ +static traffic_selector_t *clone_(private_traffic_selector_t *this) +{ + private_traffic_selector_t *clone; + + clone = traffic_selector_create(this->protocol, this->type, + this->from_port, this->to_port); + + clone->dynamic = this->dynamic; + switch (clone->type) + { + case TS_IPV4_ADDR_RANGE: + { + memcpy(clone->from4, this->from4, sizeof(this->from4)); + memcpy(clone->to4, this->to4, sizeof(this->to4)); + return &clone->public; + } + case TS_IPV6_ADDR_RANGE: + { + memcpy(clone->from6, this->from6, sizeof(this->from6)); + memcpy(clone->to6, this->to6, sizeof(this->to6)); + return &clone->public; + } + default: + { + /* unreachable */ + return &clone->public; + } + } +} + +/** + * Implements traffic_selector_t.destroy. + */ +static void destroy(private_traffic_selector_t *this) +{ + free(this); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, + ts_type_t type, + chunk_t from, u_int16_t from_port, + chunk_t to, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (from.len != 4 || to.len != 4) + { + free(this); + return NULL; + } + memcpy(this->from4, from.ptr, from.len); + memcpy(this->to4, to.ptr, to.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + if (from.len != 16 || to.len != 16) + { + free(this); + return NULL; + } + memcpy(this->from6, from.ptr, from.len); + memcpy(this->to6, to.ptr, to.len); + break; + } + default: + { + free(this); + return NULL; + } + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_subnet(host_t *net, + u_int8_t netbits, u_int8_t protocol, u_int16_t port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, 0, 0, 65535); + + switch (net->get_family(net)) + { + case AF_INET: + { + chunk_t from; + + this->type = TS_IPV4_ADDR_RANGE; + from = net->get_address(net); + memcpy(this->from4, from.ptr, from.len); + if (this->from4[0] == 0) + { + /* use /0 for 0.0.0.0 */ + this->to4[0] = ~0; + } + else + { + calc_range(this, netbits); + } + break; + } + case AF_INET6: + { + chunk_t from; + + this->type = TS_IPV6_ADDR_RANGE; + from = net->get_address(net); + memcpy(this->from6, from.ptr, from.len); + if (this->from6[0] == 0 && this->from6[1] == 0 && + this->from6[2] == 0 && this->from6[3] == 0) + { + /* use /0 for ::0 */ + this->to6[0] = ~0; + this->to6[1] = ~0; + this->to6[2] = ~0; + this->to6[3] = ~0; + } + else + { + calc_range(this, netbits); + } + break; + } + default: + { + free(this); + return NULL; + } + } + if (port) + { + this->from_port = port; + this->to_port = port; + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_from_string( + u_int8_t protocol, ts_type_t type, + char *from_addr, u_int16_t from_port, + char *to_addr, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + this->type = type; + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (inet_pton(AF_INET, from_addr, (struct in_addr*)this->from4) < 0) + { + free(this); + return NULL; + } + if (inet_pton(AF_INET, to_addr, (struct in_addr*)this->to4) < 0) + { + free(this); + return NULL; + } + break; + } + case TS_IPV6_ADDR_RANGE: + { + if (inet_pton(AF_INET6, from_addr, (struct in6_addr*)this->from6) < 0) + { + free(this); + return NULL; + } + if (inet_pton(AF_INET6, to_addr, (struct in6_addr*)this->to6) < 0) + { + free(this); + return NULL; + } + break; + } + } + return (&this->public); +} + +/* + * see header + */ +traffic_selector_t *traffic_selector_create_dynamic( + u_int8_t protocol, ts_type_t type, + u_int16_t from_port, u_int16_t to_port) +{ + private_traffic_selector_t *this = traffic_selector_create(protocol, type, + from_port, to_port); + + memset(this->from6, 0, sizeof(this->from6)); + memset(this->to6, 0xFF, sizeof(this->to6)); + + this->dynamic = TRUE; + + return &this->public; +} + +/* + * see declaration + */ +static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, + ts_type_t type, u_int16_t from_port, u_int16_t to_port) +{ + private_traffic_selector_t *this = malloc_thing(private_traffic_selector_t); + + /* public functions */ + this->public.get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset; + this->public.equals = (bool(*)(traffic_selector_t*,traffic_selector_t*))equals; + this->public.get_from_address = (chunk_t(*)(traffic_selector_t*))get_from_address; + this->public.get_to_address = (chunk_t(*)(traffic_selector_t*))get_to_address; + this->public.get_from_port = (u_int16_t(*)(traffic_selector_t*))get_from_port; + this->public.get_to_port = (u_int16_t(*)(traffic_selector_t*))get_to_port; + this->public.get_type = (ts_type_t(*)(traffic_selector_t*))get_type; + this->public.get_protocol = (u_int8_t(*)(traffic_selector_t*))get_protocol; + this->public.is_host = (bool(*)(traffic_selector_t*,host_t*))is_host; + this->public.is_contained_in = (bool(*)(traffic_selector_t*,traffic_selector_t*))is_contained_in; + this->public.includes = (bool(*)(traffic_selector_t*,host_t*))includes; + this->public.set_address = (void(*)(traffic_selector_t*,host_t*))set_address; + this->public.clone = (traffic_selector_t*(*)(traffic_selector_t*))clone_; + this->public.destroy = (void(*)(traffic_selector_t*))destroy; + + this->from_port = from_port; + this->to_port = to_port; + this->protocol = protocol; + this->type = type; + this->dynamic = FALSE; + + return this; +} + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/config/traffic_selector.h b/src/charon/config/traffic_selector.h new file mode 100644 index 000000000..0e798fc6a --- /dev/null +++ b/src/charon/config/traffic_selector.h @@ -0,0 +1,312 @@ +/** + * @file traffic_selector.h + * + * @brief Interface of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef TRAFFIC_SELECTOR_H_ +#define TRAFFIC_SELECTOR_H_ + +typedef enum ts_type_t ts_type_t; +typedef struct traffic_selector_t traffic_selector_t; + +#include +#include + +/** + * Traffic selector types. + * + * @ingroup config + */ +enum ts_type_t { + + /** + * A range of IPv4 addresses, represented by two four (4) octet + * values. The first value is the beginning IPv4 address + * (inclusive) and the second value is the ending IPv4 address + * (inclusive). All addresses falling between the two specified + * addresses are considered to be within the list. + */ + TS_IPV4_ADDR_RANGE = 7, + + /** + * A range of IPv6 addresses, represented by two sixteen (16) + * octet values. The first value is the beginning IPv6 address + * (inclusive) and the second value is the ending IPv6 address + * (inclusive). All addresses falling between the two specified + * addresses are considered to be within the list. + */ + TS_IPV6_ADDR_RANGE = 8 +}; + +/** + * enum names for ts_type_t + */ +extern enum_name_t *ts_type_name; + +/** + * @brief Object representing a traffic selector entry. + * + * A traffic selector defines an range of addresses + * and a range of ports. IPv6 is not fully supported yet. + * + * @b Constructors: + * - traffic_selector_create_from_bytes() + * - traffic_selector_create_from_string() + * + * @todo Add IPv6 support + * + * @ingroup config + */ +struct traffic_selector_t { + + /** + * @brief Compare two traffic selectors, and create a new one + * which is the largest subset of both (subnet & port). + * + * Resulting traffic_selector is newly created and must be destroyed. + * + * @param this first to compare + * @param other second to compare + * @return + * - created subset of them + * - or NULL if no match between this and other + */ + traffic_selector_t *(*get_subset) (traffic_selector_t *this, + traffic_selector_t *other); + + /** + * @brief Clone a traffic selector. + * + * @param this traffic selector to clone + * @return clone of it + */ + traffic_selector_t *(*clone) (traffic_selector_t *this); + + /** + * @brief Get starting address of this ts as a chunk. + * + * Chunk is in network order gets allocated. + * + * @param this called object + * @return chunk containing the address + */ + chunk_t (*get_from_address) (traffic_selector_t *this); + + /** + * @brief Get ending address of this ts as a chunk. + * + * Chunk is in network order gets allocated. + * + * @param this called object + * @return chunk containing the address + */ + chunk_t (*get_to_address) (traffic_selector_t *this); + + /** + * @brief Get starting port of this ts. + * + * Port is in host order, since the parser converts it. + * Size depends on protocol. + * + * @param this called object + * @return port + */ + u_int16_t (*get_from_port) (traffic_selector_t *this); + + /** + * @brief Get ending port of this ts. + * + * Port is in host order, since the parser converts it. + * Size depends on protocol. + * + * @param this called object + * @return port + */ + u_int16_t (*get_to_port) (traffic_selector_t *this); + + /** + * @brief Get the type of the traffic selector. + * + * @param this called object + * @return ts_type_t specifying the type + */ + ts_type_t (*get_type) (traffic_selector_t *this); + + /** + * @brief Get the protocol id of this ts. + * + * @param this called object + * @return protocol id + */ + u_int8_t (*get_protocol) (traffic_selector_t *this); + + /** + * @brief Check if the traffic selector is for a single host. + * + * Traffic selector may describe the end of *-to-host tunnel. In this + * case, the address range is a single address equal to the hosts + * peer address. + * If host is NULL, the traffic selector is checked if it is a single host, + * but not a specific one. + * + * @param this called object + * @param host host_t specifying the address range + */ + bool (*is_host) (traffic_selector_t *this, host_t* host); + + /** + * @brief Update the address of a traffic selector. + * + * Update the address range of a traffic selector, if it is + * constructed with the traffic_selector_create_dynamic(). + * + * @param this called object + * @param host host_t specifying the address + */ + void (*set_address) (traffic_selector_t *this, host_t* host); + + /** + * @brief Compare two traffic selectors for equality. + * + * @param this first to compare + * @param other second to compare with first + * @return pointer to a string. + */ + bool (*equals) (traffic_selector_t *this, traffic_selector_t *other); + + /** + * @brief Check if a traffic selector is contained completly in another. + * + * contains() allows to check if multiple traffic selectors are redundant. + * + * @param this ts that is contained in another + * @param other ts that contains this + * @return TRUE if other contains this completly, FALSE otherwise + */ + bool (*is_contained_in) (traffic_selector_t *this, traffic_selector_t *other); + + /** + * @brief Check if a specific host is included in the address range of + * this traffic selector. + * + * @param this called object + * @param host the host to check + */ + bool (*includes) (traffic_selector_t *this, host_t *host); + + /** + * @brief Destroys the ts object + * + * @param this called object + */ + void (*destroy) (traffic_selector_t *this); +}; + +/** + * @brief Create a new traffic selector using human readable params. + * + * @param protocol protocol for this ts, such as TCP or UDP + * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE + * @param from_addr start of address range as string + * @param from_port port number in host order + * @param to_addr end of address range as string + * @param to_port port number in host order + * @return + * - traffic_selector_t object + * - NULL if invalid address strings/protocol + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_string( + u_int8_t protocol, ts_type_t type, + char *from_addr, u_int16_t from_port, + char *to_addr, u_int16_t to_port); + +/** + * @brief Create a new traffic selector using data read from the net. + * + * There exists a mix of network and host order in the params. + * But the parser gives us this data in this format, so we + * don't have to convert twice. + * + * @param protocol protocol for this ts, such as TCP or UDP + * @param type type of following addresses, such as TS_IPV4_ADDR_RANGE + * @param from_address start of address range, network order + * @param from_port port number, host order + * @param to_address end of address range as string, network + * @param to_port port number, host order + * @return traffic_selector_t object + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_bytes( + u_int8_t protocol, ts_type_t type, + chunk_t from_address, u_int16_t from_port, + chunk_t to_address, u_int16_t to_port); + +/** + * @brief Create a new traffic selector defining a whole subnet. + * + * In most cases, definition of a traffic selector for full subnets + * is sufficient. This constructor creates a traffic selector for + * all protocols, all ports and the address range specified by the + * subnet. + * Additionally, a protocol and a port may be specified. Port ranges + * are not supported via this constructor. + * + * @param net subnet to use + * @param netbits size of the subnet, as used in e.g. 192.168.0.0/24 notation + * @return + * - traffic_selector_t object + * - NULL if address family of net not supported + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_subnet( + host_t *net, u_int8_t netbits, + u_int8_t protocol, u_int16_t port); + +/** + * @brief Create a traffic selector for host-to-host cases. + * + * For host2host or virtual IP setups, the traffic selectors gets + * created at runtime using the external/virtual IP. Using this constructor, + * a call to set_address() sets this traffic selector to the supplied host. + * + * + * @param protocol upper layer protocl to allow + * @param type family type + * @param from_port start of allowed port range + * @param to_port end of range + * @return + * - traffic_selector_t object + * - NULL if type not supported + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_dynamic( + u_int8_t protocol, ts_type_t type, + u_int16_t from_port, u_int16_t to_port); + +#endif /* TRAFFIC_SELECTOR_H_ */ + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/daemon.c b/src/charon/daemon.c new file mode 100644 index 000000000..7671aea86 --- /dev/null +++ b/src/charon/daemon.c @@ -0,0 +1,529 @@ +/** + * @file daemon.c + * + * @brief Implementation of daemon_t and main of IKEv2-Daemon. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_BACKTRACE +# include +#endif /* HAVE_BACKTRACE */ + +#include "daemon.h" + +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_daemon_t private_daemon_t; + +/** + * Private additions to daemon_t, contains threads and internal functions. + */ +struct private_daemon_t { + /** + * Public members of daemon_t. + */ + daemon_t public; + + /** + * Signal set used for signal handling. + */ + sigset_t signal_set; + + /** + * The thread_id of main-thread. + */ + pthread_t main_thread_id; +}; + +/** + * One and only instance of the daemon. + */ +daemon_t *charon; + +/** + * hook in library for debugging messages + */ +extern void (*dbg) (int level, char *fmt, ...); + +/** + * Logging hook for library logs, spreads debug message over bus + */ +static void dbg_bus(int level, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + charon->bus->vsignal(charon->bus, DBG_LIB, level, fmt, args); + va_end(args); +} + +/** + * Logging hook for library logs, using stderr output + */ +static void dbg_stderr(int level, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "00[LIB] "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); +} + +/** + * Run the daemon and handle unix signals + */ +static void run(private_daemon_t *this) +{ + /* reselect signals for this thread */ + sigemptyset(&(this->signal_set)); + sigaddset(&(this->signal_set), SIGINT); + sigaddset(&(this->signal_set), SIGHUP); + sigaddset(&(this->signal_set), SIGTERM); + pthread_sigmask(SIG_BLOCK, &(this->signal_set), 0); + + while(TRUE) + { + int signal_number; + int error; + + error = sigwait(&(this->signal_set), &signal_number); + if(error) + { + DBG1(DBG_DMN, "error %d while waiting for a signal", error); + return; + } + switch (signal_number) + { + case SIGHUP: + { + DBG1(DBG_DMN, "signal of type SIGHUP received. Ignored"); + break; + } + case SIGINT: + { + DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down"); + return; + } + case SIGTERM: + DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down"); + return; + default: + { + DBG1(DBG_DMN, "unknown signal %d received. Ignored", signal_number); + break; + } + } + } +} + +/** + * Clean up all daemon resources + */ +static void destroy(private_daemon_t *this) +{ + /* destruction is a non trivial task, we need to follow + * a strict order to prevent threading issues! + * Kill active threads first, except the sender, as + * the killed IKE_SA want to send delete messages. + */ + /* we don't want to receive anything anymore... */ + DESTROY_IF(this->public.receiver); + /* ignore all incoming user requests */ + DESTROY_IF(this->public.stroke); + /* stop scheduing jobs */ + DESTROY_IF(this->public.scheduler); + /* stop processing jobs */ + DESTROY_IF(this->public.thread_pool); + /* shut down manager with all IKE SAs */ + DESTROY_IF(this->public.ike_sa_manager); + /* all child SAs should be down now, so kill kernel interface */ + DESTROY_IF(this->public.kernel_interface); + /* destroy other infrastructure */ + DESTROY_IF(this->public.job_queue); + DESTROY_IF(this->public.event_queue); + DESTROY_IF(this->public.configuration); + DESTROY_IF(this->public.credentials); + DESTROY_IF(this->public.connections); + DESTROY_IF(this->public.policies); + sched_yield(); + /* we hope the sender could send the outstanding deletes, but + * we shut down here at any cost */ + DESTROY_IF(this->public.sender); + DESTROY_IF(this->public.socket); + /* before destroying bus with its listeners, rehook library logs */ + dbg = dbg_stderr; + DESTROY_IF(this->public.bus); + DESTROY_IF(this->public.outlog); + DESTROY_IF(this->public.syslog); + DESTROY_IF(this->public.authlog); + free(this); +} + +/** + * Enforce daemon shutdown, with a given reason to do so. + */ +static void kill_daemon(private_daemon_t *this, char *reason) +{ + /* we send SIGTERM, so the daemon can cleanly shut down */ + DBG1(DBG_DMN, "killing daemon: %s", reason); + if (this->main_thread_id == pthread_self()) + { + /* initialization failed, terminate daemon */ + destroy(this); + unlink(PID_FILE); + exit(-1); + } + else + { + DBG1(DBG_DMN, "sending SIGTERM to ourself"); + raise(SIGTERM); + /* thread must die, since he produced a ciritcal failure and can't continue */ + pthread_exit(NULL); + } +} + +/** + * Initialize the daemon, optional with a strict crl policy + */ +static void initialize(private_daemon_t *this, bool strict, bool syslog, + level_t levels[]) +{ + credential_store_t* credentials; + signal_t signal; + + /* for uncritical pseudo random numbers */ + srandom(time(NULL) + getpid()); + + /* setup bus and it's listeners first to enable log output */ + this->public.bus = bus_create(); + this->public.outlog = file_logger_create(stdout); + this->public.syslog = sys_logger_create(LOG_DAEMON); + this->public.authlog = sys_logger_create(LOG_AUTHPRIV); + this->public.bus->add_listener(this->public.bus, &this->public.syslog->listener); + this->public.bus->add_listener(this->public.bus, &this->public.outlog->listener); + this->public.bus->add_listener(this->public.bus, &this->public.authlog->listener); + this->public.authlog->set_level(this->public.authlog, SIG_ANY, LEVEL_AUDIT); + /* set up hook to log dbg message in library via charons message bus */ + dbg = dbg_bus; + + /* apply loglevels */ + for (signal = 0; signal < DBG_MAX; signal++) + { + if (syslog) + { + this->public.syslog->set_level(this->public.syslog, + signal, levels[signal]); + } + else + { + this->public.outlog->set_level(this->public.outlog, + signal, levels[signal]); + } + } + + DBG1(DBG_DMN, "starting charon (strongSwan Version %s)", VERSION); + + this->public.configuration = configuration_create(); + this->public.socket = socket_create(IKEV2_UDP_PORT, IKEV2_NATT_PORT); + this->public.ike_sa_manager = ike_sa_manager_create(); + this->public.job_queue = job_queue_create(); + this->public.event_queue = event_queue_create(); + this->public.connections = (connection_store_t*)local_connection_store_create(); + this->public.policies = (policy_store_t*)local_policy_store_create(); + this->public.credentials = (credential_store_t*)local_credential_store_create(strict); + + /* initialize fetcher_t class */ + fetcher_initialize(); + + /* load secrets, ca certificates and crls */ + credentials = this->public.credentials; + credentials->load_ca_certificates(credentials); + credentials->load_ocsp_certificates(credentials); + credentials->load_crls(credentials); + credentials->load_secrets(credentials); + + /* start building threads, we are multi-threaded NOW */ + this->public.stroke = stroke_create(); + this->public.sender = sender_create(); + this->public.receiver = receiver_create(); + this->public.scheduler = scheduler_create(); + this->public.kernel_interface = kernel_interface_create(); + this->public.thread_pool = thread_pool_create(NUMBER_OF_WORKING_THREADS); +} + +/** + * Handle SIGSEGV/SIGILL signals raised by threads + */ +void signal_handler(int signal) +{ +#ifdef HAVE_BACKTRACE + void *array[20]; + size_t size; + char **strings; + size_t i; + + size = backtrace(array, 20); + strings = backtrace_symbols(array, size); + + DBG1(DBG_DMN, "thread %u received %s. Dumping %d frames from stack:", + pthread_self(), signal == SIGSEGV ? "SIGSEGV" : "SIGILL", size); + + for (i = 0; i < size; i++) + { + DBG1(DBG_DMN, " %s", strings[i]); + } + free (strings); +#else /* !HAVE_BACKTRACE */ + DBG1(DBG_DMN, "thread %u received %s", + pthread_self(), signal == SIGSEGV ? "SIGSEGV" : "SIGILL"); +#endif /* HAVE_BACKTRACE */ + DBG1(DBG_DMN, "killing ourself hard after SIGSEGV"); + raise(SIGKILL); +} + +/** + * Create the daemon. + */ +private_daemon_t *daemon_create(void) +{ + private_daemon_t *this = malloc_thing(private_daemon_t); + struct sigaction action; + + /* assign methods */ + this->public.kill = (void (*) (daemon_t*,char*))kill_daemon; + + /* NULL members for clean destruction */ + this->public.socket = NULL; + this->public.ike_sa_manager = NULL; + this->public.job_queue = NULL; + this->public.event_queue = NULL; + this->public.configuration = NULL; + this->public.credentials = NULL; + this->public.connections = NULL; + this->public.policies = NULL; + this->public.sender= NULL; + this->public.receiver = NULL; + this->public.scheduler = NULL; + this->public.kernel_interface = NULL; + this->public.thread_pool = NULL; + this->public.stroke = NULL; + this->public.bus = NULL; + this->public.outlog = NULL; + this->public.syslog = NULL; + this->public.authlog = NULL; + + this->main_thread_id = pthread_self(); + + /* setup signal handling for all threads */ + sigemptyset(&(this->signal_set)); + sigaddset(&(this->signal_set), SIGSEGV); + sigaddset(&(this->signal_set), SIGINT); + sigaddset(&(this->signal_set), SIGHUP); + sigaddset(&(this->signal_set), SIGTERM); + pthread_sigmask(SIG_BLOCK, &(this->signal_set), 0); + + /* setup SIGSEGV handler for all threads */ + action.sa_handler = signal_handler; + action.sa_mask = this->signal_set; + action.sa_flags = 0; + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGILL, &action, NULL); + return this; +} + +/** + * print command line usage and exit + */ +static void usage(const char *msg) +{ + if (msg != NULL && *msg != '\0') + { + fprintf(stderr, "%s\n", msg); + } + fprintf(stderr, "Usage: charon\n" + " [--help]\n" + " [--version]\n" + " [--strictcrlpolicy]\n" + " [--cachecrls]\n" + " [--crlcheckinterval ]\n" + " [--eapdir ]\n" + " [--use-syslog]\n" + " [--debug- ]\n" + " : log context type (dmn|mgr|ike|chd|job|cfg|knl|net|enc|lib)\n" + " : log verbosity (-1 = silent, 0 = audit, 1 = control,\n" + " 2 = controlmore, 3 = raw, 4 = private)\n" + "\n" + ); + exit(msg == NULL? 0 : 1); +} + +/** + * Main function, manages the daemon. + */ +int main(int argc, char *argv[]) +{ + u_int crl_check_interval = 0; + bool strict_crl_policy = FALSE; + bool cache_crls = FALSE; + bool use_syslog = FALSE; + char *eapdir = IPSEC_EAPDIR; + + private_daemon_t *private_charon; + FILE *pid_file; + struct stat stb; + linked_list_t *list; + host_t *host; + level_t levels[DBG_MAX]; + int signal; + + /* use CTRL loglevel for default */ + for (signal = 0; signal < DBG_MAX; signal++) + { + levels[signal] = LEVEL_CTRL; + } + + /* handle arguments */ + for (;;) + { + struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "use-syslog", no_argument, NULL, 'l' }, + { "strictcrlpolicy", no_argument, NULL, 'r' }, + { "cachecrls", no_argument, NULL, 'C' }, + { "crlcheckinterval", required_argument, NULL, 'x' }, + { "eapdir", required_argument, NULL, 'e' }, + /* TODO: handle "debug-all" */ + { "debug-dmn", required_argument, &signal, DBG_DMN }, + { "debug-mgr", required_argument, &signal, DBG_MGR }, + { "debug-ike", required_argument, &signal, DBG_IKE }, + { "debug-chd", required_argument, &signal, DBG_CHD }, + { "debug-job", required_argument, &signal, DBG_JOB }, + { "debug-cfg", required_argument, &signal, DBG_CFG }, + { "debug-knl", required_argument, &signal, DBG_KNL }, + { "debug-net", required_argument, &signal, DBG_NET }, + { "debug-enc", required_argument, &signal, DBG_ENC }, + { "debug-lib", required_argument, &signal, DBG_LIB }, + { 0,0,0,0 } + }; + + int c = getopt_long(argc, argv, "", long_opts, NULL); + switch (c) + { + case EOF: + break; + case 'h': + usage(NULL); + break; + case 'v': + printf("Linux strongSwan %s\n", VERSION); + exit(0); + case 'l': + use_syslog = TRUE; + continue; + case 'r': + strict_crl_policy = TRUE; + continue; + case 'C': + cache_crls = TRUE; + continue; + case 'x': + crl_check_interval = atoi(optarg); + continue; + case 'e': + eapdir = optarg; + continue; + case 0: + /* option is in signal */ + levels[signal] = atoi(optarg); + continue; + default: + usage(""); + break; + } + break; + } + + private_charon = daemon_create(); + charon = (daemon_t*)private_charon; + + /* initialize daemon */ + initialize(private_charon, strict_crl_policy, use_syslog, levels); + + /* load pluggable EAP modules */ + eap_method_load(eapdir); + + /* set cache_crls and crl_check_interval options */ + ca_info_set_options(cache_crls, crl_check_interval); + + /* check/setup PID file */ + if (stat(PID_FILE, &stb) == 0) + { + DBG1(DBG_DMN, "charon already running (\""PID_FILE"\" exists)"); + destroy(private_charon); + exit(-1); + } + pid_file = fopen(PID_FILE, "w"); + if (pid_file) + { + fprintf(pid_file, "%d\n", getpid()); + fclose(pid_file); + } + + /* log socket info */ + list = charon->kernel_interface->create_address_list(charon->kernel_interface); + DBG1(DBG_NET, "listening on %d addresses:", list->get_count(list)); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + DBG1(DBG_NET, " %H", host); + host->destroy(host); + } + list->destroy(list); + + /* run daemon */ + run(private_charon); + + eap_method_unload(); + fetcher_finalize(); + /* normal termination, cleanup and exit */ + destroy(private_charon); + unlink(PID_FILE); + + return 0; +} diff --git a/src/charon/daemon.h b/src/charon/daemon.h new file mode 100644 index 000000000..420262474 --- /dev/null +++ b/src/charon/daemon.h @@ -0,0 +1,403 @@ +/** + * @file daemon.h + * + * @brief Interface of daemon_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DAEMON_H_ +#define DAEMON_H_ + +typedef struct daemon_t daemon_t; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @defgroup charon charon + * + * @brief IKEv2 keying daemon. + * + * @section Architecture + * + * All IKEv2 stuff is handled in charon. It uses a newer and more flexible + * architecture than pluto. Charon uses a thread-pool, which allows parallel + * execution SA-management. Beside the thread-pool, there are some special purpose + * threads which do their job for the common health of the daemon. + @verbatim + +------+ + | E Q | + | v u |---+ +------+ +------+ + | e e | | | | | IKE- | + | n u | +-----------+ | |--| SA | + | t e | | | | I M | +------+ + +------------+ | - | | Scheduler | | K a | + | receiver | +------+ | | | E n | +------+ + +----+-------+ +-----------+ | - a | | IKE- | + | | +------+ | | S g |--| SA | + +-------+--+ +-----| J Q |---+ +------------+ | A e | +------+ + -| socket | | o u | | | | - r | + +-------+--+ | b e | | Thread- | | | + | | - u | | Pool | | | + +----+-------+ | e |------| |---| | + | sender | +------+ +------------+ +------+ + +------------+ + + @endverbatim + * The thread-pool is the heart of the architecture. It processes jobs from a + * (fully synchronized) job-queue. Mostly, a job is associated with a specific + * IKE SA. These IKE SAs are synchronized, only one thread can work one an IKE SA. + * This makes it unnecesary to use further synchronisation methods once a IKE SA + * is checked out. The (rather complex) synchronization of IKE SAs is completely + * done in the IKE SA manager. + * The sceduler is responsible for event firing. It waits until a event in the + * (fully synchronized) event-queue is ready for processing and pushes the event + * down to the job-queue. A thread form the pool will pick it up as quick as + * possible. Every thread can queue events or jobs. Furter, an event can place a + * packet in the sender. The sender thread waits for those packets and sends + * them over the wire, via the socket. The receiver does exactly the opposite of + * the sender. It waits on the socket, reads in packets an places them on the + * job-queue for further processing by a thread from the pool. + * There are even more threads, not drawn in the upper scheme. The stroke thread + * is responsible for reading and processessing commands from another process. The + * kernel interface thread handles communication from and to the kernel via a + * netlink socket. It waits for kernel events and processes them appropriately. + */ + +/** + * @defgroup config config + * + * Classes implementing configuration related things. + * + * @ingroup charon + */ + +/** + * @defgroup encoding encoding + * + * Classes used to encode and decode IKEv2 messages. + * + * @ingroup charon + */ + + /** + * @defgroup payloads payloads + * + * Classes representing specific IKEv2 payloads. + * + * @ingroup encoding + */ + +/** + * @defgroup network network + * + * Classes for network relevant stuff. + * + * @ingroup charon + */ + +/** + * @defgroup queues queues + * + * Different kind of queues + * (thread save lists). + * + * @ingroup charon + */ + +/** + * @defgroup jobs jobs + * + * Jobs used in job queue and event queue. + * + * @ingroup queues + */ + +/** + * @defgroup sa sa + * + * Security associations for IKE and IPSec, + * and some helper classes. + * + * @ingroup charon + */ + +/** + * @defgroup tasks tasks + * + * Tasks process and build message payloads. They are used to create + * and process multiple exchanges. + * + * @ingroup sa + */ + +/** + * @defgroup authenticators authenticators + * + * Authenticator classes to prove identity of peer. + * + * @ingroup sa + */ + +/** + * @defgroup eap eap + * + * EAP authentication module interface and it's implementations. + * + * @ingroup authenticators + */ + +/** + * @defgroup threads threads + * + * Threaded classes, which will do their job alone. + * + * @ingroup charon + */ + +/** + * @defgroup bus bus + * + * Signaling bus and its listeners. + * + * @ingroup charon + */ + +/** + * Name of the daemon. + * + * @ingroup charon + */ +#define DAEMON_NAME "charon" + +/** + * @brief Number of threads in the thread pool. + * + * There are several other threads, this defines + * only the number of threads in thread_pool_t. + * + * @ingroup charon + */ +#define NUMBER_OF_WORKING_THREADS 4 + +/** + * UDP Port on which the daemon will listen for incoming traffic. + * + * @ingroup charon + */ +#define IKEV2_UDP_PORT 500 + +/** + * UDP Port to which the daemon will float to if NAT is detected. + * + * @ingroup charon + */ +#define IKEV2_NATT_PORT 4500 + +/** + * PID file, in which charon stores its process id + * + * @ingroup charon + */ +#define PID_FILE IPSEC_PIDDIR "/charon.pid" + +/** + * Configuration directory + * + * @ingroup charon + */ +#define CONFIG_DIR IPSEC_CONFDIR + +/** + * Directory of IPsec relevant files + * + * @ingroup charon + */ +#define IPSEC_D_DIR CONFIG_DIR "/ipsec.d" + +/** + * Default directory for private keys + * + * @ingroup charon + */ +#define PRIVATE_KEY_DIR IPSEC_D_DIR "/private" + +/** + * Default directory for end entity certificates + * + * @ingroup charon + */ +#define CERTIFICATE_DIR IPSEC_D_DIR "/certs" + +/** + * Default directory for trusted CA certificates + * + * @ingroup charon + */ +#define CA_CERTIFICATE_DIR IPSEC_D_DIR "/cacerts" + +/** + * Default directory for OCSP signing certificates + * + * @ingroup charon + */ +#define OCSP_CERTIFICATE_DIR IPSEC_D_DIR "/ocspcerts" + +/** + * Default directory for CRLs + * + * @ingroup charon + */ +#define CRL_DIR IPSEC_D_DIR "/crls" + +/** + * Secrets files + * + * @ingroup charon + */ +#define SECRETS_FILE CONFIG_DIR "/ipsec.secrets" + +/** + * @brief Main class of daemon, contains some globals. + * + * @ingroup charon + */ +struct daemon_t { + /** + * A socket_t instance. + */ + socket_t *socket; + + /** + * A job_queue_t instance. + */ + job_queue_t *job_queue; + + /** + * A event_queue_t instance. + */ + event_queue_t *event_queue; + + /** + * A ike_sa_manager_t instance. + */ + ike_sa_manager_t *ike_sa_manager; + + /** + * A configuration_t instance. + */ + configuration_t *configuration; + + /** + * A connection_store_t instance. + */ + connection_store_t *connections; + + /** + * A policy_store_t instance. + */ + policy_store_t *policies; + + /** + * A credential_store_t instance. + */ + credential_store_t *credentials; + + /** + * The Sender-Thread. + */ + sender_t *sender; + + /** + * The Receiver-Thread. + */ + receiver_t *receiver; + + /** + * The Scheduler-Thread. + */ + scheduler_t *scheduler; + + /** + * The Thread pool managing the worker threads. + */ + thread_pool_t *thread_pool; + + /** + * The signaling bus. + */ + bus_t *bus; + + /** + * A bus listener logging to stdout + */ + file_logger_t *outlog; + + /** + * A bus listener logging to syslog + */ + sys_logger_t *syslog; + + /** + * A bus listener logging most important events + */ + sys_logger_t *authlog; + + /** + * Kernel Interface to communicate with kernel + */ + kernel_interface_t *kernel_interface; + + /** + * IPC interface, as whack in pluto + */ + stroke_t *stroke; + + /** + * @brief Shut down the daemon. + * + * @param this the daemon to kill + * @param reason describtion why it will be killed + */ + void (*kill) (daemon_t *this, char *reason); +}; + +/** + * The one and only instance of the daemon. + */ +extern daemon_t *charon; + +#endif /*DAEMON_H_*/ diff --git a/src/charon/encoding/generator.c b/src/charon/encoding/generator.c new file mode 100644 index 000000000..efa845bb3 --- /dev/null +++ b/src/charon/encoding/generator.c @@ -0,0 +1,1063 @@ +/** + * @file generator.c + * + * @brief Implementation of generator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include + + +#include "generator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_generator_t private_generator_t; + +/** + * Private part of a generator_t object. + */ +struct private_generator_t { + /** + * Public part of a generator_t object. + */ + generator_t public; + + /** + * Generates a U_INT-Field type and writes it to buffer. + * + * @param this private_generator_t object + * @param int_type type of U_INT field (U_INT_4, U_INT_8, etc.) + * ATTRIBUTE_TYPE is also generated in this function + * @param offset offset of value in data struct + * @param generator_contexts generator_contexts_t object where the context is written or read from + * @return + * - SUCCESS + * - FAILED if allignment is wrong + */ + void (*generate_u_int_type) (private_generator_t *this,encoding_type_t int_type,u_int32_t offset); + + /** + * Get size of current buffer in bytes. + * + * @param this private_generator_t object + * @return Size of buffer in bytes + */ + size_t (*get_current_buffer_size) (private_generator_t *this); + + /** + * Get free space of current buffer in bytes. + * + * @param this private_generator_t object + * @return space in buffer in bytes + */ + size_t (*get_current_buffer_space) (private_generator_t *this); + + /** + * Get length of data in buffer (in bytes). + * + * @param this private_generator_t object + * @return length of data in bytes + */ + size_t (*get_current_data_length) (private_generator_t *this); + + /** + * Get current offset in buffer (in bytes). + * + * @param this private_generator_t object + * @return offset in bytes + */ + u_int32_t (*get_current_buffer_offset) (private_generator_t *this); + + /** + * Generates a RESERVED BIT field or a RESERVED BYTE field and writes + * it to the buffer. + * + * @param this private_generator_t object + * @param generator_contexts generator_contexts_t object where the context is written or read from + * @param bits number of bits to generate + */ + void (*generate_reserved_field) (private_generator_t *this,int bits); + + /** + * Generates a FLAG field. + * + * @param this private_generator_t object + * @param generator_contexts generator_contexts_t object where the context is written or read from + * @param offset offset of flag value in data struct + */ + void (*generate_flag) (private_generator_t *this,u_int32_t offset); + + /** + * Writes the current buffer content into a chunk_t. + * + * Memory of specific chunk_t gets allocated. + * + * @param this calling private_generator_t object + * @param data pointer of chunk_t to write to + */ + void (*write_chunk) (private_generator_t *this,chunk_t *data); + + /** + * Generates a bytestream from a chunk_t. + * + * @param this private_generator_t object + * @param offset offset of chunk_t value in data struct + */ + void (*generate_from_chunk) (private_generator_t *this,u_int32_t offset); + + /** + * Makes sure enough space is available in buffer to store amount of bits. + * + * If buffer is to small to hold the specific amount of bits it + * is increased using reallocation function of allocator. + * + * @param this calling private_generator_t object + * @param bits number of bits to make available in buffer + */ + void (*make_space_available) (private_generator_t *this,size_t bits); + + /** + * Writes a specific amount of byte into the buffer. + * + * If buffer is to small to hold the specific amount of bytes it + * is increased. + * + * @param this calling private_generator_t object + * @param bytes pointer to bytes to write + * @param number_of_bytes number of bytes to write into buffer + */ + void (*write_bytes_to_buffer) (private_generator_t *this,void * bytes,size_t number_of_bytes); + + + /** + * Writes a specific amount of byte into the buffer at a specific offset. + * + * @warning buffer size is not check to hold the data if offset is to large. + * + * @param this calling private_generator_t object + * @param bytes pointer to bytes to write + * @param number_of_bytes number of bytes to write into buffer + * @param offset offset to write the data into + */ + void (*write_bytes_to_buffer_at_offset) (private_generator_t *this,void * bytes,size_t number_of_bytes,u_int32_t offset); + + /** + * Buffer used to generate the data into. + */ + u_int8_t *buffer; + + /** + * Current write position in buffer (one byte aligned). + */ + u_int8_t *out_position; + + /** + * Position of last byte in buffer. + */ + u_int8_t *roof_position; + + /** + * Current bit writing to in current byte (between 0 and 7). + */ + size_t current_bit; + + /** + * Associated data struct to read informations from. + */ + void * data_struct; + + /* + * Last payload length position offset in the buffer. + */ + u_int32_t last_payload_length_position_offset; + + /** + * Offset of the header length field in the buffer. + */ + u_int32_t header_length_position_offset; + + /** + * Last SPI size. + */ + u_int8_t last_spi_size; + + /** + * Attribute format of the last generated transform attribute. + * + * Used to check if a variable value field is used or not for + * the transform attribute value. + */ + bool attribute_format; + + /** + * Depending on the value of attribute_format this field is used + * to hold the length of the transform attribute in bytes. + */ + u_int16_t attribute_length; +}; + +/** + * Implementation of private_generator_t.get_current_buffer_size. + */ +static size_t get_current_buffer_size (private_generator_t *this) +{ + return ((this->roof_position) - (this->buffer)); +} + +/** + * Implementation of private_generator_t.get_current_buffer_space. + */ +static size_t get_current_buffer_space (private_generator_t *this) +{ + /* we know, one byte more */ + size_t space = (this->roof_position) - (this->out_position); + return (space); +} + +/** + * Implementation of private_generator_t.get_current_data_length. + */ +static size_t get_current_data_length (private_generator_t *this) +{ + return (this->out_position - this->buffer); +} + +/** + * Implementation of private_generator_t.get_current_buffer_offset. + */ +static u_int32_t get_current_buffer_offset (private_generator_t *this) +{ + return (this->out_position - this->buffer); +} + +/** + * Implementation of private_generator_t.generate_u_int_type. + */ +static void generate_u_int_type (private_generator_t *this,encoding_type_t int_type,u_int32_t offset) +{ + size_t number_of_bits = 0; + + /* find out number of bits of each U_INT type to check for enough space + in buffer */ + switch (int_type) + { + case U_INT_4: + number_of_bits = 4; + break; + case TS_TYPE: + case U_INT_8: + number_of_bits = 8; + break; + case U_INT_16: + case CONFIGURATION_ATTRIBUTE_LENGTH: + number_of_bits = 16; + break; + case U_INT_32: + number_of_bits = 32; + break; + case U_INT_64: + number_of_bits = 64; + break; + case ATTRIBUTE_TYPE: + number_of_bits = 15; + break; + case IKE_SPI: + number_of_bits = 64; + break; + + default: + DBG1(DBG_ENC, "U_INT Type %N is not supported", + encoding_type_names, int_type); + + return; + } + /* U_INT Types of multiple then 8 bits must be aligned */ + if (((number_of_bits % 8) == 0) && (this->current_bit != 0)) + { + DBG1(DBG_ENC, "U_INT Type %N is not 8 Bit aligned", + encoding_type_names, int_type); + /* current bit has to be zero for values multiple of 8 bits */ + return; + } + + /* make sure enough space is available in buffer */ + this->make_space_available(this,number_of_bits); + /* now handle each u int type differently */ + switch (int_type) + { + case U_INT_4: + { + if (this->current_bit == 0) + { + /* highval of current byte in buffer has to be set to the new value*/ + u_int8_t high_val = *((u_int8_t *)(this->data_struct + offset)) << 4; + /* lowval in buffer is not changed */ + u_int8_t low_val = *(this->out_position) & 0x0F; + /* highval is set, low_val is not changed */ + *(this->out_position) = high_val | low_val; + DBG3(DBG_ENC, " => %d", *(this->out_position)); + /* write position is not changed, just bit position is moved */ + this->current_bit = 4; + } + else if (this->current_bit == 4) + { + /* highval in buffer is not changed */ + u_int high_val = *(this->out_position) & 0xF0; + /* lowval of current byte in buffer has to be set to the new value*/ + u_int low_val = *((u_int8_t *)(this->data_struct + offset)) & 0x0F; + *(this->out_position) = high_val | low_val; + DBG3(DBG_ENC, " => %d", *(this->out_position)); + this->out_position++; + this->current_bit = 0; + + } + else + { + DBG1(DBG_ENC, "U_INT_4 Type is not 4 Bit aligned"); + /* 4 Bit integers must have a 4 bit alignment */ + return; + }; + break; + } + case TS_TYPE: + case U_INT_8: + { + /* 8 bit values are written as they are */ + *this->out_position = *((u_int8_t *)(this->data_struct + offset)); + DBG3(DBG_ENC, " => %d", *(this->out_position)); + this->out_position++; + break; + + } + case ATTRIBUTE_TYPE: + { + /* attribute type must not change first bit uf current byte ! */ + if (this->current_bit != 1) + { + DBG1(DBG_ENC, "ATTRIBUTE FORMAT flag is not set"); + /* first bit has to be set! */ + return; + } + /* get value of attribute format flag */ + u_int8_t attribute_format_flag = *(this->out_position) & 0x80; + /* get attribute type value as 16 bit integer*/ + u_int16_t int16_val = *((u_int16_t*)(this->data_struct + offset)); + /* unset most significant bit */ + int16_val &= 0x7FFF; + if (attribute_format_flag) + { + int16_val |= 0x8000; + } + int16_val = htons(int16_val); + DBG3(DBG_ENC, " => %d", int16_val); + /* write bytes to buffer (set bit is overwritten)*/ + this->write_bytes_to_buffer(this,&int16_val,sizeof(u_int16_t)); + this->current_bit = 0; + break; + + } + case U_INT_16: + case CONFIGURATION_ATTRIBUTE_LENGTH: + { + u_int16_t int16_val = htons(*((u_int16_t*)(this->data_struct + offset))); + DBG3(DBG_ENC, " => %b", (void*)&int16_val, sizeof(int16_val)); + this->write_bytes_to_buffer(this,&int16_val,sizeof(u_int16_t)); + break; + } + case U_INT_32: + { + u_int32_t int32_val = htonl(*((u_int32_t*)(this->data_struct + offset))); + DBG3(DBG_ENC, " => %b", (void*)&int32_val, sizeof(int32_val)); + this->write_bytes_to_buffer(this,&int32_val,sizeof(u_int32_t)); + break; + } + case U_INT_64: + { + /* 64 bit integers are written as two 32 bit integers */ + u_int32_t int32_val_low = htonl(*((u_int32_t*)(this->data_struct + offset))); + u_int32_t int32_val_high = htonl(*((u_int32_t*)(this->data_struct + offset) + 1)); + DBG3(DBG_ENC, " => %b %b", + (void*)&int32_val_low, sizeof(int32_val_low), + (void*)&int32_val_high, sizeof(int32_val_high)); + /* TODO add support for big endian machines */ + this->write_bytes_to_buffer(this,&int32_val_high,sizeof(u_int32_t)); + this->write_bytes_to_buffer(this,&int32_val_low,sizeof(u_int32_t)); + break; + } + + case IKE_SPI: + { + /* 64 bit are written as they come :-) */ + this->write_bytes_to_buffer(this,(this->data_struct + offset),sizeof(u_int64_t)); + DBG3(DBG_ENC, " => %b", (void*)(this->data_struct + offset), sizeof(u_int64_t)); + break; + } + default: + { + DBG1(DBG_ENC, "U_INT Type %N is not supported", + encoding_type_names, int_type); + return; + } + } +} + +/** + * Implementation of private_generator_t.generate_reserved_field. + */ +static void generate_reserved_field(private_generator_t *this,int bits) +{ + /* only one bit or 8 bit fields are supported */ + if ((bits != 1) && (bits != 8)) + { + DBG1(DBG_ENC, "reserved field of %d bits cannot be generated", bits); + return ; + } + /* make sure enough space is available in buffer */ + this->make_space_available(this,bits); + + if (bits == 1) + { + /* one bit processing */ + u_int8_t reserved_bit = ~(1 << (7 - this->current_bit)); + *(this->out_position) = *(this->out_position) & reserved_bit; + if (this->current_bit == 0) + { + /* memory must be zero */ + *(this->out_position) = 0x00; + } + + + this->current_bit++; + if (this->current_bit >= 8) + { + this->current_bit = this->current_bit % 8; + this->out_position++; + } + } + else + { + /* one byte processing*/ + if (this->current_bit > 0) + { + DBG1(DBG_ENC, "reserved field cannot be written cause " + "alignement of current bit is %d", this->current_bit); + return; + } + *(this->out_position) = 0x00; + this->out_position++; + } +} + +/** + * Implementation of private_generator_t.generate_flag. + */ +static void generate_flag (private_generator_t *this,u_int32_t offset) +{ + /* value of current flag */ + u_int8_t flag_value; + /* position of flag in current byte */ + u_int8_t flag; + + /* if the value in the data_struct is TRUE, flag_value is set to 1, 0 otherwise */ + flag_value = (*((bool *) (this->data_struct + offset))) ? 1 : 0; + /* get flag position */ + flag = (flag_value << (7 - this->current_bit)); + + /* make sure one bit is available in buffer */ + this->make_space_available(this,1); + if (this->current_bit == 0) + { + /* memory must be zero */ + *(this->out_position) = 0x00; + } + + *(this->out_position) = *(this->out_position) | flag; + + + DBG3(DBG_ENC, " => %d", *(this->out_position)); + + this->current_bit++; + if (this->current_bit >= 8) + { + this->current_bit = this->current_bit % 8; + this->out_position++; + } +} + +/** + * Implementation of private_generator_t.generate_from_chunk. + */ +static void generate_from_chunk (private_generator_t *this,u_int32_t offset) +{ + if (this->current_bit != 0) + { + DBG1(DBG_ENC, "can not generate a chunk at Bitpos %d", this->current_bit); + return ; + } + + /* position in buffer */ + chunk_t *attribute_value = (chunk_t *)(this->data_struct + offset); + + DBG3(DBG_ENC, " => %B", attribute_value); + + /* use write_bytes_to_buffer function to do the job */ + this->write_bytes_to_buffer(this,attribute_value->ptr,attribute_value->len); +} + +/** + * Implementation of private_generator_t.make_space_available. + */ +static void make_space_available (private_generator_t *this, size_t bits) +{ + while (((this->get_current_buffer_space(this) * 8) - this->current_bit) < bits) + { + /* must increase buffer */ + size_t old_buffer_size = this->get_current_buffer_size(this); + size_t new_buffer_size = old_buffer_size + GENERATOR_DATA_BUFFER_INCREASE_VALUE; + size_t out_position_offset = ((this->out_position) - (this->buffer)); + + DBG2(DBG_ENC, "increased gen buffer from %d to %d byte", + old_buffer_size, new_buffer_size); + + /* Reallocate space for new buffer */ + this->buffer = realloc(this->buffer,new_buffer_size); + + this->out_position = (this->buffer + out_position_offset); + this->roof_position = (this->buffer + new_buffer_size); + } +} + +/** + * Implementation of private_generator_t.write_bytes_to_buffer. + */ +static void write_bytes_to_buffer (private_generator_t *this,void * bytes, size_t number_of_bytes) +{ + int i; + u_int8_t *read_position = (u_int8_t *) bytes; + + this->make_space_available(this,number_of_bytes * 8); + + for (i = 0; i < number_of_bytes; i++) + { + *(this->out_position) = *(read_position); + read_position++; + this->out_position++; + } +} + +/** + * Implementation of private_generator_t.write_bytes_to_buffer_at_offset. + */ +static void write_bytes_to_buffer_at_offset (private_generator_t *this,void * bytes,size_t number_of_bytes,u_int32_t offset) +{ + int i; + u_int8_t *read_position = (u_int8_t *) bytes; + u_int8_t *write_position; + u_int32_t free_space_after_offset = (this->get_current_buffer_size(this) - offset); + + /* check first if enough space for new data is available */ + if (number_of_bytes > free_space_after_offset) + { + this->make_space_available(this,(number_of_bytes - free_space_after_offset) * 8); + } + + write_position = this->buffer + offset; + for (i = 0; i < number_of_bytes; i++) + { + *(write_position) = *(read_position); + read_position++; + write_position++; + } +} + +/** + * Implementation of private_generator_t.write_to_chunk. + */ +static void write_to_chunk (private_generator_t *this,chunk_t *data) +{ + size_t data_length = this->get_current_data_length(this); + u_int32_t header_length_field = data_length; + + /* write length into header length field */ + if (this->header_length_position_offset > 0) + { + u_int32_t int32_val = htonl(header_length_field); + this->write_bytes_to_buffer_at_offset(this,&int32_val,sizeof(u_int32_t),this->header_length_position_offset); + } + + if (this->current_bit > 0) + data_length++; + data->ptr = malloc(data_length); + memcpy(data->ptr,this->buffer,data_length); + data->len = data_length; + + DBG3(DBG_ENC, "generated data of this generator %B", data); +} + +/** + * Implementation of private_generator_t.generate_payload. + */ +static void generate_payload (private_generator_t *this,payload_t *payload) +{ + int i; + this->data_struct = payload; + size_t rule_count; + encoding_rule_t *rules; + payload_type_t payload_type; + u_int8_t *payload_start; + + /* get payload type */ + payload_type = payload->get_type(payload); + /* spi size has to get reseted */ + this->last_spi_size = 0; + + payload_start = this->out_position; + + DBG2(DBG_ENC, "generating payload of type %N", + payload_type_names, payload_type); + + /* each payload has its own encoding rules */ + payload->get_encoding_rules(payload,&rules,&rule_count); + + for (i = 0; i < rule_count;i++) + { + DBG2(DBG_ENC, " generating rule %d %N", + i, encoding_type_names, rules[i].type); + switch (rules[i].type) + { + /* all u int values, IKE_SPI,TS_TYPE and ATTRIBUTE_TYPE are generated in generate_u_int_type */ + case U_INT_4: + case U_INT_8: + case U_INT_16: + case U_INT_32: + case U_INT_64: + case IKE_SPI: + case TS_TYPE: + case ATTRIBUTE_TYPE: + case CONFIGURATION_ATTRIBUTE_LENGTH: + { + this->generate_u_int_type(this,rules[i].type,rules[i].offset); + break; + } + case RESERVED_BIT: + { + this->generate_reserved_field(this,1); + break; + } + case RESERVED_BYTE: + { + this->generate_reserved_field(this,8); + break; + } + case FLAG: + { + this->generate_flag(this,rules[i].offset); + break; + } + case PAYLOAD_LENGTH: + { + /* position of payload lenght field is temporary stored */ + this->last_payload_length_position_offset = this->get_current_buffer_offset(this); + /* payload length is generated like an U_INT_16 */ + this->generate_u_int_type(this,U_INT_16,rules[i].offset); + break; + } + case HEADER_LENGTH: + { + /* position of header length field is temporary stored */ + this->header_length_position_offset = this->get_current_buffer_offset(this); + /* header length is generated like an U_INT_32 */ + this->generate_u_int_type(this,U_INT_32,rules[i].offset); + break; + } + case SPI_SIZE: + /* spi size is handled as 8 bit unsigned integer */ + this->generate_u_int_type(this,U_INT_8,rules[i].offset); + /* last spi size is temporary stored */ + this->last_spi_size = *((u_int8_t *)(this->data_struct + rules[i].offset)); + break; + case ADDRESS: + { + /* the Address value is generated from chunk */ + this->generate_from_chunk(this,rules[i].offset); + break; + } + case SPI: + { + /* the SPI value is generated from chunk */ + this->generate_from_chunk(this,rules[i].offset); + break; + } + case KEY_EXCHANGE_DATA: + case NOTIFICATION_DATA: + case NONCE_DATA: + case ID_DATA: + case AUTH_DATA: + case CERT_DATA: + case CERTREQ_DATA: + case SPIS: + case CONFIGURATION_ATTRIBUTE_VALUE: + case VID_DATA: + case EAP_DATA: + { + u_int32_t payload_length_position_offset; + u_int16_t length_of_payload; + u_int16_t header_length = 0; + u_int16_t length_in_network_order; + + switch(rules[i].type) + { + case KEY_EXCHANGE_DATA: + header_length = KE_PAYLOAD_HEADER_LENGTH; + break; + case NOTIFICATION_DATA: + header_length = NOTIFY_PAYLOAD_HEADER_LENGTH + this->last_spi_size ; + break; + case NONCE_DATA: + header_length = NONCE_PAYLOAD_HEADER_LENGTH; + break; + case ID_DATA: + header_length = ID_PAYLOAD_HEADER_LENGTH; + break; + case AUTH_DATA: + header_length = AUTH_PAYLOAD_HEADER_LENGTH; + break; + case CERT_DATA: + header_length = CERT_PAYLOAD_HEADER_LENGTH; + break; + case CERTREQ_DATA: + header_length = CERTREQ_PAYLOAD_HEADER_LENGTH; + break; + case SPIS: + header_length = DELETE_PAYLOAD_HEADER_LENGTH; + break; + case VID_DATA: + header_length = VENDOR_ID_PAYLOAD_HEADER_LENGTH; + break; + case CONFIGURATION_ATTRIBUTE_VALUE: + header_length = CONFIGURATION_ATTRIBUTE_HEADER_LENGTH; + break; + case EAP_DATA: + header_length = EAP_PAYLOAD_HEADER_LENGTH; + break; + default: + break; + } + + /* the data value is generated from chunk */ + this->generate_from_chunk(this,rules[i].offset); + + payload_length_position_offset = this->last_payload_length_position_offset; + + + /* Length of payload is calculated */ + length_of_payload = header_length + ((chunk_t *)(this->data_struct + rules[i].offset))->len; + + length_in_network_order = htons(length_of_payload); + this->write_bytes_to_buffer_at_offset(this,&length_in_network_order,sizeof(u_int16_t),payload_length_position_offset); + break; + } + case PROPOSALS: + { + /* before iterative generate the transforms, store the current payload length position */ + u_int32_t payload_length_position_offset = this->last_payload_length_position_offset; + /* Length of SA_PAYLOAD is calculated */ + u_int16_t length_of_sa_payload = SA_PAYLOAD_HEADER_LENGTH; + u_int16_t int16_val; + /* proposals are stored in a linked list and so accessed */ + linked_list_t *proposals = *((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_proposal; + + /* create forward iterator */ + iterator = proposals->create_iterator(proposals,TRUE); + /* every proposal is processed (iterative call )*/ + while (iterator->iterate(iterator, (void**)¤t_proposal)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_proposal); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_sa_payload += (after_generate_position_offset - before_generate_position_offset); + } + iterator->destroy(iterator); + + int16_val = htons(length_of_sa_payload); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),payload_length_position_offset); + break; + } + case TRANSFORMS: + { + /* before iterative generate the transforms, store the current length position */ + u_int32_t payload_length_position_offset = this->last_payload_length_position_offset; + u_int16_t length_of_proposal = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH + this->last_spi_size; + u_int16_t int16_val; + linked_list_t *transforms = *((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_transform; + + /* create forward iterator */ + iterator = transforms->create_iterator(transforms,TRUE); + while (iterator->iterate(iterator, (void**)¤t_transform)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_transform); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_proposal += (after_generate_position_offset - before_generate_position_offset); + } + + iterator->destroy(iterator); + + int16_val = htons(length_of_proposal); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),payload_length_position_offset); + + break; + } + case TRANSFORM_ATTRIBUTES: + { + /* before iterative generate the transform attributes, store the current length position */ + u_int32_t transform_length_position_offset = this->last_payload_length_position_offset; + u_int16_t length_of_transform = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + u_int16_t int16_val; + linked_list_t *transform_attributes =*((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_attribute; + + /* create forward iterator */ + iterator = transform_attributes->create_iterator(transform_attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_attribute); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_transform += (after_generate_position_offset - before_generate_position_offset); + } + + iterator->destroy(iterator); + + int16_val = htons(length_of_transform); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),transform_length_position_offset); + + break; + } + case CONFIGURATION_ATTRIBUTES: + { + /* before iterative generate the configuration attributes, store the current length position */ + u_int32_t configurations_length_position_offset = this->last_payload_length_position_offset; + u_int16_t length_of_configurations = CP_PAYLOAD_HEADER_LENGTH; + u_int16_t int16_val; + linked_list_t *configuration_attributes =*((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_attribute; + + /* create forward iterator */ + iterator = configuration_attributes->create_iterator(configuration_attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_attribute); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_configurations += (after_generate_position_offset - before_generate_position_offset); + } + + iterator->destroy(iterator); + + int16_val = htons(length_of_configurations); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),configurations_length_position_offset); + + break; + } + case ATTRIBUTE_FORMAT: + { + this->generate_flag(this,rules[i].offset); + /* Attribute format is a flag which is stored in context*/ + this->attribute_format = *((bool *) (this->data_struct + rules[i].offset)); + break; + } + + case ATTRIBUTE_LENGTH_OR_VALUE: + { + if (this->attribute_format == FALSE) + { + this->generate_u_int_type(this,U_INT_16,rules[i].offset); + /* this field hold the length of the attribute */ + this->attribute_length = *((u_int16_t *)(this->data_struct + rules[i].offset)); + } + else + { + this->generate_u_int_type(this,U_INT_16,rules[i].offset); + } + break; + } + case ATTRIBUTE_VALUE: + { + if (this->attribute_format == FALSE) + { + DBG2(DBG_ENC, "attribute value has not fixed size"); + /* the attribute value is generated */ + this->generate_from_chunk(this,rules[i].offset); + } + break; + } + case TRAFFIC_SELECTORS: + { + /* before iterative generate the traffic_selectors, store the current payload length position */ + u_int32_t payload_length_position_offset = this->last_payload_length_position_offset; + /* Length of SA_PAYLOAD is calculated */ + u_int16_t length_of_ts_payload = TS_PAYLOAD_HEADER_LENGTH; + u_int16_t int16_val; + /* traffic selectors are stored in a linked list and so accessed */ + linked_list_t *traffic_selectors = *((linked_list_t **)(this->data_struct + rules[i].offset)); + iterator_t *iterator; + payload_t *current_traffic_selector_substructure; + + /* create forward iterator */ + iterator = traffic_selectors->create_iterator(traffic_selectors,TRUE); + /* every proposal is processed (iterative call )*/ + while (iterator->iterate(iterator, (void **)¤t_traffic_selector_substructure)) + { + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + before_generate_position_offset = this->get_current_buffer_offset(this); + this->public.generate_payload(&(this->public),current_traffic_selector_substructure); + after_generate_position_offset = this->get_current_buffer_offset(this); + + /* increase size of transform */ + length_of_ts_payload += (after_generate_position_offset - before_generate_position_offset); + } + iterator->destroy(iterator); + + int16_val = htons(length_of_ts_payload); + this->write_bytes_to_buffer_at_offset(this,&int16_val,sizeof(u_int16_t),payload_length_position_offset); + break; + } + + case ENCRYPTED_DATA: + { + this->generate_from_chunk(this, rules[i].offset); + break; + } + default: + DBG1(DBG_ENC, "field type %N is not supported", + encoding_type_names, rules[i].type); + return; + } + } + DBG2(DBG_ENC, "generating %N payload finished", + payload_type_names, payload_type); + DBG3(DBG_ENC, "generated data for this payload %b", + payload_start, this->out_position-payload_start); +} + +/** + * Implementation of generator_t.destroy. + */ +static status_t destroy(private_generator_t *this) +{ + free(this->buffer); + free(this); + return SUCCESS; +} + +/* + * Described in header + */ +generator_t *generator_create() +{ + private_generator_t *this; + + this = malloc_thing(private_generator_t); + + /* initiate public functions */ + this->public.generate_payload = (void(*)(generator_t*, payload_t *)) generate_payload; + this->public.destroy = (void(*)(generator_t*)) destroy; + this->public.write_to_chunk = (void (*) (generator_t *,chunk_t *)) write_to_chunk; + + + /* initiate private functions */ + this->get_current_buffer_size = get_current_buffer_size; + this->get_current_buffer_space = get_current_buffer_space; + this->get_current_data_length = get_current_data_length; + this->get_current_buffer_offset = get_current_buffer_offset; + this->generate_u_int_type = generate_u_int_type; + this->generate_reserved_field = generate_reserved_field; + this->generate_flag = generate_flag; + this->generate_from_chunk = generate_from_chunk; + this->make_space_available = make_space_available; + this->write_bytes_to_buffer = write_bytes_to_buffer; + this->write_bytes_to_buffer_at_offset = write_bytes_to_buffer_at_offset; + + + /* allocate memory for buffer */ + this->buffer = malloc(GENERATOR_DATA_BUFFER_SIZE); + + /* initiate private variables */ + this->out_position = this->buffer; + this->roof_position = this->buffer + GENERATOR_DATA_BUFFER_SIZE; + this->data_struct = NULL; + this->current_bit = 0; + this->last_payload_length_position_offset = 0; + this->header_length_position_offset = 0; + + return &(this->public); +} diff --git a/src/charon/encoding/generator.h b/src/charon/encoding/generator.h new file mode 100644 index 000000000..8eff957cc --- /dev/null +++ b/src/charon/encoding/generator.h @@ -0,0 +1,102 @@ +/** + * @file generator.h + * + * @brief Interface of generator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef GENERATOR_H_ +#define GENERATOR_H_ + +typedef struct generator_t generator_t; + +#include +#include +#include + +/** + * Generating is done in a data buffer. + * This is thehe start size of this buffer in bytes. + * + * @ingroup enconding + */ +#define GENERATOR_DATA_BUFFER_SIZE 500 + +/** + * Number of bytes to increase the buffer, if it is to small. + * + * @ingroup enconding + */ +#define GENERATOR_DATA_BUFFER_INCREASE_VALUE 500 + + +/** + * @brief A generator_t class used to generate IKEv2 payloads. + * + * After creation, multiple payloads can be generated with the generate_payload + * method. The generated bytes are appended. After all payloads are added, + * the write_to_chunk method writes out all generated data since + * the creation of the generator. After that, the generator must be destroyed. + * The generater uses a set of encoding rules, which it can get from + * the supplied payload. With this rules, the generater can generate + * the payload and all substructures automatically. + * + * @b Constructor: + * - generator_create() + * + * @ingroup encoding + */ +struct generator_t { + + /** + * @brief Generates a specific payload from given payload object. + * + * Remember: Header and substructures are also handled as payloads. + * + * @param this generator_t object + * @param[in] payload interface payload_t implementing object + */ + void (*generate_payload) (generator_t *this,payload_t *payload); + + /** + * @brief Writes all generated data of the generator to a chunk. + * + * @param this generator_t object + * @param[out] data chunk to write the data to + */ + void (*write_to_chunk) (generator_t *this,chunk_t *data); + + /** + * @brief Destroys a generator_t object. + * + * @param this generator_t object + */ + void (*destroy) (generator_t *this); +}; + +/** + * @brief Constructor to create a generator. + * + * @return generator_t object. + * + * @ingroup encoding + */ +generator_t *generator_create(void); + +#endif /*GENERATOR_H_*/ diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c new file mode 100644 index 000000000..5f3f91f8b --- /dev/null +++ b/src/charon/encoding/message.c @@ -0,0 +1,1316 @@ +/** + * @file message.c + * + * @brief Implementation of message_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include + +#include "message.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Max number of notify payloads per IKEv2 Message + */ +#define MAX_NOTIFY_PAYLOADS 20 + + +typedef struct payload_rule_t payload_rule_t; + +/** + * A payload rule defines the rules for a payload + * in a specific message rule. It defines if and how + * many times a payload must/can occur in a message + * and if it must be encrypted. + */ +struct payload_rule_t { + /** + * Payload type. + */ + payload_type_t payload_type; + + /** + * Minimal occurence of this payload. + */ + size_t min_occurence; + + /** + * Max occurence of this payload. + */ + size_t max_occurence; + + /** + * TRUE if payload must be encrypted + */ + bool encrypted; + + /** + * If this payload occurs, the message rule is + * fullfilled in any case. This applies e.g. to + * notify_payloads. + */ + bool sufficient; +}; + +typedef struct message_rule_t message_rule_t; + +/** + * A message rule defines the kind of a message, + * if it has encrypted contents and a list + * of payload rules. + * + */ +struct message_rule_t { + /** + * Type of message. + */ + exchange_type_t exchange_type; + + /** + * Is message a request or response. + */ + bool is_request; + + /** + * Message contains encrypted content. + */ + bool encrypted_content; + + /** + * Number of payload rules which will follow + */ + size_t payload_rule_count; + + /** + * Pointer to first payload rule + */ + payload_rule_t *payload_rules; +}; + +/** + * Message rule for IKE_SA_INIT from initiator. + */ +static payload_rule_t ike_sa_init_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,FALSE,FALSE}, + {SECURITY_ASSOCIATION,1,1,FALSE,FALSE}, + {KEY_EXCHANGE,1,1,FALSE,FALSE}, + {NONCE,1,1,FALSE,FALSE}, + {VENDOR_ID,0,10,FALSE,FALSE}, +}; + +/** + * Message rule for IKE_SA_INIT from responder. + */ +static payload_rule_t ike_sa_init_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,FALSE,TRUE}, + {SECURITY_ASSOCIATION,1,1,FALSE,FALSE}, + {KEY_EXCHANGE,1,1,FALSE,FALSE}, + {NONCE,1,1,FALSE,FALSE}, + {VENDOR_ID,0,10,FALSE,FALSE}, +}; + +/** + * Message rule for IKE_AUTH from initiator. + */ +static payload_rule_t ike_auth_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {EXTENSIBLE_AUTHENTICATION,0,1,TRUE,TRUE}, + {AUTHENTICATION,0,1,TRUE,TRUE}, + {ID_INITIATOR,1,1,TRUE,FALSE}, + {CERTIFICATE,0,1,TRUE,FALSE}, + {CERTIFICATE_REQUEST,0,1,TRUE,FALSE}, + {ID_RESPONDER,0,1,TRUE,FALSE}, + {SECURITY_ASSOCIATION,1,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,1,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,1,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + +/** + * Message rule for IKE_AUTH from responder. + */ +static payload_rule_t ike_auth_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,TRUE}, + {EXTENSIBLE_AUTHENTICATION,0,1,TRUE,TRUE}, + {CERTIFICATE,0,1,TRUE,FALSE}, + {ID_RESPONDER,0,1,TRUE,FALSE}, + {AUTHENTICATION,0,1,TRUE,FALSE}, + {SECURITY_ASSOCIATION,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,0,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + + +/** + * Message rule for INFORMATIONAL from initiator. + */ +static payload_rule_t informational_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {DELETE,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, + +}; + +/** + * Message rule for INFORMATIONAL from responder. + */ +static payload_rule_t informational_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {DELETE,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + +/** + * Message rule for CREATE_CHILD_SA from initiator. + */ +static payload_rule_t create_child_sa_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {SECURITY_ASSOCIATION,1,1,TRUE,FALSE}, + {NONCE,1,1,TRUE,FALSE}, + {KEY_EXCHANGE,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,0,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + +/** + * Message rule for CREATE_CHILD_SA from responder. + */ +static payload_rule_t create_child_sa_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,TRUE}, + {SECURITY_ASSOCIATION,1,1,TRUE,FALSE}, + {NONCE,1,1,TRUE,FALSE}, + {KEY_EXCHANGE,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_INITIATOR,0,1,TRUE,FALSE}, + {TRAFFIC_SELECTOR_RESPONDER,0,1,TRUE,FALSE}, + {CONFIGURATION,0,1,TRUE,FALSE}, + {VENDOR_ID,0,10,TRUE,FALSE}, +}; + + +/** + * Message rules, defines allowed payloads. + */ +static message_rule_t message_rules[] = { + {IKE_SA_INIT,TRUE,FALSE,(sizeof(ike_sa_init_i_payload_rules)/sizeof(payload_rule_t)),ike_sa_init_i_payload_rules}, + {IKE_SA_INIT,FALSE,FALSE,(sizeof(ike_sa_init_r_payload_rules)/sizeof(payload_rule_t)),ike_sa_init_r_payload_rules}, + {IKE_AUTH,TRUE,TRUE,(sizeof(ike_auth_i_payload_rules)/sizeof(payload_rule_t)),ike_auth_i_payload_rules}, + {IKE_AUTH,FALSE,TRUE,(sizeof(ike_auth_r_payload_rules)/sizeof(payload_rule_t)),ike_auth_r_payload_rules}, + {INFORMATIONAL,TRUE,TRUE,(sizeof(informational_i_payload_rules)/sizeof(payload_rule_t)),informational_i_payload_rules}, + {INFORMATIONAL,FALSE,TRUE,(sizeof(informational_r_payload_rules)/sizeof(payload_rule_t)),informational_r_payload_rules}, + {CREATE_CHILD_SA,TRUE,TRUE,(sizeof(create_child_sa_i_payload_rules)/sizeof(payload_rule_t)),create_child_sa_i_payload_rules}, + {CREATE_CHILD_SA,FALSE,TRUE,(sizeof(create_child_sa_r_payload_rules)/sizeof(payload_rule_t)),create_child_sa_r_payload_rules}, +}; + + +typedef struct private_message_t private_message_t; + +/** + * Private data of an message_t object. + */ +struct private_message_t { + + /** + * Public part of a message_t object. + */ + message_t public; + + /** + * Minor version of message. + */ + u_int8_t major_version; + + /** + * Major version of message. + */ + u_int8_t minor_version; + + /** + * First Payload in message. + */ + payload_type_t first_payload; + + /** + * Assigned exchange type. + */ + exchange_type_t exchange_type; + + /** + * TRUE if message is a request, FALSE if a reply. + */ + bool is_request; + + /** + * Message ID of this message. + */ + u_int32_t message_id; + + /** + * ID of assigned IKE_SA. + */ + ike_sa_id_t *ike_sa_id; + + /** + * Assigned UDP packet, stores incoming packet or last generated one. + */ + packet_t *packet; + + /** + * Linked List where payload data are stored in. + */ + linked_list_t *payloads; + + /** + * Assigned parser to parse Header and Body of this message. + */ + parser_t *parser; + + /** + * The message rule for this message instance + */ + message_rule_t *message_rule; +}; + +/** + * Implementation of private_message_t.set_message_rule. + */ +static status_t set_message_rule(private_message_t *this) +{ + int i; + + for (i = 0; i < (sizeof(message_rules) / sizeof(message_rule_t)); i++) + { + if ((this->exchange_type == message_rules[i].exchange_type) && + (this->is_request == message_rules[i].is_request)) + { + /* found rule for given exchange_type*/ + this->message_rule = &(message_rules[i]); + return SUCCESS; + } + } + this->message_rule = NULL; + return NOT_FOUND; +} + +/** + * Implementation of private_message_t.get_payload_rule. + */ +static status_t get_payload_rule(private_message_t *this, payload_type_t payload_type, payload_rule_t **payload_rule) +{ + int i; + + for (i = 0; i < this->message_rule->payload_rule_count;i++) + { + if (this->message_rule->payload_rules[i].payload_type == payload_type) + { + *payload_rule = &(this->message_rule->payload_rules[i]); + return SUCCESS; + } + } + + *payload_rule = NULL; + return NOT_FOUND; +} + +/** + * Implementation of message_t.set_ike_sa_id. + */ +static void set_ike_sa_id (private_message_t *this,ike_sa_id_t *ike_sa_id) +{ + DESTROY_IF(this->ike_sa_id); + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); +} + +/** + * Implementation of message_t.get_ike_sa_id. + */ +static ike_sa_id_t* get_ike_sa_id (private_message_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implementation of message_t.set_message_id. + */ +static void set_message_id (private_message_t *this,u_int32_t message_id) +{ + this->message_id = message_id; +} + +/** + * Implementation of message_t.get_message_id. + */ +static u_int32_t get_message_id (private_message_t *this) +{ + return this->message_id; +} + +/** + * Implementation of message_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi (private_message_t *this) +{ + return (this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); +} + +/** + * Implementation of message_t.get_responder_spi. + */ +static u_int64_t get_responder_spi (private_message_t *this) +{ + return (this->ike_sa_id->get_responder_spi(this->ike_sa_id)); +} + +/** + * Implementation of message_t.set_major_version. + */ +static void set_major_version (private_message_t *this,u_int8_t major_version) +{ + this->major_version = major_version; +} + + +/** + * Implementation of message_t.set_major_version. + */ +static u_int8_t get_major_version (private_message_t *this) +{ + return this->major_version; +} + +/** + * Implementation of message_t.set_minor_version. + */ +static void set_minor_version (private_message_t *this,u_int8_t minor_version) +{ + this->minor_version = minor_version; +} + +/** + * Implementation of message_t.get_minor_version. + */ +static u_int8_t get_minor_version (private_message_t *this) +{ + return this->minor_version; +} + +/** + * Implementation of message_t.set_exchange_type. + */ +static void set_exchange_type (private_message_t *this,exchange_type_t exchange_type) +{ + this->exchange_type = exchange_type; +} + +/** + * Implementation of message_t.get_exchange_type. + */ +static exchange_type_t get_exchange_type (private_message_t *this) +{ + return this->exchange_type; +} + +/** + * Implementation of message_t.set_request. + */ +static void set_request (private_message_t *this,bool request) +{ + this->is_request = request; +} + +/** + * Implementation of message_t.get_request. + */ +static exchange_type_t get_request (private_message_t *this) +{ + return this->is_request; +} + +/** + * Is this message in an encoded form? + */ +static bool is_encoded(private_message_t *this) +{ + chunk_t data = this->packet->get_data(this->packet); + + if (data.ptr == NULL) + { + return FALSE; + } + return TRUE; +} + +/** + * Implementation of message_t.add_payload. + */ +static void add_payload(private_message_t *this, payload_t *payload) +{ + payload_t *last_payload, *first_payload; + + if ((this->is_request && payload->get_type(payload) == ID_INITIATOR) || + (!this->is_request && payload->get_type(payload) == ID_RESPONDER)) + { + /* HOTD: insert ID payload in the beginning to respect RFC */ + if (this->payloads->get_first(this->payloads, + (void **)&first_payload) == SUCCESS) + { + payload->set_next_type(payload, first_payload->get_type(first_payload)); + } + else + { + payload->set_next_type(payload, NO_PAYLOAD); + } + this->first_payload = payload->get_type(payload); + this->payloads->insert_first(this->payloads, payload); + } + else + { + if (this->payloads->get_count(this->payloads) > 0) + { + this->payloads->get_last(this->payloads,(void **) &last_payload); + last_payload->set_next_type(last_payload, payload->get_type(payload)); + } + else + { + this->first_payload = payload->get_type(payload); + } + payload->set_next_type(payload, NO_PAYLOAD); + this->payloads->insert_last(this->payloads, payload); + } + + DBG2(DBG_ENC ,"added payload of type %N to message", + payload_type_names, payload->get_type(payload)); +} + +/** + * Implementation of message_t.add_notify. + */ +static void add_notify(private_message_t *this, bool flush, notify_type_t type, + chunk_t data) +{ + notify_payload_t *notify; + payload_t *payload; + + if (flush) + { + while (this->payloads->remove_last(this->payloads, + (void**)&payload) == SUCCESS) + { + payload->destroy(payload); + } + } + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + notify->set_notification_data(notify, data); + add_payload(this, (payload_t*)notify); +} + +/** + * Implementation of message_t.set_source. + */ +static void set_source(private_message_t *this, host_t *host) +{ + this->packet->set_source(this->packet, host); +} + +/** + * Implementation of message_t.set_destination. + */ +static void set_destination(private_message_t *this, host_t *host) +{ + this->packet->set_destination(this->packet, host); +} + +/** + * Implementation of message_t.get_source. + */ +static host_t* get_source(private_message_t *this) +{ + return this->packet->get_source(this->packet); +} + +/** + * Implementation of message_t.get_destination. + */ +static host_t * get_destination(private_message_t *this) +{ + return this->packet->get_destination(this->packet); +} + +/** + * Implementation of message_t.get_payload_iterator. + */ +static iterator_t *get_payload_iterator(private_message_t *this) +{ + return this->payloads->create_iterator(this->payloads, TRUE); +} + +/** + * Implementation of message_t.get_payload. + */ +static payload_t *get_payload(private_message_t *this, payload_type_t type) +{ + payload_t *current, *found = NULL; + iterator_t *iterator; + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_type(current) == type) + { + found = current; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_message_t *this = *((private_message_t**)(args[0])); + iterator_t *iterator; + payload_t *payload; + bool first = TRUE; + size_t total_written = 0; + size_t written; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + written = fprintf(stream, "%N %s [", + exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + if (written < 0) + { + return written; + } + total_written += written; + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (!first) + { + written = fprintf(stream, " "); + if (written < 0) + { + return written; + } + total_written += written; + } + else + { + first = FALSE; + } + written = fprintf(stream, "%N", payload_type_short_names, + payload->get_type(payload)); + if (written < 0) + { + return written; + } + total_written += written; + } + iterator->destroy(iterator); + written = fprintf(stream, "]"); + if (written < 0) + { + return written; + } + total_written += written; + return total_written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_MESSAGE, print, arginfo_ptr); +} + +/** + * Implementation of private_message_t.encrypt_payloads. + */ +static status_t encrypt_payloads (private_message_t *this,crypter_t *crypter, signer_t* signer) +{ + encryption_payload_t *encryption_payload = NULL; + status_t status; + linked_list_t *all_payloads; + + if (!this->message_rule->encrypted_content) + { + DBG2(DBG_ENC, "message doesn't have to be encrypted"); + /* message contains no content to encrypt */ + return SUCCESS; + } + + DBG2(DBG_ENC, "copy all payloads to a temporary list"); + all_payloads = linked_list_create(); + + /* first copy all payloads in a temporary list */ + while (this->payloads->get_count(this->payloads) > 0) + { + void *current_payload; + this->payloads->remove_first(this->payloads,¤t_payload); + all_payloads->insert_last(all_payloads,current_payload); + } + + encryption_payload = encryption_payload_create(); + + DBG2(DBG_ENC, "check each payloads if they have to get encrypted"); + while (all_payloads->get_count(all_payloads) > 0) + { + payload_rule_t *payload_rule; + payload_t *current_payload; + bool to_encrypt = FALSE; + + all_payloads->remove_first(all_payloads,(void **)¤t_payload); + + status = get_payload_rule(this, + current_payload->get_type(current_payload),&payload_rule); + /* for payload types which are not found in supported payload list, + * it is presumed that they don't have to be encrypted */ + if ((status == SUCCESS) && (payload_rule->encrypted)) + { + DBG2(DBG_ENC, "payload %N gets encrypted", + payload_type_names, current_payload->get_type(current_payload)); + to_encrypt = TRUE; + } + + if (to_encrypt) + { + DBG2(DBG_ENC, "insert payload %N to encryption payload", + payload_type_names, current_payload->get_type(current_payload)); + encryption_payload->add_payload(encryption_payload,current_payload); + } + else + { + DBG2(DBG_ENC, "insert payload %N unencrypted", + payload_type_names ,current_payload->get_type(current_payload)); + add_payload(this, (payload_t*)encryption_payload); + } + } + + status = SUCCESS; + DBG2(DBG_ENC, "encrypting encryption payload"); + encryption_payload->set_transforms(encryption_payload, crypter,signer); + status = encryption_payload->encrypt(encryption_payload); + DBG2(DBG_ENC, "add encrypted payload to payload list"); + add_payload(this, (payload_t*)encryption_payload); + + all_payloads->destroy(all_payloads); + + return status; +} + +/** + * Implementation of message_t.generate. + */ +static status_t generate(private_message_t *this, crypter_t *crypter, signer_t* signer, packet_t **packet) +{ + generator_t *generator; + ike_header_t *ike_header; + payload_t *payload, *next_payload; + iterator_t *iterator; + status_t status; + chunk_t packet_data; + + if (is_encoded(this)) + { + /* already generated, return a new packet clone */ + *packet = this->packet->clone(this->packet); + return SUCCESS; + } + + DBG1(DBG_ENC, "generating %M", this); + + if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) + { + DBG1(DBG_ENC, "exchange type is not defined"); + return INVALID_STATE; + } + + if (this->packet->get_source(this->packet) == NULL || + this->packet->get_destination(this->packet) == NULL) + { + DBG1(DBG_ENC, "%s not defined", + !this->packet->get_source(this->packet) ? "source" : "destination"); + return INVALID_STATE; + } + + /* set the rules for this messge */ + status = set_message_rule(this); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "no message rules specified for this message type"); + return NOT_SUPPORTED; + } + + /* going to encrypt all content which have to be encrypted */ + status = encrypt_payloads(this, crypter, signer); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "payload encryption failed"); + return status; + } + + /* build ike header */ + ike_header = ike_header_create(); + + ike_header->set_exchange_type(ike_header, this->exchange_type); + ike_header->set_message_id(ike_header, this->message_id); + ike_header->set_response_flag(ike_header, !this->is_request); + ike_header->set_initiator_flag(ike_header, this->ike_sa_id->is_initiator(this->ike_sa_id)); + ike_header->set_initiator_spi(ike_header, this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); + ike_header->set_responder_spi(ike_header, this->ike_sa_id->get_responder_spi(this->ike_sa_id)); + + generator = generator_create(); + + payload = (payload_t*)ike_header; + + + /* generate every payload expect last one, this is doen later*/ + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while(iterator->iterate(iterator, (void**)&next_payload)) + { + payload->set_next_type(payload, next_payload->get_type(next_payload)); + generator->generate_payload(generator, payload); + payload = next_payload; + } + iterator->destroy(iterator); + + /* last payload has no next payload*/ + payload->set_next_type(payload, NO_PAYLOAD); + + generator->generate_payload(generator, payload); + + ike_header->destroy(ike_header); + + /* build packet */ + generator->write_to_chunk(generator, &packet_data); + generator->destroy(generator); + + /* if last payload is of type encrypted, integrity checksum if necessary */ + if (payload->get_type(payload) == ENCRYPTED) + { + DBG2(DBG_ENC, "build signature on whole message"); + encryption_payload_t *encryption_payload = (encryption_payload_t*)payload; + status = encryption_payload->build_signature(encryption_payload, packet_data); + if (status != SUCCESS) + { + return status; + } + } + + this->packet->set_data(this->packet, packet_data); + + /* clone packet for caller */ + *packet = this->packet->clone(this->packet); + + DBG2(DBG_ENC, "message generated successfully"); + return SUCCESS; +} + +/** + * Implementation of message_t.get_packet. + */ +static packet_t *get_packet (private_message_t *this) +{ + if (this->packet == NULL) + { + return NULL; + } + return this->packet->clone(this->packet); +} + +/** + * Implementation of message_t.get_packet_data. + */ +static chunk_t get_packet_data (private_message_t *this) +{ + if (this->packet == NULL) + { + return chunk_empty; + } + return chunk_clone(this->packet->get_data(this->packet)); +} + +/** + * Implementation of message_t.parse_header. + */ +static status_t parse_header(private_message_t *this) +{ + ike_header_t *ike_header; + status_t status; + + DBG2(DBG_ENC, "parsing header of message"); + + this->parser->reset_context(this->parser); + status = this->parser->parse_payload(this->parser,HEADER,(payload_t **) &ike_header); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "header could not be parsed"); + return status; + + } + + /* verify payload */ + status = ike_header->payload_interface.verify(&(ike_header->payload_interface)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "header verification failed"); + ike_header->destroy(ike_header); + return status; + } + + if (this->ike_sa_id != NULL) + { + this->ike_sa_id->destroy(this->ike_sa_id); + } + + this->ike_sa_id = ike_sa_id_create(ike_header->get_initiator_spi(ike_header), + ike_header->get_responder_spi(ike_header), + ike_header->get_initiator_flag(ike_header)); + + this->exchange_type = ike_header->get_exchange_type(ike_header); + this->message_id = ike_header->get_message_id(ike_header); + this->is_request = (!(ike_header->get_response_flag(ike_header))); + this->major_version = ike_header->get_maj_version(ike_header); + this->minor_version = ike_header->get_min_version(ike_header); + this->first_payload = ike_header->payload_interface.get_next_type(&(ike_header->payload_interface)); + + DBG2(DBG_ENC, "parsed a %N %s", exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + + ike_header->destroy(ike_header); + + /* get the rules for this messge */ + status = set_message_rule(this); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "no message rules specified for a %N %s", + exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + } + + return status; +} + +/** + * Implementation of private_message_t.decrypt_and_verify_payloads. + */ +static status_t decrypt_payloads(private_message_t *this,crypter_t *crypter, signer_t* signer) +{ + bool current_payload_was_encrypted = FALSE; + payload_t *previous_payload = NULL; + int payload_number = 1; + iterator_t *iterator; + payload_t *current_payload; + status_t status; + + iterator = this->payloads->create_iterator(this->payloads,TRUE); + + /* process each payload and decrypt a encryption payload */ + while(iterator->iterate(iterator, (void**)¤t_payload)) + { + payload_rule_t *payload_rule; + payload_type_t current_payload_type; + + /* needed to check */ + current_payload_type = current_payload->get_type(current_payload); + + DBG2(DBG_ENC, "process payload of type %N", + payload_type_names, current_payload_type); + + if (current_payload_type == ENCRYPTED) + { + encryption_payload_t *encryption_payload; + payload_t *current_encrypted_payload; + + encryption_payload = (encryption_payload_t*)current_payload; + + DBG2(DBG_ENC, "found an encryption payload"); + + if (payload_number != this->payloads->get_count(this->payloads)) + { + /* encrypted payload is not last one */ + DBG1(DBG_ENC, "encrypted payload is not last payload"); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + /* decrypt */ + encryption_payload->set_transforms(encryption_payload, crypter, signer); + DBG2(DBG_ENC, "verify signature of encryption payload"); + status = encryption_payload->verify_signature(encryption_payload, + this->packet->get_data(this->packet)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "encryption payload signature invalid"); + iterator->destroy(iterator); + return FAILED; + } + DBG2(DBG_ENC, "decrypting content of encryption payload"); + status = encryption_payload->decrypt(encryption_payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "encrypted payload could not be decrypted and parsed"); + iterator->destroy(iterator); + return PARSE_ERROR; + } + + /* needed later to find out if a payload was encrypted */ + current_payload_was_encrypted = TRUE; + + /* check if there are payloads contained in the encryption payload */ + if (encryption_payload->get_payload_count(encryption_payload) == 0) + { + DBG2(DBG_ENC, "encrypted payload is empty"); + /* remove the encryption payload, is not needed anymore */ + iterator->remove(iterator); + /* encrypted payload contains no other payload */ + current_payload_type = NO_PAYLOAD; + } + else + { + /* encryption_payload is replaced with first payload contained in encryption_payload */ + encryption_payload->remove_first_payload(encryption_payload, ¤t_encrypted_payload); + iterator->replace(iterator,NULL,(void *) current_encrypted_payload); + current_payload_type = current_encrypted_payload->get_type(current_encrypted_payload); + } + + /* is the current paylad the first in the message? */ + if (previous_payload == NULL) + { + /* yes, set the first payload type of the message to the current type */ + this->first_payload = current_payload_type; + } + else + { + /* no, set the next_type of the previous payload to the current type */ + previous_payload->set_next_type(previous_payload, current_payload_type); + } + + /* all encrypted payloads are added to the payload list */ + while (encryption_payload->get_payload_count(encryption_payload) > 0) + { + encryption_payload->remove_first_payload(encryption_payload, ¤t_encrypted_payload); + DBG2(DBG_ENC, "insert unencrypted payload of type %N at end of list", + payload_type_names, current_encrypted_payload->get_type(current_encrypted_payload)); + this->payloads->insert_last(this->payloads,current_encrypted_payload); + } + + /* encryption payload is processed, payloads are moved. Destroy it. */ + encryption_payload->destroy(encryption_payload); + } + + /* we allow unknown payloads of any type and don't bother if it was encrypted. Not our problem. */ + if (current_payload_type != UNKNOWN_PAYLOAD && current_payload_type != NO_PAYLOAD) + { + /* get the ruleset for found payload */ + status = get_payload_rule(this, current_payload_type, &payload_rule); + if (status != SUCCESS) + { + /* payload is not allowed */ + DBG1(DBG_ENC, "payload type %N not allowed", + payload_type_names, current_payload_type); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + + /* check if the payload was encrypted, and if it should been have encrypted */ + if (payload_rule->encrypted != current_payload_was_encrypted) + { + /* payload was not encrypted, but should have been. or vice-versa */ + DBG1(DBG_ENC, "payload type %N should be %s!", + payload_type_names, current_payload_type, + (payload_rule->encrypted) ? "encrypted" : "not encrypted"); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + } + /* advance to the next payload */ + payload_number++; + /* is stored to set next payload in case of found encryption payload */ + previous_payload = current_payload; + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of private_message_t.verify. + */ +static status_t verify(private_message_t *this) +{ + int i; + iterator_t *iterator; + payload_t *current_payload; + size_t total_found_payloads = 0; + + DBG2(DBG_ENC, "verifying message structure"); + + iterator = this->payloads->create_iterator(this->payloads,TRUE); + /* check for payloads with wrong count*/ + for (i = 0; i < this->message_rule->payload_rule_count;i++) + { + size_t found_payloads = 0; + + /* check all payloads for specific rule */ + iterator->reset(iterator); + + while(iterator->iterate(iterator,(void **)¤t_payload)) + { + payload_type_t current_payload_type; + + current_payload_type = current_payload->get_type(current_payload); + if (current_payload_type == UNKNOWN_PAYLOAD) + { + /* unknown payloads are ignored, IF they are not critical */ + unknown_payload_t *unknown_payload = (unknown_payload_t*)current_payload; + if (unknown_payload->is_critical(unknown_payload)) + { + DBG1(DBG_ENC, "%N is not supported, but its critical!", + payload_type_names, current_payload_type); + iterator->destroy(iterator); + return NOT_SUPPORTED; + } + } + else if (current_payload_type == this->message_rule->payload_rules[i].payload_type) + { + found_payloads++; + total_found_payloads++; + DBG2(DBG_ENC, "found payload of type %N", + payload_type_names, this->message_rule->payload_rules[i].payload_type); + + /* as soon as ohe payload occures more then specified, the verification fails */ + if (found_payloads > this->message_rule->payload_rules[i].max_occurence) + { + DBG1(DBG_ENC, "payload of type %N more than %d times (%d) occured in current message", + payload_type_names, current_payload_type, + this->message_rule->payload_rules[i].max_occurence, found_payloads); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + } + } + + if (found_payloads < this->message_rule->payload_rules[i].min_occurence) + { + DBG1(DBG_ENC, "payload of type %N not occured %d times (%d)", + payload_type_names, this->message_rule->payload_rules[i].payload_type, + this->message_rule->payload_rules[i].min_occurence, found_payloads); + iterator->destroy(iterator); + return VERIFY_ERROR; + } + if ((this->message_rule->payload_rules[i].sufficient) && (this->payloads->get_count(this->payloads) == total_found_payloads)) + { + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of message_t.parse_body. + */ +static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t *signer) +{ + status_t status = SUCCESS; + payload_type_t current_payload_type; + + current_payload_type = this->first_payload; + + DBG2(DBG_ENC, "parsing body of message, first payload is %N", + payload_type_names, current_payload_type); + + /* parse payload for payload, while there are more available */ + while ((current_payload_type != NO_PAYLOAD)) + { + payload_t *current_payload; + + DBG2(DBG_ENC, "starting parsing a %N payload", + payload_type_names, current_payload_type); + + /* parse current payload */ + status = this->parser->parse_payload(this->parser,current_payload_type,(payload_t **) ¤t_payload); + + if (status != SUCCESS) + { + DBG1(DBG_ENC, "payload type %N could not be parsed", + payload_type_names, current_payload_type); + return PARSE_ERROR; + } + + DBG2(DBG_ENC, "verifying payload of type %N", + payload_type_names, current_payload_type); + + /* verify it, stop parsig if its invalid */ + status = current_payload->verify(current_payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "%N payload verification failed", + payload_type_names, current_payload_type); + current_payload->destroy(current_payload); + return VERIFY_ERROR; + } + + DBG2(DBG_ENC, "%N payload verified. Adding to payload list", + payload_type_names, current_payload_type); + this->payloads->insert_last(this->payloads,current_payload); + + /* an encryption payload is the last one, so STOP here. decryption is done later */ + if (current_payload_type == ENCRYPTED) + { + DBG2(DBG_ENC, "%N payload found. Stop parsing", + payload_type_names, current_payload_type); + break; + } + + /* get next payload type */ + current_payload_type = current_payload->get_next_type(current_payload); + } + + if (current_payload_type == ENCRYPTED) + { + status = decrypt_payloads(this,crypter,signer); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "could not decrypt payloads"); + return status; + } + } + + status = verify(this); + if (status != SUCCESS) + { + return status; + } + + DBG1(DBG_ENC, "parsed %M", this); + + return SUCCESS; +} + +/** + * Implementation of message_t.destroy. + */ +static void destroy (private_message_t *this) +{ + DESTROY_IF(this->ike_sa_id); + this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); + this->packet->destroy(this->packet); + this->parser->destroy(this->parser); + free(this); +} + +/* + * Described in Header-File + */ +message_t *message_create_from_packet(packet_t *packet) +{ + private_message_t *this = malloc_thing(private_message_t); + + /* public functions */ + this->public.set_major_version = (void(*)(message_t*, u_int8_t))set_major_version; + this->public.get_major_version = (u_int8_t(*)(message_t*))get_major_version; + this->public.set_minor_version = (void(*)(message_t*, u_int8_t))set_minor_version; + this->public.get_minor_version = (u_int8_t(*)(message_t*))get_minor_version; + this->public.set_message_id = (void(*)(message_t*, u_int32_t))set_message_id; + this->public.get_message_id = (u_int32_t(*)(message_t*))get_message_id; + this->public.get_initiator_spi = (u_int64_t(*)(message_t*))get_initiator_spi; + this->public.get_responder_spi = (u_int64_t(*)(message_t*))get_responder_spi; + this->public.set_ike_sa_id = (void(*)(message_t*, ike_sa_id_t *))set_ike_sa_id; + this->public.get_ike_sa_id = (ike_sa_id_t*(*)(message_t*))get_ike_sa_id; + this->public.set_exchange_type = (void(*)(message_t*, exchange_type_t))set_exchange_type; + this->public.get_exchange_type = (exchange_type_t(*)(message_t*))get_exchange_type; + this->public.set_request = (void(*)(message_t*, bool))set_request; + this->public.get_request = (bool(*)(message_t*))get_request; + this->public.add_payload = (void(*)(message_t*,payload_t*))add_payload; + this->public.add_notify = (void(*)(message_t*,bool,notify_type_t,chunk_t))add_notify; + this->public.generate = (status_t (*) (message_t *,crypter_t*,signer_t*,packet_t**)) generate; + this->public.set_source = (void (*) (message_t*,host_t*)) set_source; + this->public.get_source = (host_t * (*) (message_t*)) get_source; + this->public.set_destination = (void (*) (message_t*,host_t*)) set_destination; + this->public.get_destination = (host_t * (*) (message_t*)) get_destination; + this->public.get_payload_iterator = (iterator_t * (*) (message_t *)) get_payload_iterator; + this->public.get_payload = (payload_t * (*) (message_t *, payload_type_t)) get_payload; + this->public.parse_header = (status_t (*) (message_t *)) parse_header; + this->public.parse_body = (status_t (*) (message_t *,crypter_t*,signer_t*)) parse_body; + this->public.get_packet = (packet_t * (*) (message_t*)) get_packet; + this->public.get_packet_data = (chunk_t (*) (message_t *this)) get_packet_data; + this->public.destroy = (void(*)(message_t*))destroy; + + /* private values */ + this->exchange_type = EXCHANGE_TYPE_UNDEFINED; + this->is_request = TRUE; + this->ike_sa_id = NULL; + this->first_payload = NO_PAYLOAD; + this->message_id = 0; + + /* private values */ + if (packet == NULL) + { + packet = packet_create(); + } + this->message_rule = NULL; + this->packet = packet; + this->payloads = linked_list_create(); + + /* parser is created from data of packet */ + this->parser = parser_create(this->packet->get_data(this->packet)); + + return (&this->public); +} + +/* + * Described in Header. + */ +message_t *message_create() +{ + return message_create_from_packet(NULL); +} diff --git a/src/charon/encoding/message.h b/src/charon/encoding/message.h new file mode 100644 index 000000000..73c2e05c6 --- /dev/null +++ b/src/charon/encoding/message.h @@ -0,0 +1,390 @@ +/** + * @file message.h + * + * @brief Interface of message_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef MESSAGE_H_ +#define MESSAGE_H_ + +typedef struct message_t message_t; + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief This class is used to represent an IKEv2-Message. + * + * The message handles parsing and generation of payloads + * via parser_t/generator_t. Encryption is done transparently + * via the encryption_payload_t. A set of rules for messages + * and payloads does check parsed messages. + * + * @b Constructors: + * - message_create() + * - message_create_from_packet() + * - message_create_notify_reply() + * + * @ingroup encoding + */ +struct message_t { + + /** + * @brief Sets the IKE major version of the message. + * + * @param this message_t object + * @param major_version major version to set + */ + void (*set_major_version) (message_t *this,u_int8_t major_version); + + /** + * @brief Gets the IKE major version of the message. + * + * @param this message_t object + * @return major version of the message + */ + u_int8_t (*get_major_version) (message_t *this); + + /** + * @brief Sets the IKE minor version of the message. + * + * @param this message_t object + * @param minor_version minor version to set + */ + void (*set_minor_version) (message_t *this,u_int8_t minor_version); + + /** + * @brief Gets the IKE minor version of the message. + * + * @param this message_t object + * @return minor version of the message + */ + u_int8_t (*get_minor_version) (message_t *this); + + /** + * @brief Sets the Message ID of the message. + * + * @param this message_t object + * @param message_id message_id to set + */ + void (*set_message_id) (message_t *this,u_int32_t message_id); + + /** + * @brief Gets the Message ID of the message. + * + * @param this message_t object + * @return message_id type of the message + */ + u_int32_t (*get_message_id) (message_t *this); + + /** + * @brief Gets the initiator SPI of the message. + * + * @param this message_t object + * @return initiator spi of the message + */ + u_int64_t (*get_initiator_spi) (message_t *this); + + /** + * @brief Gets the responder SPI of the message. + * + * @param this message_t object + * @return responder spi of the message + */ + u_int64_t (*get_responder_spi) (message_t *this); + + /** + * @brief Sets the IKE_SA ID of the message. + * + * ike_sa_id gets cloned. + * + * @param this message_t object + * @param ike_sa_id ike_sa_id to set + */ + void (*set_ike_sa_id) (message_t *this, ike_sa_id_t * ike_sa_id); + + /** + * @brief Gets the IKE_SA ID of the message. + * + * The ike_sa_id points to the message internal id, do not modify. + * + * @param this message_t object + * @return ike_sa_id of message + */ + ike_sa_id_t *(*get_ike_sa_id) (message_t *this); + + /** + * @brief Sets the exchange type of the message. + * + * @param this message_t object + * @param exchange_type exchange_type to set + */ + void (*set_exchange_type) (message_t *this,exchange_type_t exchange_type); + + /** + * @brief Gets the exchange type of the message. + * + * @param this message_t object + * @return exchange type of the message + */ + exchange_type_t (*get_exchange_type) (message_t *this); + + /** + * @brief Sets the request flag. + * + * @param this message_t object + * @param original_initiator TRUE if message is a request, FALSE if it is a reply + */ + void (*set_request) (message_t *this,bool request); + + /** + * @brief Gets request flag. + * + * @param this message_t object + * @return TRUE if message is a request, FALSE if it is a reply + */ + bool (*get_request) (message_t *this); + + /** + * @brief Append a payload to the message. + * + * If the payload must be encrypted is not specified here. Encryption + * of payloads is evaluated via internal rules for the messages and + * is done before generation. The order of payloads may change, since + * all payloads to encrypt are added to the encryption payload, which is + * always the last one. + * + * @param this message_t object + * @param payload payload to append + */ + void (*add_payload) (message_t *this, payload_t *payload); + + /** + * @brief Build a notify payload and add it to the message. + * + * This is a helper method to create notify messages or add + * notify payload to messages. The flush parameter specifies if existing + * payloads should get removed before appending the notify. + * + * @param this message_t object + * @param flush TRUE to remove existing payloads + * @param type type of the notify + * @param data a chunk of data to add to the notify, gets cloned + */ + void (*add_notify) (message_t *this, bool flush, notify_type_t type, + chunk_t data); + + /** + * @brief Parses header of message. + * + * Begins parisng of a message created via message_create_from_packet(). + * The parsing context is stored, so a subsequent call to parse_body() + * will continue the parsing process. + * + * @param this message_t object + * @return + * - SUCCESS if header could be parsed + * - PARSE_ERROR if corrupted/invalid data found + * - FAILED if consistence check of header failed + */ + status_t (*parse_header) (message_t *this); + + /** + * @brief Parses body of message. + * + * The body gets not only parsed, but rather it gets verified. + * All payloads are verified if they are allowed to exist in the message + * of this type and if their own structure is ok. + * If there are encrypted payloads, they get decrypted via the supplied + * crypter. Also the message integrity gets verified with the supplied + * signer. + * Crypter/signer can be omitted (by passing NULL) when no encryption + * payload is expected. + * + * @param this message_t object + * @param crypter crypter to decrypt encryption payloads + * @param signer signer to verifiy a message with an encryption payload + * @return + * - SUCCESS if parsing successful + * - NOT_SUPPORTED if ciritcal unknown payloads found + * - NOT_SUPPORTED if message type is not supported! + * - PARSE_ERROR if message parsing failed + * - VERIFY_ERROR if message verification failed (bad syntax) + * - FAILED if integrity check failed + * - INVALID_STATE if crypter/signer not supplied, but needed + */ + status_t (*parse_body) (message_t *this, crypter_t *crypter, signer_t *signer); + + /** + * @brief Generates the UDP packet of specific message. + * + * Payloads which must be encrypted are generated first and added to + * an encryption payload. This encryption payload will get encrypted via + * the supplied crypter. Then all other payloads and the header get generated. + * After that, the checksum is added to the encryption payload over the full + * message. + * Crypter/signer can be omitted (by passing NULL) when no encryption + * payload is expected. + * Generation is only done once, multiple calls will just return a packet copy. + * + * @param this message_t object + * @param crypter crypter to use when a payload must be encrypted + * @param signer signer to build a mac + * @param packet copy of generated packet + * @return + * - SUCCESS if packet could be generated + * - INVALID_STATE if exchange type is currently not set + * - NOT_FOUND if no rules found for message generation + * - INVALID_STATE if crypter/signer not supplied but needed. + */ + status_t (*generate) (message_t *this, crypter_t *crypter, signer_t *signer, packet_t **packet); + + /** + * @brief Gets the source host informations. + * + * @warning Returned host_t object is not getting cloned, + * do not destroy nor modify. + * + * @param this message_t object + * @return host_t object representing source host + */ + host_t * (*get_source) (message_t *this); + + /** + * @brief Sets the source host informations. + * + * @warning host_t object is not getting cloned and gets destroyed by + * message_t.destroy or next call of message_t.set_source. + * + * @param this message_t object + * @param host host_t object representing source host + */ + void (*set_source) (message_t *this, host_t *host); + + /** + * @brief Gets the destination host informations. + * + * @warning Returned host_t object is not getting cloned, + * do not destroy nor modify. + * + * @param this message_t object + * @return host_t object representing destination host + */ + host_t * (*get_destination) (message_t *this); + + /** + * @brief Sets the destination host informations. + * + * @warning host_t object is not getting cloned and gets destroyed by + * message_t.destroy or next call of message_t.set_destination. + * + * @param this message_t object + * @param host host_t object representing destination host + */ + void (*set_destination) (message_t *this, host_t *host); + + /** + * @brief Returns an iterator on all stored payloads. + * + * @warning Don't insert payloads over this iterator. + * Use add_payload() instead. + * + * @param this message_t object + * @return iterator_t object which has to get destroyd by the caller + */ + iterator_t * (*get_payload_iterator) (message_t *this); + + /** + * @brief Find a payload of a spicific type. + * + * Returns the first occurance. + * + * @param this message_t object + * @param type type of the payload to find + * @return payload, or NULL if no such payload found + */ + payload_t* (*get_payload) (message_t *this, payload_type_t type); + + /** + * @brief Returns a clone of the internal stored packet_t object. + * + * @param this message_t object + * @return packet_t object as clone of internal one + */ + packet_t * (*get_packet) (message_t *this); + + /** + * @brief Returns a clone of the internal stored packet_t data. + * + * @param this message_t object + * @return clone of the internal stored packet_t data. + */ + chunk_t (*get_packet_data) (message_t *this); + + /** + * @brief Destroys a message and all including objects. + * + * @param this message_t object + */ + void (*destroy) (message_t *this); +}; + +/** + * @brief Creates an message_t object from a incoming UDP Packet. + * + * @warning the given packet_t object is not copied and gets + * destroyed in message_t's destroy call. + * + * @warning Packet is not parsed in here! + * + * - exchange_type is set to NOT_SET + * - original_initiator is set to TRUE + * - is_request is set to TRUE + * Call message_t.parse_header afterwards. + * + * @param packet packet_t object which is assigned to message + * @return message_t object + * + * @ingroup encoding + */ +message_t * message_create_from_packet(packet_t *packet); + + +/** + * @brief Creates an empty message_t object. + * + * - exchange_type is set to NOT_SET + * - original_initiator is set to TRUE + * - is_request is set to TRUE + * + * @return message_t object + * + * @ingroup encoding + */ +message_t * message_create(void); + +#endif /*MESSAGE_H_*/ diff --git a/src/charon/encoding/parser.c b/src/charon/encoding/parser.c new file mode 100644 index 000000000..d7caf7099 --- /dev/null +++ b/src/charon/encoding/parser.c @@ -0,0 +1,1048 @@ +/** + * @file parser.c + * + * @brief Implementation of parser_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include + +#include "parser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_parser_t private_parser_t; + +/** + * Private data stored in a context. + * + * Contains pointers and counters to store current state. + */ +struct private_parser_t { + /** + * Public members, see parser_t. + */ + parser_t public; + + /** + * @brief Parse a 4-Bit unsigned integer from the current parsing position. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint4) (private_parser_t *this, int rule_number, u_int8_t *output_pos); + + /** + * @brief Parse a 8-Bit unsigned integer from the current parsing position. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint8) (private_parser_t *this, int rule_number, u_int8_t *output_pos); + + /** + * @brief Parse a 15-Bit unsigned integer from the current parsing position. + * + * This is a special case used for ATTRIBUTE_TYPE. + * Big-/Little-endian conversion is done here. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint15) (private_parser_t *this, int rule_number, u_int16_t *output_pos); + + /** + * @brief Parse a 16-Bit unsigned integer from the current parsing position. + * + * Big-/Little-endian conversion is done here. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint16) (private_parser_t *this, int rule_number, u_int16_t *output_pos); + + /** + * @brief Parse a 32-Bit unsigned integer from the current parsing position. + * + * Big-/Little-endian conversion is done here. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint32) (private_parser_t *this, int rule_number, u_int32_t *output_pos); + + /** + * @brief Parse a 64-Bit unsigned integer from the current parsing position. + * + * @todo add support for big-endian machines. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_uint64) (private_parser_t *this, int rule_number, u_int64_t *output_pos); + + /** + * @brief Parse a given amount of bytes and writes them to a specific location + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @param bytes number of bytes to parse + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_bytes) (private_parser_t *this, int rule_number, u_int8_t *output_pos,size_t bytes); + + /** + * @brief Parse a single Bit from the current parsing position + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer where to write the parsed result + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_bit) (private_parser_t *this, int rule_number, bool *output_pos); + + /** + * @brief Parse substructures in a list + * + * This function calls the parser recursively to parse contained substructures + * in a linked_list_t. The list must already be created. Payload defines + * the type of the substructures. parsing is continued until the specified length + * is completely parsed. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer of a linked_list where substructures are added + * @param payload_type type of the contained substructures to parse + * @param length number of bytes to parse in this list + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_list) (private_parser_t *this, int rule_number, linked_list_t **output_pos, payload_type_t payload_ype, size_t length); + + /** + * @brief Parse data from current parsing position in a chunk. + * + * This function clones length number of bytes to output_pos, without + * modifiyng them. Space will be allocated and must be freed by caller. + * + * @param this parser_t object + * @param rule_number number of current rule + * @param[out] output_pos pointer of a chunk which will point to the allocated data + * @param length number of bytes to clone + * @return + * - SUCCESS or + * - PARSE_ERROR when not successful + */ + status_t (*parse_chunk) (private_parser_t *this, int rule_number, chunk_t *output_pos, size_t length); + + /** + * Current bit for reading in input data. + */ + u_int8_t bit_pos; + + /** + * Current byte for reading in input data. + */ + u_int8_t *byte_pos; + + /** + * Input data to parse. + */ + u_int8_t *input; + + /** + * Roof of input, used for length-checking. + */ + u_int8_t *input_roof; + + /** + * Set of encoding rules for this parsing session. + */ + encoding_rule_t *rules; +}; + +/** + * Implementation of private_parser_t.parse_uint4. + */ +static status_t parse_uint4(private_parser_t *this, int rule_number, u_int8_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int8_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + switch (this->bit_pos) + { + case 0: + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos) >> 4; + } + this->bit_pos = 4; + break; + case 4: + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos) & 0x0F; + } + this->bit_pos = 0; + this->byte_pos++; + break; + default: + DBG2(DBG_ENC, " found rule %d %N on bitpos %d", + rule_number, encoding_type_names, + this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + if (output_pos != NULL) + { + DBG3(DBG_ENC, " => %d", *output_pos); + } + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint8. + */ +static status_t parse_uint8(private_parser_t *this, int rule_number, u_int8_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int8_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", + rule_number, encoding_type_names, + this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos); + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos++; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint15. + */ +static status_t parse_uint15(private_parser_t *this, int rule_number, u_int16_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int16_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos != 1) + { + DBG2(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = ntohs(*((u_int16_t*)this->byte_pos)) & ~0x8000; + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos += 2; + this->bit_pos = 0; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint16. + */ +static status_t parse_uint16(private_parser_t *this, int rule_number, u_int16_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int16_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = ntohs(*((u_int16_t*)this->byte_pos)); + + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos += 2; + + return SUCCESS; +} +/** + * Implementation of private_parser_t.parse_uint32. + */ +static status_t parse_uint32(private_parser_t *this, int rule_number, u_int32_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int32_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = ntohl(*((u_int32_t*)this->byte_pos)); + + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->byte_pos += 4; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_uint64. + */ +static status_t parse_uint64(private_parser_t *this, int rule_number, u_int64_t *output_pos) +{ + if (this->byte_pos + sizeof(u_int64_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + /* assuming little endian host order */ + *(output_pos + 1) = ntohl(*((u_int32_t*)this->byte_pos)); + *output_pos = ntohl(*(((u_int32_t*)this->byte_pos) + 1)); + + DBG3(DBG_ENC, " => %b", (void*)output_pos, sizeof(u_int64_t)); + } + this->byte_pos += 8; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_bytes. + */ +static status_t parse_bytes (private_parser_t *this, int rule_number, u_int8_t *output_pos,size_t bytes) +{ + if (this->byte_pos + bytes > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + /* caller interested in result ? */ + if (output_pos != NULL) + { + memcpy(output_pos,this->byte_pos,bytes); + + DBG3(DBG_ENC, " => %b", (void*)output_pos, bytes); + } + this->byte_pos += bytes; + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_bit. + */ +static status_t parse_bit(private_parser_t *this, int rule_number, bool *output_pos) +{ + if (this->byte_pos + sizeof(u_int8_t) > this->input_roof) + { + DBG1(DBG_ENC, " not enough input to parse rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + /* caller interested in result ? */ + if (output_pos != NULL) + { + u_int8_t mask; + mask = 0x01 << (7 - this->bit_pos); + *output_pos = *this->byte_pos & mask; + + if (*output_pos) + { + /* set to a "clean", comparable true */ + *output_pos = TRUE; + } + + DBG3(DBG_ENC, " => %d", *output_pos); + } + this->bit_pos = (this->bit_pos + 1) % 8; + if (this->bit_pos == 0) + { + this->byte_pos++; + } + + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_list. + */ +static status_t parse_list(private_parser_t *this, int rule_number, linked_list_t **output_pos, payload_type_t payload_type, size_t length) +{ + linked_list_t * list = *output_pos; + + if (length < 0) + { + DBG1(DBG_ENC, " invalid length for rule %d %N", + rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + + while (length > 0) + { + u_int8_t *pos_before = this->byte_pos; + payload_t *payload; + status_t status; + DBG2(DBG_ENC, " %d bytes left, parsing recursively %N", + length, payload_type_names, payload_type); + status = this->public.parse_payload((parser_t*)this, payload_type, &payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, " parsing of a %N substructure failed", + payload_type_names, payload_type); + return status; + } + list->insert_last(list, payload); + length -= this->byte_pos - pos_before; + } + *output_pos = list; + return SUCCESS; +} + +/** + * Implementation of private_parser_t.parse_chunk. + */ +static status_t parse_chunk(private_parser_t *this, int rule_number, chunk_t *output_pos, size_t length) +{ + if (this->byte_pos + length > this->input_roof) + { + DBG1(DBG_ENC, " not enough input (%d bytes) to parse rule %d %N", + length, rule_number, encoding_type_names, this->rules[rule_number].type); + return PARSE_ERROR; + } + if (this->bit_pos) + { + DBG1(DBG_ENC, " found rule %d %N on bitpos %d", rule_number, + encoding_type_names, this->rules[rule_number].type, this->bit_pos); + return PARSE_ERROR; + } + if (output_pos != NULL) + { + output_pos->len = length; + output_pos->ptr = malloc(length); + memcpy(output_pos->ptr, this->byte_pos, length); + } + this->byte_pos += length; + DBG3(DBG_ENC, " => %b", (void*)output_pos->ptr, length); + + return SUCCESS; +} + +/** + * Implementation of parser_t.parse_payload. + */ +static status_t parse_payload(private_parser_t *this, payload_type_t payload_type, payload_t **payload) +{ + payload_t *pld; + void *output; + size_t rule_count, payload_length = 0, spi_size = 0, attribute_length = 0; + u_int16_t ts_type = 0; + bool attribute_format = FALSE; + int rule_number; + encoding_rule_t *rule; + + /* create instance of the payload to parse */ + pld = payload_create(payload_type); + + DBG2(DBG_ENC, "parsing %N payload, %d bytes left", + payload_type_names, payload_type, this->input_roof - this->byte_pos); + + DBG3(DBG_ENC, "parsing payload from %b", + this->byte_pos, this->input_roof-this->byte_pos); + + if (pld->get_type(pld) == UNKNOWN_PAYLOAD) + { + DBG1(DBG_ENC, " payload type %d is unknown, handling as %N", + payload_type, payload_type_names, UNKNOWN_PAYLOAD); + } + + /* base pointer for output, avoids casting in every rule */ + output = pld; + + /* parse the payload with its own rulse */ + pld->get_encoding_rules(pld, &(this->rules), &rule_count); + for (rule_number = 0; rule_number < rule_count; rule_number++) + { + rule = &(this->rules[rule_number]); + DBG2(DBG_ENC, " parsing rule %d %N", + rule_number, encoding_type_names, rule->type); + switch (rule->type) + { + case U_INT_4: + { + if (this->parse_uint4(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_8: + { + if (this->parse_uint8(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_16: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_32: + { + if (this->parse_uint32(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case U_INT_64: + { + if (this->parse_uint64(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case IKE_SPI: + { + if (this->parse_bytes(this, rule_number, output + rule->offset,8) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case RESERVED_BIT: + { + if (this->parse_bit(this, rule_number, NULL) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case RESERVED_BYTE: + { + if (this->parse_uint8(this, rule_number, NULL) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case FLAG: + { + if (this->parse_bit(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case PAYLOAD_LENGTH: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + payload_length = *(u_int16_t*)(output + rule->offset); + break; + } + case HEADER_LENGTH: + { + if (this->parse_uint32(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case SPI_SIZE: + { + if (this->parse_uint8(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + spi_size = *(u_int8_t*)(output + rule->offset); + break; + } + case SPI: + { + if (this->parse_chunk(this, rule_number, output + rule->offset, spi_size) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case PROPOSALS: + { + size_t proposals_length = payload_length - SA_PAYLOAD_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, PROPOSAL_SUBSTRUCTURE, proposals_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TRANSFORMS: + { + size_t transforms_length = payload_length - spi_size - PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, TRANSFORM_SUBSTRUCTURE, transforms_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TRANSFORM_ATTRIBUTES: + { + size_t transform_a_length = payload_length - TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, TRANSFORM_ATTRIBUTE, transform_a_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CONFIGURATION_ATTRIBUTES: + { + size_t configuration_attributes_length = payload_length - CP_PAYLOAD_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, CONFIGURATION_ATTRIBUTE, configuration_attributes_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case ATTRIBUTE_FORMAT: + { + if (this->parse_bit(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_format = *(bool*)(output + rule->offset); + break; + } + case ATTRIBUTE_TYPE: + { + if (this->parse_uint15(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_format = *(bool*)(output + rule->offset); + break; + } + case CONFIGURATION_ATTRIBUTE_LENGTH: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_length = *(u_int16_t*)(output + rule->offset); + break; + } + case ATTRIBUTE_LENGTH_OR_VALUE: + { + if (this->parse_uint16(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + attribute_length = *(u_int16_t*)(output + rule->offset); + break; + } + case ATTRIBUTE_VALUE: + { + if (attribute_format == FALSE) + { + if (this->parse_chunk(this, rule_number, output + rule->offset, attribute_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + } + break; + } + case NONCE_DATA: + { + size_t nonce_length = payload_length - NONCE_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, nonce_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case ID_DATA: + { + size_t data_length = payload_length - ID_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case AUTH_DATA: + { + size_t data_length = payload_length - AUTH_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CERT_DATA: + { + size_t data_length = payload_length - CERT_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CERTREQ_DATA: + { + size_t data_length = payload_length - CERTREQ_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case EAP_DATA: + { + size_t data_length = payload_length - EAP_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case SPIS: + { + size_t data_length = payload_length - DELETE_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case VID_DATA: + { + size_t data_length = payload_length - VENDOR_ID_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case CONFIGURATION_ATTRIBUTE_VALUE: + { + size_t data_length = attribute_length; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case KEY_EXCHANGE_DATA: + { + size_t keydata_length = payload_length - KE_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, keydata_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case NOTIFICATION_DATA: + { + size_t notify_length = payload_length - NOTIFY_PAYLOAD_HEADER_LENGTH - spi_size; + if (this->parse_chunk(this, rule_number, output + rule->offset, notify_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case ENCRYPTED_DATA: + { + size_t data_length = payload_length - ENCRYPTION_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TS_TYPE: + { + if (this->parse_uint8(this, rule_number, output + rule->offset) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + ts_type = *(u_int8_t*)(output + rule->offset); + break; + } + case ADDRESS: + { + size_t address_length = (ts_type == TS_IPV4_ADDR_RANGE) ? 4 : 16; + if (this->parse_chunk(this, rule_number, output + rule->offset,address_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case TRAFFIC_SELECTORS: + { + size_t traffic_selectors_length = payload_length - TS_PAYLOAD_HEADER_LENGTH; + if (this->parse_list(this, rule_number, output + rule->offset, TRAFFIC_SELECTOR_SUBSTRUCTURE, traffic_selectors_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + case UNKNOWN_PAYLOAD: + { + size_t unknown_payload_data_length = payload_length - UNKNOWN_PAYLOAD_HEADER_LENGTH; + if (this->parse_chunk(this, rule_number, output + rule->offset, unknown_payload_data_length) != SUCCESS) + { + pld->destroy(pld); + return PARSE_ERROR; + } + break; + } + default: + { + DBG1(DBG_ENC, " no rule to parse rule %d %N", + rule_number, encoding_type_names, rule->type); + pld->destroy(pld); + return PARSE_ERROR; + } + } + /* process next rulue */ + rule++; + } + + *payload = pld; + DBG2(DBG_ENC, "parsing %N payload finished", + payload_type_names, payload_type); + return SUCCESS; +} + +/** + * Implementation of parser_t.get_remaining_byte_count. + */ +static int get_remaining_byte_count (private_parser_t *this) +{ + int count = (this->input_roof - this->byte_pos); + return count; +} + +/** + * Implementation of parser_t.reset_context. + */ +static void reset_context (private_parser_t *this) +{ + this->byte_pos = this->input; + this->bit_pos = 0; +} + +/** + * Implementation of parser_t.destroy. + */ +static void destroy(private_parser_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +parser_t *parser_create(chunk_t data) +{ + private_parser_t *this = malloc_thing(private_parser_t); + + this->public.parse_payload = (status_t(*)(parser_t*,payload_type_t,payload_t**)) parse_payload; + this->public.reset_context = (void(*)(parser_t*)) reset_context; + this->public.get_remaining_byte_count = (int (*) (parser_t *))get_remaining_byte_count; + this->public.destroy = (void(*)(parser_t*)) destroy; + + this->parse_uint4 = parse_uint4; + this->parse_uint8 = parse_uint8; + this->parse_uint15 = parse_uint15; + this->parse_uint16 = parse_uint16; + this->parse_uint32 = parse_uint32; + this->parse_uint64 = parse_uint64; + this->parse_bytes = parse_bytes; + this->parse_bit = parse_bit; + this->parse_list = parse_list; + this->parse_chunk = parse_chunk; + + this->input = data.ptr; + this->byte_pos = data.ptr; + this->bit_pos = 0; + this->input_roof = data.ptr + data.len; + + return (parser_t*)this; +} diff --git a/src/charon/encoding/parser.h b/src/charon/encoding/parser.h new file mode 100644 index 000000000..e9978524c --- /dev/null +++ b/src/charon/encoding/parser.h @@ -0,0 +1,95 @@ +/** + * @file parser.h + * + * @brief Interface of parser_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PARSER_H_ +#define PARSER_H_ + +typedef struct parser_t parser_t; + +#include +#include +#include + +/** + * @brief A parser_t class to parse IKEv2 payloads. + * + * A parser is used for parsing one chunk of data. Multiple + * payloads can be parsed out of the chunk using parse_payload. + * The parser remains the state until destroyed. + * + * @b Constructors: + * - parser_create() + * + * @ingroup encoding + */ +struct parser_t { + + /** + * @brief Parses the next payload. + * + * @warning Caller is responsible for freeing allocated payload. + * + * Rules for parsing are described in the payload definition. + * + * @param this parser_t bject + * @param payload_type payload type to parse + * @param[out] payload pointer where parsed payload was allocated + * @return + * - SUCCESSFUL if succeeded, + * - PARSE_ERROR if corrupted/invalid data found + */ + status_t (*parse_payload) (parser_t *this, payload_type_t payload_type, payload_t **payload); + + /** + * Gets the remaining byte count which is not currently parsed. + * + * @param parser parser_t object + */ + int (*get_remaining_byte_count) (parser_t *this); + + /** + * @brief Resets the current parser context. + * + * @param parser parser_t object + */ + void (*reset_context) (parser_t *this); + + /** + * @brief Destroys a parser_t object. + * + * @param parser parser_t object + */ + void (*destroy) (parser_t *this); +}; + +/** + * @brief Constructor to create a parser_t object. + * + * @param data chunk of data to parse with this parser_t object + * @return parser_t object + * + * @ingroup encoding + */ +parser_t *parser_create(chunk_t data); + +#endif /*PARSER_H_*/ diff --git a/src/charon/encoding/payloads/auth_payload.c b/src/charon/encoding/payloads/auth_payload.c new file mode 100644 index 000000000..256d6c8a4 --- /dev/null +++ b/src/charon/encoding/payloads/auth_payload.c @@ -0,0 +1,265 @@ +/** + * @file auth_payload.h + * + * @brief Implementation of auth_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "auth_payload.h" + +#include + + +typedef struct private_auth_payload_t private_auth_payload_t; + +/** + * Private data of an auth_payload_t object. + * + */ +struct private_auth_payload_t { + + /** + * Public auth_payload_t interface. + */ + auth_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Method of the AUTH Data. + */ + u_int8_t auth_method; + + /** + * The contained auth data value. + */ + chunk_t auth_data; +}; + +/** + * Encoding rules to parse or generate a AUTH payload + * + * The defined offsets are the positions in a object of type + * private_auth_payload_t. + */ +encoding_rule_t auth_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_auth_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_auth_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_auth_payload_t, payload_length)}, + /* 1 Byte AUTH type*/ + { U_INT_8, offsetof(private_auth_payload_t, auth_method) }, + /* 3 reserved bytes */ + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* some auth data bytes, length is defined in PAYLOAD_LENGTH */ + { AUTH_DATA, offsetof(private_auth_payload_t, auth_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Auth Method ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Authentication Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_auth_payload_t *this) +{ + if (this->auth_method == 0 || + (this->auth_method >= 4 && this->auth_method <= 200)) + { + /* reserved IDs */ + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of auth_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_auth_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = auth_payload_encodings; + *rule_count = sizeof(auth_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_auth_payload_t *this) +{ + return AUTHENTICATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_auth_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_auth_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_auth_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of auth_payload_t.set_auth_method. + */ +static void set_auth_method (private_auth_payload_t *this, auth_method_t method) +{ + this->auth_method = method; +} + +/** + * Implementation of auth_payload_t.get_auth_method. + */ +static auth_method_t get_auth_method (private_auth_payload_t *this) +{ + return (this->auth_method); +} + +/** + * Implementation of auth_payload_t.set_data. + */ +static void set_data (private_auth_payload_t *this, chunk_t data) +{ + if (this->auth_data.ptr != NULL) + { + chunk_free(&(this->auth_data)); + } + this->auth_data.ptr = clalloc(data.ptr,data.len); + this->auth_data.len = data.len; + this->payload_length = AUTH_PAYLOAD_HEADER_LENGTH + this->auth_data.len; +} + +/** + * Implementation of auth_payload_t.get_data. + */ +static chunk_t get_data (private_auth_payload_t *this) +{ + return (this->auth_data); +} + +/** + * Implementation of auth_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_auth_payload_t *this) +{ + chunk_t cloned_data; + if (this->auth_data.ptr == NULL) + { + return (this->auth_data); + } + cloned_data.ptr = clalloc(this->auth_data.ptr,this->auth_data.len); + cloned_data.len = this->auth_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and auth_payload_t.destroy. + */ +static void destroy(private_auth_payload_t *this) +{ + if (this->auth_data.ptr != NULL) + { + chunk_free(&(this->auth_data)); + } + + free(this); +} + +/* + * Described in header + */ +auth_payload_t *auth_payload_create() +{ + private_auth_payload_t *this = malloc_thing(private_auth_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (auth_payload_t *)) destroy; + this->public.set_auth_method = (void (*) (auth_payload_t *,auth_method_t)) set_auth_method; + this->public.get_auth_method = (auth_method_t (*) (auth_payload_t *)) get_auth_method; + this->public.set_data = (void (*) (auth_payload_t *,chunk_t)) set_data; + this->public.get_data_clone = (chunk_t (*) (auth_payload_t *)) get_data_clone; + this->public.get_data = (chunk_t (*) (auth_payload_t *)) get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =AUTH_PAYLOAD_HEADER_LENGTH; + this->auth_data = chunk_empty; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/auth_payload.h b/src/charon/encoding/payloads/auth_payload.h new file mode 100644 index 000000000..2db82ec0b --- /dev/null +++ b/src/charon/encoding/payloads/auth_payload.h @@ -0,0 +1,121 @@ +/** + * @file auth_payload.h + * + * @brief Interface of auth_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef AUTH_PAYLOAD_H_ +#define AUTH_PAYLOAD_H_ + +typedef struct auth_payload_t auth_payload_t; + +#include +#include +#include + +/** + * Length of a auth payload without the auth data in bytes. + * + * @ingroup payloads + */ +#define AUTH_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2 AUTH payload. + * + * The AUTH payload format is described in RFC section 3.8. + * + * @b Constructors: + * - auth_payload_create() + * + * @ingroup payloads + */ +struct auth_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the AUTH method. + * + * @param this calling auth_payload_t object + * @param method auth_method_t to use + */ + void (*set_auth_method) (auth_payload_t *this, auth_method_t method); + + /** + * @brief Get the AUTH method. + * + * @param this calling auth_payload_t object + * @return auth_method_t used + */ + auth_method_t (*get_auth_method) (auth_payload_t *this); + + /** + * @brief Set the AUTH data. + * + * Data are getting cloned. + * + * @param this calling auth_payload_t object + * @param data AUTH data as chunk_t + */ + void (*set_data) (auth_payload_t *this, chunk_t data); + + /** + * @brief Get the AUTH data. + * + * Returned data are a copy of the internal one. + * + * @param this calling auth_payload_t object + * @return AUTH data as chunk_t + */ + chunk_t (*get_data_clone) (auth_payload_t *this); + + /** + * @brief Get the AUTH data. + * + * Returned data are NOT copied + * + * @param this calling auth_payload_t object + * @return AUTH data as chunk_t + */ + chunk_t (*get_data) (auth_payload_t *this); + + /** + * @brief Destroys an auth_payload_t object. + * + * @param this auth_payload_t object to destroy + */ + void (*destroy) (auth_payload_t *this); +}; + +/** + * @brief Creates an empty auth_payload_t object. + * + * @return auth_payload_t object + * + * @ingroup payloads + */ +auth_payload_t *auth_payload_create(void); + + +#endif /* AUTH_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/cert_payload.c b/src/charon/encoding/payloads/cert_payload.c new file mode 100644 index 000000000..c456f4936 --- /dev/null +++ b/src/charon/encoding/payloads/cert_payload.c @@ -0,0 +1,290 @@ +/** + * @file cert_payload.c + * + * @brief Implementation of cert_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "cert_payload.h" + + +ENUM(cert_encoding_names, CERT_NONE, CERT_OCSP_CONTENT, + "CERT_NONE", + "CERT_PKCS7_WRAPPED_X509", + "CERT_PGP", + "CERT_DNS_SIGNED_KEY", + "CERT_X509_SIGNATURE", + "CERT_X509_KEY_EXCHANGE", + "CERT_KERBEROS_TOKENS", + "CERT_CRL", + "CERT_ARL", + "CERT_SPKI", + "CERT_X509_ATTRIBUTE", + "CERT_RAW_RSA_KEY", + "CERT_X509_HASH_AND_URL", + "CERT_X509_HASH_AND_URL_BUNDLE", + "CERT_OCSP_CONTENT", +); + +typedef struct private_cert_payload_t private_cert_payload_t; + +/** + * Private data of an cert_payload_t object. + * + */ +struct private_cert_payload_t { + /** + * Public cert_payload_t interface. + */ + cert_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Encoding of the CERT Data. + */ + u_int8_t cert_encoding; + + /** + * The contained cert data value. + */ + chunk_t cert_data; +}; + +/** + * Encoding rules to parse or generate a CERT payload + * + * The defined offsets are the positions in a object of type + * private_cert_payload_t. + * + */ +encoding_rule_t cert_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_cert_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_cert_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_cert_payload_t, payload_length)}, + /* 1 Byte CERT type*/ + { U_INT_8, offsetof(private_cert_payload_t, cert_encoding) }, + /* some cert data bytes, length is defined in PAYLOAD_LENGTH */ + { CERT_DATA, offsetof(private_cert_payload_t, cert_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Cert Encoding ! ! + +-+-+-+-+-+-+-+-+ ! + ~ Certificate Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_cert_payload_t *this) +{ + if ((this->cert_encoding == 0) || + ((this->cert_encoding >= CERT_ROOF) && (this->cert_encoding <= 200))) + { + /* reserved IDs */ + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of cert_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_cert_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = cert_payload_encodings; + *rule_count = sizeof(cert_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_cert_payload_t *this) +{ + return CERTIFICATE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_cert_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_cert_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_cert_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of cert_payload_t.set_cert_encoding. + */ +static void set_cert_encoding (private_cert_payload_t *this, cert_encoding_t encoding) +{ + this->cert_encoding = encoding; +} + +/** + * Implementation of cert_payload_t.get_cert_encoding. + */ +static cert_encoding_t get_cert_encoding (private_cert_payload_t *this) +{ + return (this->cert_encoding); +} + +/** + * Implementation of cert_payload_t.set_data. + */ +static void set_data (private_cert_payload_t *this, chunk_t data) +{ + if (this->cert_data.ptr != NULL) + { + chunk_free(&(this->cert_data)); + } + this->cert_data.ptr = clalloc(data.ptr,data.len); + this->cert_data.len = data.len; + this->payload_length = CERT_PAYLOAD_HEADER_LENGTH + this->cert_data.len; +} + +/** + * Implementation of cert_payload_t.get_data. + */ +static chunk_t get_data (private_cert_payload_t *this) +{ + return (this->cert_data); +} + +/** + * Implementation of cert_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_cert_payload_t *this) +{ + chunk_t cloned_data; + if (this->cert_data.ptr == NULL) + { + return (this->cert_data); + } + cloned_data.ptr = clalloc(this->cert_data.ptr,this->cert_data.len); + cloned_data.len = this->cert_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and cert_payload_t.destroy. + */ +static void destroy(private_cert_payload_t *this) +{ + if (this->cert_data.ptr != NULL) + { + chunk_free(&(this->cert_data)); + } + + free(this); +} + +/* + * Described in header + */ +cert_payload_t *cert_payload_create() +{ + private_cert_payload_t *this = malloc_thing(private_cert_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t*))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t*,encoding_rule_t**, size_t*))get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t*))get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t*))get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t*,payload_type_t))set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t*))get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t*))destroy; + + /* public functions */ + this->public.destroy = (void (*) (cert_payload_t*))destroy; + this->public.set_cert_encoding = (void (*) (cert_payload_t*,cert_encoding_t))set_cert_encoding; + this->public.get_cert_encoding = (cert_encoding_t (*) (cert_payload_t*))get_cert_encoding; + this->public.set_data = (void (*) (cert_payload_t*,chunk_t))set_data; + this->public.get_data_clone = (chunk_t (*) (cert_payload_t*))get_data_clone; + this->public.get_data = (chunk_t (*) (cert_payload_t*))get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = CERT_PAYLOAD_HEADER_LENGTH; + this->cert_data = chunk_empty; + + return (&(this->public)); +} + +/* + * Described in header + */ +cert_payload_t *cert_payload_create_from_x509(x509_t *cert) +{ + cert_payload_t *this = cert_payload_create(); + + this->set_cert_encoding(this, CERT_X509_SIGNATURE); + this->set_data(this, cert->get_certificate(cert)); + return this; +} diff --git a/src/charon/encoding/payloads/cert_payload.h b/src/charon/encoding/payloads/cert_payload.h new file mode 100644 index 000000000..bcb961398 --- /dev/null +++ b/src/charon/encoding/payloads/cert_payload.h @@ -0,0 +1,166 @@ +/** + * @file cert_payload.h + * + * @brief Interface of cert_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CERT_PAYLOAD_H_ +#define CERT_PAYLOAD_H_ + +typedef enum cert_encoding_t cert_encoding_t; +typedef struct cert_payload_t cert_payload_t; + +#include +#include +#include + +/** + * Length of a cert payload without the cert data in bytes. + * + * @ingroup payloads + */ +#define CERT_PAYLOAD_HEADER_LENGTH 5 + +/** + * @brief Certificate encoding, as described in IKEv2 RFC section 3.6 + * + * @ingroup payloads + */ +enum cert_encoding_t { + CERT_NONE = 0, + CERT_PKCS7_WRAPPED_X509 = 1, + CERT_PGP = 2, + CERT_DNS_SIGNED_KEY = 3, + CERT_X509_SIGNATURE = 4, + CERT_KERBEROS_TOKEN = 6, + CERT_CRL = 7, + CERT_ARL = 8, + CERT_SPKI = 9, + CERT_X509_ATTRIBUTE = 10, + CERT_RAW_RSA_KEY = 11, + CERT_X509_HASH_AND_URL = 12, + CERT_X509_HASH_AND_URL_BUNDLE = 13, + CERT_OCSP_CONTENT = 14, /* from RFC 4806 */ + CERT_ROOF = 15 +}; + +/** + * string mappings for cert_encoding_t. + * + * @ingroup payloads + */ +extern enum_name_t *cert_encoding_names; + +/** + * @brief Class representing an IKEv2 CERT payload. + * + * The CERT payload format is described in RFC section 3.6. + * This is just a dummy implementation to fullfill the standards + * requirements. A full implementation would offer setters/getters + * for the different encoding types. + * + * @b Constructors: + * - cert_payload_create() + * + * @todo Implement setters/getters for the different certificate encodings. + * + * @ingroup payloads + */ +struct cert_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the CERT encoding. + * + * @param this calling cert_payload_t object + * @param encoding CERT encoding + */ + void (*set_cert_encoding) (cert_payload_t *this, cert_encoding_t encoding); + + /** + * @brief Get the CERT encoding. + * + * @param this calling cert_payload_t object + * @return Encoding of the CERT + */ + cert_encoding_t (*get_cert_encoding) (cert_payload_t *this); + + /** + * @brief Set the CERT data. + * + * Data are getting cloned. + * + * @param this calling cert_payload_t object + * @param data CERT data as chunk_t + */ + void (*set_data) (cert_payload_t *this, chunk_t data); + + /** + * @brief Get the CERT data. + * + * Returned data are a copy of the internal one. + * + * @param this calling cert_payload_t object + * @return CERT data as chunk_t + */ + chunk_t (*get_data_clone) (cert_payload_t *this); + + /** + * @brief Get the CERT data. + * + * Returned data are NOT copied. + * + * @param this calling cert_payload_t object + * @return CERT data as chunk_t + */ + chunk_t (*get_data) (cert_payload_t *this); + + /** + * @brief Destroys an cert_payload_t object. + * + * @param this cert_payload_t object to destroy + */ + void (*destroy) (cert_payload_t *this); +}; + +/** + * @brief Creates an empty cert_payload_t object. + * + * @return cert_payload_t object + * + * @ingroup payloads + */ +cert_payload_t *cert_payload_create(void); + +/** + * @brief Creates a cert_payload_t object with an X.509 certificate. + * + * @param cert X.509 certificate + * @return cert_payload_t object + * + * @ingroup payloads + */ +cert_payload_t *cert_payload_create_from_x509(x509_t *cert); + +#endif /* CERT_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/certreq_payload.c b/src/charon/encoding/payloads/certreq_payload.c new file mode 100644 index 000000000..46663811a --- /dev/null +++ b/src/charon/encoding/payloads/certreq_payload.c @@ -0,0 +1,335 @@ +/** + * @file certreq_payload.c + * + * @brief Implementation of certreq_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include +#include +#include + +#include "certreq_payload.h" + + +typedef struct private_certreq_payload_t private_certreq_payload_t; + +/** + * Private data of an certreq_payload_t object. + * + */ +struct private_certreq_payload_t { + /** + * Public certreq_payload_t interface. + */ + certreq_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Encoding of the CERT Data. + */ + u_int8_t cert_encoding; + + /** + * The contained certreq data value. + */ + chunk_t certreq_data; +}; + +/** + * Encoding rules to parse or generate a CERTREQ payload + * + * The defined offsets are the positions in a object of type + * private_certreq_payload_t. + * + */ +encoding_rule_t certreq_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_certreq_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_certreq_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_certreq_payload_t, payload_length)}, + /* 1 Byte CERTREQ type*/ + { U_INT_8, offsetof(private_certreq_payload_t, cert_encoding)}, + /* some certreq data bytes, length is defined in PAYLOAD_LENGTH */ + { CERTREQ_DATA, offsetof(private_certreq_payload_t, certreq_data)} +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Cert Encoding ! ! + +-+-+-+-+-+-+-+-+ ! + ~ Certification Authority ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_certreq_payload_t *this) +{ + if ((this->cert_encoding == 0) || + ((this->cert_encoding >= CERT_ROOF) && (this->cert_encoding <= 200))) + { + /* reserved IDs */ + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of certreq_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_certreq_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = certreq_payload_encodings; + *rule_count = sizeof(certreq_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_certreq_payload_t *this) +{ + return CERTIFICATE_REQUEST; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_certreq_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_certreq_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_certreq_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of certreq_payload_t.set_cert_encoding. + */ +static void set_cert_encoding (private_certreq_payload_t *this, cert_encoding_t encoding) +{ + this->cert_encoding = encoding; +} + +/** + * Implementation of certreq_payload_t.get_cert_encoding. + */ +static cert_encoding_t get_cert_encoding (private_certreq_payload_t *this) +{ + return (this->cert_encoding); +} + +/** + * Implementation of certreq_payload_t.set_data. + */ +static void set_data (private_certreq_payload_t *this, chunk_t data) +{ + if (this->certreq_data.ptr != NULL) + { + chunk_free(&(this->certreq_data)); + } + this->certreq_data.ptr = clalloc(data.ptr,data.len); + this->certreq_data.len = data.len; + this->payload_length = CERTREQ_PAYLOAD_HEADER_LENGTH + this->certreq_data.len; +} + +/** + * Implementation of certreq_payload_t.get_data. + */ +static chunk_t get_data (private_certreq_payload_t *this) +{ + return (this->certreq_data); +} + +/** + * Implementation of certreq_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_certreq_payload_t *this) +{ + chunk_t cloned_data; + if (this->certreq_data.ptr == NULL) + { + return (this->certreq_data); + } + cloned_data.ptr = clalloc(this->certreq_data.ptr,this->certreq_data.len); + cloned_data.len = this->certreq_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and certreq_payload_t.destroy. + */ +static void destroy(private_certreq_payload_t *this) +{ + if (this->certreq_data.ptr != NULL) + { + chunk_free(&(this->certreq_data)); + } + + free(this); +} + +/* + * Described in header + */ +certreq_payload_t *certreq_payload_create() +{ + private_certreq_payload_t *this = malloc_thing(private_certreq_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t*))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t*,encoding_rule_t**,size_t*))get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t*))get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t*))get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t*,payload_type_t))set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t*))get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t*))destroy; + + /* public functions */ + this->public.destroy = (void (*) (certreq_payload_t*)) destroy; + this->public.set_cert_encoding = (void (*) (certreq_payload_t*,cert_encoding_t))set_cert_encoding; + this->public.get_cert_encoding = (cert_encoding_t (*) (certreq_payload_t*))get_cert_encoding; + this->public.set_data = (void (*) (certreq_payload_t*,chunk_t))set_data; + this->public.get_data_clone = (chunk_t (*) (certreq_payload_t*))get_data_clone; + this->public.get_data = (chunk_t (*) (certreq_payload_t*))get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =CERTREQ_PAYLOAD_HEADER_LENGTH; + this->certreq_data = chunk_empty; + + return (&(this->public)); +} + +/* + * Described in header + */ +certreq_payload_t *certreq_payload_create_from_cacert(identification_t *id) +{ + x509_t *cacert; + rsa_public_key_t *pubkey; + chunk_t keyid; + certreq_payload_t *this; + + cacert = charon->credentials->get_auth_certificate(charon->credentials, AUTH_CA, id); + if (cacert == NULL) + { + /* no such CA cert */ + return NULL; + } + + this = certreq_payload_create(); + pubkey = cacert->get_public_key(cacert); + keyid = pubkey->get_keyid(pubkey); + + DBG2(DBG_IKE, "requesting certificate issued by '%D'", id); + DBG2(DBG_IKE, " with keyid %#B", &keyid); + + this->set_cert_encoding(this, CERT_X509_SIGNATURE); + this->set_data(this, keyid); + return this; +} + +/* + * Described in header + */ +certreq_payload_t *certreq_payload_create_from_cacerts(void) +{ + certreq_payload_t *this; + chunk_t keyids; + u_char *pos; + ca_info_t *cainfo; + + iterator_t *iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + int count = iterator->get_count(iterator); + + if (count == 0) + { + iterator->destroy(iterator); + return NULL; + } + + this = certreq_payload_create(); + keyids = chunk_alloc(count * HASH_SIZE_SHA1); + pos = keyids.ptr; + + while (iterator->iterate(iterator, (void**)&cainfo)) + { + x509_t *cacert = cainfo->get_certificate(cainfo); + chunk_t keyid = cacert->get_keyid(cacert); + + DBG2(DBG_IKE, "requesting certificate issued by '%D'", cacert->get_subject(cacert)); + DBG2(DBG_IKE, " with keyid %#B", &keyid); + memcpy(pos, keyid.ptr, keyid.len); + pos += HASH_SIZE_SHA1; + } + iterator->destroy(iterator); + + this->set_cert_encoding(this, CERT_X509_SIGNATURE); + this->set_data(this, keyids); + free(keyids.ptr); + return this; +} diff --git a/src/charon/encoding/payloads/certreq_payload.h b/src/charon/encoding/payloads/certreq_payload.h new file mode 100644 index 000000000..2985fdae1 --- /dev/null +++ b/src/charon/encoding/payloads/certreq_payload.h @@ -0,0 +1,144 @@ +/** + * @file certreq_payload.h + * + * @brief Interface of certreq_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CERTREQ_PAYLOAD_H_ +#define CERTREQ_PAYLOAD_H_ + +typedef struct certreq_payload_t certreq_payload_t; + +#include +#include +#include + +/** + * Length of a CERTREQ payload without the CERTREQ data in bytes. + * + * @ingroup payloads + */ +#define CERTREQ_PAYLOAD_HEADER_LENGTH 5 + + +/** + * @brief Class representing an IKEv2 CERTREQ payload. + * + * The CERTREQ payload format is described in RFC section 3.7. + * This is just a dummy implementation to fullfill the standards + * requirements. A full implementation would offer setters/getters + * for the different encoding types. + * + * @b Constructors: + * - certreq_payload_create() + * + * @todo Implement payload functionality. + * + * @ingroup payloads + */ +struct certreq_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the CERT encoding. + * + * @param this calling certreq_payload_t object + * @param encoding CERT encoding + */ + void (*set_cert_encoding) (certreq_payload_t *this, cert_encoding_t encoding); + + /** + * @brief Get the CERT encoding. + * + * @param this calling certreq_payload_t object + * @return Encoding of the CERT + */ + cert_encoding_t (*get_cert_encoding) (certreq_payload_t *this); + + /** + * @brief Set the CERTREQ data. + * + * Data are getting cloned. + * + * @param this calling certreq_payload_t object + * @param data CERTREQ data as chunk_t + */ + void (*set_data) (certreq_payload_t *this, chunk_t data); + + /** + * @brief Get the CERTREQ data. + * + * Returned data are a copy of the internal one. + * + * @param this calling certreq_payload_t object + * @return CERTREQ data as chunk_t + */ + chunk_t (*get_data_clone) (certreq_payload_t *this); + + /** + * @brief Get the CERTREQ data. + * + * Returned data are NOT copied. + * + * @param this calling certreq_payload_t object + * @return CERTREQ data as chunk_t + */ + chunk_t (*get_data) (certreq_payload_t *this); + + /** + * @brief Destroys an certreq_payload_t object. + * + * @param this certreq_payload_t object to destroy + */ + void (*destroy) (certreq_payload_t *this); +}; + +/** + * @brief Creates an empty certreq_payload_t object. + * + * @return certreq_payload_t object + * + * @ingroup payloads + */ +certreq_payload_t *certreq_payload_create(void); + +/** + * @brief Creates a certreq_payload_t object from a ca certificate + * + * @param id subject distinguished name of CA certificate + * @return certreq_payload_t object + * + * @ingroup payloads + */ +certreq_payload_t *certreq_payload_create_from_cacert(identification_t *id); + +/** + * @brief Creates a certreq_payload_t object from all ca certificates + * + * @return certreq_payload_t object + * + * @ingroup payloads + */ +certreq_payload_t *certreq_payload_create_from_cacerts(void); + +#endif /* CERTREQ_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/configuration_attribute.c b/src/charon/encoding/payloads/configuration_attribute.c new file mode 100644 index 000000000..0aa82169f --- /dev/null +++ b/src/charon/encoding/payloads/configuration_attribute.c @@ -0,0 +1,313 @@ +/** + * @file configuration_attribute.c + * + * @brief Implementation of configuration_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "configuration_attribute.h" + +#include +#include +#include + + +typedef struct private_configuration_attribute_t private_configuration_attribute_t; + +/** + * Private data of an configuration_attribute_t object. + * + */ +struct private_configuration_attribute_t { + /** + * Public configuration_attribute_t interface. + */ + configuration_attribute_t public; + + /** + * Type of the attribute. + */ + u_int16_t attribute_type; + + /** + * Length of the attribute. + */ + u_int16_t attribute_length; + + /** + * Attribute value as chunk. + */ + chunk_t attribute_value; +}; + +ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS, + "INTERNAL_IP4_ADDRESS", + "INTERNAL_IP4_NETMASK", + "INTERNAL_IP4_DNS", + "INTERNAL_IP4_NBNS", + "INTERNAL_ADDRESS_EXPIRY", + "INTERNAL_IP4_DHCP", + "APPLICATION_VERSION", + "INTERNAL_IP6_ADDRESS"); +ENUM_NEXT(configuration_attribute_type_names, INTERNAL_IP6_DNS, INTERNAL_IP6_SUBNET, INTERNAL_IP6_ADDRESS, + "INTERNAL_IP6_DNS", + "INTERNAL_IP6_NBNS", + "INTERNAL_IP6_DHCP", + "INTERNAL_IP4_SUBNET", + "SUPPORTED_ATTRIBUTES", + "INTERNAL_IP6_SUBNET"); +ENUM_END(configuration_attribute_type_names, INTERNAL_IP6_SUBNET); + +/** + * Encoding rules to parse or generate a configuration attribute. + * + * The defined offsets are the positions in a object of type + * private_configuration_attribute_t. + * + */ +encoding_rule_t configuration_attribute_encodings[] = { + + { RESERVED_BIT, 0 }, + /* type of the attribute as 15 bit unsigned integer */ + { ATTRIBUTE_TYPE, offsetof(private_configuration_attribute_t, attribute_type) }, + /* Length of attribute value */ + { CONFIGURATION_ATTRIBUTE_LENGTH, offsetof(private_configuration_attribute_t, attribute_length)}, + /* Value of attribute if attribute format flag is zero */ + { CONFIGURATION_ATTRIBUTE_VALUE, offsetof(private_configuration_attribute_t, attribute_value)} +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !R| Attribute Type ! Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ~ Value ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_configuration_attribute_t *this) +{ + bool failed = FALSE; + + if (this->attribute_length != this->attribute_value.len) + { + DBG1(DBG_ENC, "invalid attribute length"); + return FAILED; + } + + switch (this->attribute_type) + { + case INTERNAL_IP4_ADDRESS: + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_NBNS: + case INTERNAL_ADDRESS_EXPIRY: + case INTERNAL_IP4_DHCP: + if (this->attribute_length != 0 && this->attribute_length != 4) + { + failed = TRUE; + } + break; + case INTERNAL_IP4_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 8) + { + failed = TRUE; + } + break; + case INTERNAL_IP6_ADDRESS: + case INTERNAL_IP6_SUBNET: + if (this->attribute_length != 0 && this->attribute_length != 17) + { + failed = TRUE; + } + break; + case INTERNAL_IP6_DNS: + case INTERNAL_IP6_NBNS: + case INTERNAL_IP6_DHCP: + if (this->attribute_length != 0 && this->attribute_length != 16) + { + failed = TRUE; + } + break; + case SUPPORTED_ATTRIBUTES: + if (this->attribute_length % 2) + { + failed = TRUE; + } + break; + case APPLICATION_VERSION: + /* any length acceptable */ + break; + default: + DBG1(DBG_ENC, "unknown attribute type %N", + configuration_attribute_type_names, this->attribute_type); + return FAILED; + } + + if (failed) + { + DBG1(DBG_ENC, "invalid attribute length %d for %N", + this->attribute_length, configuration_attribute_type_names, + this->attribute_type); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_configuration_attribute_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = configuration_attribute_encodings; + *rule_count = sizeof(configuration_attribute_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_configuration_attribute_t *this) +{ + return CONFIGURATION_ATTRIBUTE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_configuration_attribute_t *this) +{ + return (NO_PAYLOAD); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_configuration_attribute_t *this,payload_type_t type) +{ +} + +/** + * Implementation of configuration_attribute_t.get_length. + */ +static size_t get_length(private_configuration_attribute_t *this) +{ + return (this->attribute_value.len + CONFIGURATION_ATTRIBUTE_HEADER_LENGTH); +} + +/** + * Implementation of configuration_attribute_t.set_value. + */ +static void set_value(private_configuration_attribute_t *this, chunk_t value) +{ + if (this->attribute_value.ptr != NULL) + { + /* free existing value */ + chunk_free(&(this->attribute_value)); + } + + this->attribute_value.ptr = clalloc(value.ptr,value.len); + this->attribute_value.len = value.len; + + this->attribute_length = this->attribute_value.len; +} + +/** + * Implementation of configuration_attribute_t.get_value. + */ +static chunk_t get_value (private_configuration_attribute_t *this) +{ + return this->attribute_value; +} + +/** + * Implementation of configuration_attribute_t.set_type. + */ +static void set_attribute_type (private_configuration_attribute_t *this, u_int16_t type) +{ + this->attribute_type = type & 0x7FFF; +} + +/** + * Implementation of configuration_attribute_t.get_type. + */ +static u_int16_t get_attribute_type (private_configuration_attribute_t *this) +{ + return this->attribute_type; +} + +/** + * Implementation of configuration_attribute_t.get_length. + */ +static u_int16_t get_attribute_length (private_configuration_attribute_t *this) +{ + return this->attribute_length; +} + + +/** + * Implementation of configuration_attribute_t.destroy and payload_t.destroy. + */ +static void destroy(private_configuration_attribute_t *this) +{ + if (this->attribute_value.ptr != NULL) + { + free(this->attribute_value.ptr); + } + free(this); +} + +/* + * Described in header. + */ +configuration_attribute_t *configuration_attribute_create() +{ + private_configuration_attribute_t *this = malloc_thing(private_configuration_attribute_t); + + /* payload interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.set_value = (void (*) (configuration_attribute_t *,chunk_t)) set_value; + this->public.get_value = (chunk_t (*) (configuration_attribute_t *)) get_value; + this->public.set_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type; + this->public.get_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type; + this->public.get_length = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_length; + this->public.destroy = (void (*) (configuration_attribute_t *)) destroy; + + /* set default values of the fields */ + this->attribute_type = 0; + this->attribute_value = chunk_empty; + this->attribute_length = 0; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/configuration_attribute.h b/src/charon/encoding/payloads/configuration_attribute.h new file mode 100644 index 000000000..5c4f65b14 --- /dev/null +++ b/src/charon/encoding/payloads/configuration_attribute.h @@ -0,0 +1,147 @@ +/** + * @file configuration_attribute.h + * + * @brief Interface of configuration_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CONFIGURATION_ATTRIBUTE_H_ +#define CONFIGURATION_ATTRIBUTE_H_ + +typedef enum configuration_attribute_type_t configuration_attribute_type_t; +typedef struct configuration_attribute_t configuration_attribute_t; + +#include +#include + + +/** + * Configuration attribute header length in bytes. + * + * @ingroup payloads + */ +#define CONFIGURATION_ATTRIBUTE_HEADER_LENGTH 4 + +/** + * Type of the attribute, as in IKEv2 RFC 3.15.1. + * + * @ingroup payloads + */ +enum configuration_attribute_type_t { + INTERNAL_IP4_ADDRESS = 1, + INTERNAL_IP4_NETMASK = 2, + INTERNAL_IP4_DNS = 3, + INTERNAL_IP4_NBNS = 4, + INTERNAL_ADDRESS_EXPIRY = 5, + INTERNAL_IP4_DHCP = 6, + APPLICATION_VERSION = 7, + INTERNAL_IP6_ADDRESS = 8, + INTERNAL_IP6_DNS = 10, + INTERNAL_IP6_NBNS = 11, + INTERNAL_IP6_DHCP = 12, + INTERNAL_IP4_SUBNET = 13, + SUPPORTED_ATTRIBUTES = 14, + INTERNAL_IP6_SUBNET = 15 +}; + +/** + * enum names for configuration_attribute_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *configuration_attribute_type_names; + +/** + * @brief Class representing an IKEv2-CONFIGURATION Attribute. + * + * The CONFIGURATION ATTRIBUTE format is described in RFC section 3.15.1. + * + * @b Constructors: + * - configuration_attribute_create() + * + * @ingroup payloads + */ +struct configuration_attribute_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Returns the currently set value of the attribute. + * + * @warning Returned data are not copied. + * + * @param this calling configuration_attribute_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_value) (configuration_attribute_t *this); + + /** + * @brief Sets the value of the attribute. + * + * @warning Value is getting copied. + * + * @param this calling configuration_attribute_t object + * @param value chunk_t pointing to the value to set + */ + void (*set_value) (configuration_attribute_t *this, chunk_t value); + + /** + * @brief Sets the type of the attribute. + * + * @param this calling configuration_attribute_t object + * @param type type to set (most significant bit is set to zero) + */ + void (*set_type) (configuration_attribute_t *this, u_int16_t type); + + /** + * @brief get the type of the attribute. + * + * @param this calling configuration_attribute_t object + * @return type of the value + */ + u_int16_t (*get_type) (configuration_attribute_t *this); + + /** + * @brief get the length of an attribute. + * + * @param this calling configuration_attribute_t object + * @return type of the value + */ + u_int16_t (*get_length) (configuration_attribute_t *this); + + /** + * @brief Destroys an configuration_attribute_t object. + * + * @param this configuration_attribute_t object to destroy + */ + void (*destroy) (configuration_attribute_t *this); +}; + +/** + * @brief Creates an empty configuration_attribute_t object. + * + * @return created configuration_attribute_t object + * + * @ingroup payloads + */ +configuration_attribute_t *configuration_attribute_create(void); + +#endif /* CONFIGURATION_ATTRIBUTE_H_*/ diff --git a/src/charon/encoding/payloads/cp_payload.c b/src/charon/encoding/payloads/cp_payload.c new file mode 100644 index 000000000..380ed9681 --- /dev/null +++ b/src/charon/encoding/payloads/cp_payload.c @@ -0,0 +1,277 @@ +/** + * @file cp_payload.c + * + * @brief Implementation of cp_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "cp_payload.h" + +#include +#include + +ENUM(config_type_names, CFG_REQUEST, CFG_ACK, + "CFG_REQUEST", + "CFG_REPLY", + "CFG_SET", + "CFG_ACK", +); + +typedef struct private_cp_payload_t private_cp_payload_t; + +/** + * Private data of an cp_payload_t object. + * + */ +struct private_cp_payload_t { + /** + * Public cp_payload_t interface. + */ + cp_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Configuration Attributes in this payload are stored in a linked_list_t. + */ + linked_list_t * attributes; + + /** + * Config Type. + */ + u_int8_t config_type; +}; + +/** + * Encoding rules to parse or generate a IKEv2-CP Payload + * + * The defined offsets are the positions in a object of type + * private_cp_payload_t. + * + */ +encoding_rule_t cp_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_cp_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_cp_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole CP payload*/ + { PAYLOAD_LENGTH, offsetof(private_cp_payload_t, payload_length) }, + /* Proposals are stored in a proposal substructure, + offset points to a linked_list_t pointer */ + { U_INT_8, offsetof(private_cp_payload_t, config_type) }, + { RESERVED_BYTE,0 }, + { RESERVED_BYTE,0 }, + { RESERVED_BYTE,0 }, + { CONFIGURATION_ATTRIBUTES, offsetof(private_cp_payload_t, attributes) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! CFG Type ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Configuration Attributes ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_cp_payload_t *this) +{ + status_t status = SUCCESS; + iterator_t *iterator; + configuration_attribute_t *attribute; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while(iterator->iterate(iterator, (void**)&attribute)) + { + status = attribute->payload_interface.verify(&attribute->payload_interface); + if (status != SUCCESS) + { + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_cp_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = cp_payload_encodings; + *rule_count = sizeof(cp_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_cp_payload_t *this) +{ + return CONFIGURATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_cp_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_cp_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length(private_cp_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_attribute; + size_t length = CP_PAYLOAD_HEADER_LENGTH; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + length += current_attribute->get_length(current_attribute); + } + iterator->destroy(iterator); + + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_cp_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of cp_payload_t.create_configuration_attribute_iterator. + */ +static iterator_t *create_attribute_iterator (private_cp_payload_t *this) +{ + return this->attributes->create_iterator(this->attributes, TRUE); +} + +/** + * Implementation of cp_payload_t.add_proposal_substructure. + */ +static void add_configuration_attribute (private_cp_payload_t *this,configuration_attribute_t *attribute) +{ + this->attributes->insert_last(this->attributes,(void *) attribute); + compute_length(this); +} + +/** + * Implementation of cp_payload_t.set_config_type. + */ +static void set_config_type (private_cp_payload_t *this,config_type_t config_type) +{ + this->config_type = config_type; +} + +/** + * Implementation of cp_payload_t.get_config_type. + */ +static config_type_t get_config_type (private_cp_payload_t *this) +{ + return this->config_type; +} + +/** + * Implementation of payload_t.destroy and cp_payload_t.destroy. + */ +static void destroy(private_cp_payload_t *this) +{ + this->attributes->destroy_offset(this->attributes, + offsetof(configuration_attribute_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +cp_payload_t *cp_payload_create() +{ + private_cp_payload_t *this = malloc_thing(private_cp_payload_t); + + /* public interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_attribute_iterator = (iterator_t* (*) (cp_payload_t *)) create_attribute_iterator; + this->public.add_configuration_attribute = (void (*) (cp_payload_t *,configuration_attribute_t *)) add_configuration_attribute; + this->public.set_config_type = (void (*) (cp_payload_t *, config_type_t)) set_config_type; + this->public.get_config_type = (config_type_t (*) (cp_payload_t *)) get_config_type; + this->public.destroy = (void (*) (cp_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = CP_PAYLOAD_HEADER_LENGTH; + + this->attributes = linked_list_create(); + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/cp_payload.h b/src/charon/encoding/payloads/cp_payload.h new file mode 100644 index 000000000..27ff41005 --- /dev/null +++ b/src/charon/encoding/payloads/cp_payload.h @@ -0,0 +1,132 @@ +/** + * @file cp_payload.h + * + * @brief Interface of cp_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CP_PAYLOAD_H_ +#define CP_PAYLOAD_H_ + +typedef enum config_type_t config_type_t; +typedef struct cp_payload_t cp_payload_t; + +#include +#include +#include +#include + +/** + * CP_PAYLOAD length in bytes without any proposal substructure. + * + * @ingroup payloads + */ +#define CP_PAYLOAD_HEADER_LENGTH 8 + +/** + * Config Type of an Configuration Payload. + * + * @ingroup payloads + */ +enum config_type_t { + CFG_REQUEST = 1, + CFG_REPLY = 2, + CFG_SET = 3, + CFG_ACK = 4, +}; + +/** + * enum name for config_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *config_type_names; + +/** + * @brief Class representing an IKEv2-CP Payload. + * + * The CP Payload format is described in RFC section 3.15. + * + * @b Constructors: + * - cp_payload_create() + * + * @ingroup payloads + */ +struct cp_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored configuration_attribute_t objects. + * + * When deleting an attribute using this iterator, the length of this + * configuration_attribute_t has to be refreshed by calling get_length()! + * + * @param this calling cp_payload_t object + * @return created iterator_t object + */ + iterator_t *(*create_attribute_iterator) (cp_payload_t *this); + + /** + * @brief Adds a configuration_attribute_t object to this object. + * + * The added configuration_attribute_t object is getting destroyed in + * destroy function of cp_payload_t. + * + * @param this calling cp_payload_t object + * @param attribute configuration_attribute_t object to add + */ + void (*add_configuration_attribute) (cp_payload_t *this, configuration_attribute_t *attribute); + + /** + * @brief Set the config type. + * + * @param this calling cp_payload_t object + * @param config_type config_type_t to set + */ + void (*set_config_type) (cp_payload_t *this,config_type_t config_type); + + /** + * @brief Get the config type. + * + * @param this calling cp_payload_t object + * @return config_type_t + */ + config_type_t (*get_config_type) (cp_payload_t *this); + + /** + * @brief Destroys an cp_payload_t object. + * + * @param this cp_payload_t object to destroy + */ + void (*destroy) (cp_payload_t *this); +}; + +/** + * @brief Creates an empty cp_payload_t object + * + * @return cp_payload_t object + * + * @ingroup payloads + */ +cp_payload_t *cp_payload_create(void); + +#endif /*CP_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/delete_payload.c b/src/charon/encoding/payloads/delete_payload.c new file mode 100644 index 000000000..1d42a3af2 --- /dev/null +++ b/src/charon/encoding/payloads/delete_payload.c @@ -0,0 +1,299 @@ +/** + * @file delete_payload.c + * + * @brief Implementation of delete_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "delete_payload.h" + + +typedef struct private_delete_payload_t private_delete_payload_t; + +/** + * Private data of an delete_payload_t object. + * + */ +struct private_delete_payload_t { + /** + * Public delete_payload_t interface. + */ + delete_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Protocol ID. + */ + u_int8_t protocol_id; + + /** + * SPI Size. + */ + u_int8_t spi_size; + + /** + * Number of SPI's. + */ + u_int16_t spi_count; + + /** + * The contained SPI's. + */ + chunk_t spis; + + /** + * List containing u_int32_t spis + */ + linked_list_t *spi_list; +}; + +/** + * Encoding rules to parse or generate a DELETE payload + * + * The defined offsets are the positions in a object of type + * private_delete_payload_t. + * + */ +encoding_rule_t delete_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_delete_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_delete_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_delete_payload_t, payload_length)}, + { U_INT_8, offsetof(private_delete_payload_t, protocol_id) }, + { U_INT_8, offsetof(private_delete_payload_t, spi_size) }, + { U_INT_16, offsetof(private_delete_payload_t, spi_count) }, + /* some delete data bytes, length is defined in PAYLOAD_LENGTH */ + { SPIS, offsetof(private_delete_payload_t, spis) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Protocol ID ! SPI Size ! # of SPIs ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Security Parameter Index(es) (SPI) ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_delete_payload_t *this) +{ + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + if (this->spi_size != 4) + { + return FAILED; + } + break; + case PROTO_IKE: + case 0: + /* IKE deletion has no spi assigned! */ + if (this->spi_size != 0) + { + return FAILED; + } + break; + default: + return FAILED; + } + if (this->spis.len != (this->spi_count * this->spi_size)) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of delete_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_delete_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = delete_payload_encodings; + *rule_count = sizeof(delete_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_delete_payload_t *this) +{ + return DELETE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_delete_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_delete_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_delete_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of delete_payload_t.get_protocol_id. + */ +static protocol_id_t get_protocol_id (private_delete_payload_t *this) +{ + return (this->protocol_id); +} + +/** + * Implementation of delete_payload_t.add_spi. + */ +static void add_spi(private_delete_payload_t *this, u_int32_t spi) +{ + /* only add SPIs if AH|ESP, ignore others */ + if (this->protocol_id == PROTO_AH || this->protocol_id == PROTO_ESP) + { + this->spi_count += 1; + this->spis.len += this->spi_size; + this->spis.ptr = realloc(this->spis.ptr, this->spis.len); + *(u_int32_t*)(this->spis.ptr + (this->spis.len / this->spi_size - 1)) = spi; + if (this->spi_list) + { + /* reset SPI iterator list */ + this->spi_list->destroy(this->spi_list); + this->spi_list = NULL; + } + } +} + +/** + * Implementation of delete_payload_t.create_spi_iterator. + */ +static iterator_t* create_spi_iterator(private_delete_payload_t *this) +{ + int i; + + if (this->spi_list == NULL) + { + this->spi_list = linked_list_create(); + /* only parse SPIs if AH|ESP */ + if (this->protocol_id == PROTO_AH || this->protocol_id == PROTO_ESP) + { + for (i = 0; i < this->spi_count; i++) + { + this->spi_list->insert_last(this->spi_list, this->spis.ptr + i * + this->spi_size); + } + } + } + return this->spi_list->create_iterator(this->spi_list, TRUE); +} + +/** + * Implementation of payload_t.destroy and delete_payload_t.destroy. + */ +static void destroy(private_delete_payload_t *this) +{ + if (this->spis.ptr != NULL) + { + chunk_free(&this->spis); + } + if (this->spi_list) + { + this->spi_list->destroy(this->spi_list); + } + free(this); +} + +/* + * Described in header + */ +delete_payload_t *delete_payload_create(protocol_id_t protocol_id) +{ + private_delete_payload_t *this = malloc_thing(private_delete_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (delete_payload_t *)) destroy; + this->public.get_protocol_id = (protocol_id_t (*) (delete_payload_t *)) get_protocol_id; + this->public.add_spi = (void (*) (delete_payload_t *,u_int32_t))add_spi; + this->public.create_spi_iterator = (iterator_t* (*) (delete_payload_t *)) create_spi_iterator; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = DELETE_PAYLOAD_HEADER_LENGTH; + this->protocol_id = protocol_id; + this->spi_size = protocol_id == PROTO_AH || protocol_id == PROTO_ESP ? 4 : 0; + this->spi_count = 0; + this->spis = chunk_empty; + this->spi_list = NULL; + + return (&this->public); +} diff --git a/src/charon/encoding/payloads/delete_payload.h b/src/charon/encoding/payloads/delete_payload.h new file mode 100644 index 000000000..508f7fba2 --- /dev/null +++ b/src/charon/encoding/payloads/delete_payload.h @@ -0,0 +1,102 @@ +/** + * @file delete_payload.h + * + * @brief Interface of delete_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DELETE_PAYLOAD_H_ +#define DELETE_PAYLOAD_H_ + +typedef struct delete_payload_t delete_payload_t; + +#include +#include +#include + +/** + * Length of a delete payload without the SPI in bytes. + * + * @ingroup payloads + */ +#define DELETE_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2 DELETE payload. + * + * The DELETE payload format is described in RFC section 3.11. + * + * @b Constructors: + * - delete_payload_create() + * + * @todo Implement better setter/getters + * + * @ingroup payloads + */ +struct delete_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the protocol ID. + * + * @param this calling delete_payload_t object + * @return protocol ID + */ + protocol_id_t (*get_protocol_id) (delete_payload_t *this); + + /** + * @brief Add an SPI to the list of deleted SAs. + * + * @param this calling delete_payload_t object + * @param spi spi to add + */ + void (*add_spi) (delete_payload_t *this, u_int32_t spi); + + /** + * @brief Get an iterator over the SPIs. + * + * The iterate() function returns a pointer to a u_int32_t SPI. + * + * @param this calling delete_payload_t object + * @return iterator over SPIs + */ + iterator_t *(*create_spi_iterator) (delete_payload_t *this); + + /** + * @brief Destroys an delete_payload_t object. + * + * @param this delete_payload_t object to destroy + */ + void (*destroy) (delete_payload_t *this); +}; + +/** + * @brief Creates an empty delete_payload_t object. + * + * @param protocol_id protocol, such as AH|ESP + * @return delete_payload_t object + * + * @ingroup payloads + */ +delete_payload_t *delete_payload_create(protocol_id_t protocol_id); + +#endif /* DELETE_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/eap_payload.c b/src/charon/encoding/payloads/eap_payload.c new file mode 100644 index 000000000..79ab32fe5 --- /dev/null +++ b/src/charon/encoding/payloads/eap_payload.c @@ -0,0 +1,331 @@ +/** + * @file eap_payload.c + * + * @brief Implementation of eap_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "eap_payload.h" + +#include + +typedef struct private_eap_payload_t private_eap_payload_t; + +/** + * Private data of an eap_payload_t object. + * + */ +struct private_eap_payload_t { + /** + * Public eap_payload_t interface. + */ + eap_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * EAP message data, if available + */ + chunk_t data; +}; + +/** + * Encoding rules to parse or generate a EAP payload. + * + * The defined offsets are the positions in a object of type + * private_eap_payload_t. + * + */ +encoding_rule_t eap_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_eap_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_eap_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_eap_payload_t, payload_length) }, + /* chunt to data, starting at "code" */ + { EAP_DATA, offsetof(private_eap_payload_t, data) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Code ! Identifier ! Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Type ! Type_Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_eap_payload_t *this) +{ + u_int16_t length; + u_int8_t code; + + if (this->data.len < 4) + { + DBG1(DBG_ENC, "EAP payloads EAP message too short (%d)", this->data.len); + return FAILED; + } + code = *this->data.ptr; + length = htons(*(u_int16_t*)(this->data.ptr + 2)); + if (this->data.len != length) + { + DBG1(DBG_ENC, "EAP payload length (%d) does not match contained message length (%d)", + this->data.len, length); + return FAILED; + } + switch (code) + { + case EAP_REQUEST: + case EAP_RESPONSE: + { + if (this->data.len < 4) + { + DBG1(DBG_ENC, "EAP Request/Response does not have any data"); + return FAILED; + } + break; + } + case EAP_SUCCESS: + case EAP_FAILURE: + { + if (this->data.len != 4) + { + DBG1(DBG_ENC, "EAP Success/Failure has data"); + return FAILED; + } + break; + } + default: + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of eap_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_eap_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = eap_payload_encodings; + *rule_count = sizeof(eap_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_eap_payload_t *this) +{ + return EXTENSIBLE_AUTHENTICATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_eap_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_eap_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_eap_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of eap_payload_t.get_data. + */ +static chunk_t get_data(private_eap_payload_t *this) +{ + return this->data; +} + +/** + * Implementation of eap_payload_t.set_data. + */ +static void set_data(private_eap_payload_t *this, chunk_t data) +{ + chunk_free(&this->data); + this->data = chunk_clone(data); + this->payload_length = this->data.len + 4; +} + +/** + * Implementation of eap_payload_t.get_code. + */ +static eap_code_t get_code(private_eap_payload_t *this) +{ + if (this->data.len > 0) + { + return *this->data.ptr; + } + /* should not happen, as it is verified */ + return 0; +} + +/** + * Implementation of eap_payload_t.get_identifier. + */ +static u_int8_t get_identifier(private_eap_payload_t *this) +{ + if (this->data.len > 1) + { + return *(this->data.ptr + 1); + } + /* should not happen, as it is verified */ + return 0; +} + +/** + * Implementation of eap_payload_t.get_type. + */ +static eap_type_t get_type(private_eap_payload_t *this) +{ + if (this->data.len > 4) + { + return *(this->data.ptr + 4); + } + return 0; +} + +/** + * Implementation of payload_t.destroy and eap_payload_t.destroy. + */ +static void destroy(private_eap_payload_t *this) +{ + chunk_free(&this->data); + free(this); +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create() +{ + private_eap_payload_t *this = malloc_thing(private_eap_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (eap_payload_t *)) destroy; + this->public.get_data = (chunk_t (*) (eap_payload_t*))get_data; + this->public.set_data = (void (*) (eap_payload_t *,chunk_t))set_data; + this->public.get_code = (eap_code_t (*) (eap_payload_t*))get_code; + this->public.get_identifier = (u_int8_t (*) (eap_payload_t*))get_identifier; + this->public.get_type = (eap_type_t (*) (eap_payload_t*))get_type; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = EAP_PAYLOAD_HEADER_LENGTH; + this->data = chunk_empty; + + return &(this->public); +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create_data(chunk_t data) +{ + eap_payload_t *this = eap_payload_create(); + + this->set_data(this, data); + return this; +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create_code(eap_code_t code) +{ + eap_payload_t *this = eap_payload_create(); + chunk_t data = chunk_alloca(4); + + *(data.ptr + 0) = code; + *(data.ptr + 1) = 0; + *(u_int16_t*)(data.ptr + 2) = htons(data.len); + + this->set_data(this, data); + return this; +} + +/* + * Described in header + */ +eap_payload_t *eap_payload_create_nak() +{ + eap_payload_t *this = eap_payload_create(); + chunk_t data = chunk_alloca(5); + + *(data.ptr + 0) = EAP_RESPONSE; + *(data.ptr + 1) = 0; + *(u_int16_t*)(data.ptr + 2) = htons(data.len); + *(data.ptr + 4) = EAP_NAK; + + this->set_data(this, data); + return this; +} diff --git a/src/charon/encoding/payloads/eap_payload.h b/src/charon/encoding/payloads/eap_payload.h new file mode 100644 index 000000000..13c0ade80 --- /dev/null +++ b/src/charon/encoding/payloads/eap_payload.h @@ -0,0 +1,149 @@ +/** + * @file eap_payload.h + * + * @brief Interface of eap_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef EAP_PAYLOAD_H_ +#define EAP_PAYLOAD_H_ + +typedef struct eap_payload_t eap_payload_t; + +#include +#include +#include + +/** + * Length of a EAP payload without the EAP Message in bytes. + * + * @ingroup payloads + */ +#define EAP_PAYLOAD_HEADER_LENGTH 4 + +/** + * @brief Class representing an IKEv2 EAP payload. + * + * The EAP payload format is described in RFC section 3.16. + * + * @b Constructors: + * - eap_payload_create() + * + * @ingroup payloads + */ +struct eap_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the contained EAP data. + * + * This contains the FULL EAP message starting with "code". + * Chunk gets cloned. + * + * @param this calling eap_payload_t object + * @param message EAP data + */ + void (*set_data) (eap_payload_t *this, chunk_t data); + + /** + * @brief Get the contained EAP data. + * + * This contains the FULL EAP message starting with "code". + * + * @param this calling eap_payload_t object + * @return EAP data (pointer to internal data) + */ + chunk_t (*get_data) (eap_payload_t *this); + + /** + * @brief Get the EAP code. + * + * @param this calling eap_payload_t object + * @return EAP message as chunk_t + */ + eap_code_t (*get_code) (eap_payload_t *this); + + /** + * @brief Get the EAP identifier. + * + * @param this calling eap_payload_t object + * @return unique identifier + */ + u_int8_t (*get_identifier) (eap_payload_t *this); + + /** + * @brief Get the EAP method type. + * + * @param this calling eap_payload_t object + * @return EAP method type + */ + eap_type_t (*get_type) (eap_payload_t *this); + + /** + * @brief Destroys an eap_payload_t object. + * + * @param this eap_payload_t object to destroy + */ + void (*destroy) (eap_payload_t *this); +}; + +/** + * @brief Creates an empty eap_payload_t object. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create(void); + +/** + * @brief Creates an eap_payload_t object with data. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create_data(chunk_t data); + +/** + * @brief Creates an eap_payload_t object with a code. + * + * Could should be either EAP_SUCCESS/EAP_FAILURE, use + * constructor above otherwise. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create_code(eap_code_t code); + +/** + * @brief Creates an eap_payload_t EAP_RESPONSE containing an EAP_NAK. + * + * @return eap_payload_t object + * + * @ingroup payloads + */ +eap_payload_t *eap_payload_create_nak(); + +#endif /* EAP_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/encodings.c b/src/charon/encoding/payloads/encodings.c new file mode 100644 index 000000000..55a7cf132 --- /dev/null +++ b/src/charon/encoding/payloads/encodings.c @@ -0,0 +1,66 @@ +/** + * @file encodings.c + * + * @brief String mappings of encoding_type_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "encodings.h" + +ENUM(encoding_type_names, U_INT_4, ENCRYPTED_DATA, + "U_INT_4", + "U_INT_8", + "U_INT_16", + "U_INT_32", + "U_INT_64", + "RESERVED_BIT", + "RESERVED_BYTE", + "FLAG", + "PAYLOAD_LENGTH", + "HEADER_LENGTH", + "SPI_SIZE", + "SPI", + "KEY_EXCHANGE_DATA", + "NOTIFICATION_DATA", + "PROPOSALS", + "TRANSFORMS", + "TRANSFORM_ATTRIBUTES", + "CONFIGURATION_ATTRIBUTES", + "CONFIGURATION_ATTRIBUTE_VALUE", + "ATTRIBUTE_FORMAT", + "ATTRIBUTE_TYPE", + "ATTRIBUTE_LENGTH_OR_VALUE", + "CONFIGURATION_ATTRIBUTE_LENGTH", + "ATTRIBUTE_VALUE", + "TRAFFIC_SELECTORS", + "TS_TYPE", + "ADDRESS", + "NONCE_DATA", + "ID_DATA", + "AUTH_DATA", + "CERT_DATA", + "CERTREQ_DATA", + "EAP_DATA", + "SPIS", + "VID_DATA", + "UNKNOWN_DATA", + "IKE_SPI", + "ENCRYPTED_DATA", +); diff --git a/src/charon/encoding/payloads/encodings.h b/src/charon/encoding/payloads/encodings.h new file mode 100644 index 000000000..5e07fbfab --- /dev/null +++ b/src/charon/encoding/payloads/encodings.h @@ -0,0 +1,537 @@ +/** + * @file encodings.h + * + * @brief Definition of encoding_type_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef ENCODINGS_H_ +#define ENCODINGS_H_ + +typedef enum encoding_type_t encoding_type_t; +typedef struct encoding_rule_t encoding_rule_t; + +#include + +/** + * @brief All different kinds of encoding types. + * + * Each field of an IKEv2-Message (in header or payload) + * which has to be parsed or generated differently has its own + * type defined here. + * + * Header is parsed like a payload and gets its one payload_id + * from PRIVATE USE space. Also the substructures + * of specific payload types get their own payload_id + * from PRIVATE_USE space. See IKEv2-Draft for more informations. + * + * @ingroup payloads + */ +enum encoding_type_t { + + /** + * Representing a 4 Bit unsigned int value. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 4 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 4 bit forward afterwards. + */ + U_INT_4, + + /** + * Representing a 8 Bit unsigned int value. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 8 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 8 bit forward afterwards. + */ + U_INT_8, + + /** + * Representing a 16 Bit unsigned int value. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + U_INT_16, + + /** + * Representing a 32 Bit unsigned int value. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 32 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 32 bit forward afterwards. + */ + U_INT_32, + + /** + * Representing a 64 Bit unsigned int value. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 64 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 64 bit forward afterwards. + */ + U_INT_64, + + /** + * @brief represents a RESERVED_BIT used in FLAG-Bytes. + * + * When generating, the next bit is set to zero and the current write + * position is moved one bit forward. + * No value is read from the associated data struct. + * The current write position is moved 1 bit forward afterwards. + * + * When parsing, the current read pointer is moved one bit forward. + * No value is written to the associated data struct. + * The current read pointer is moved 1 bit forward afterwards. + */ + RESERVED_BIT, + + /** + * @brief represents a RESERVED_BYTE. + * + * When generating, the next byte is set to zero and the current write + * position is moved one byte forward. + * No value is read from the associated data struct. + * The current write position is moved 1 byte forward afterwards. + * + * When parsing, the current read pointer is moved one byte forward. + * No value is written to the associated data struct. + * The current read pointer is moved 1 byte forward afterwards. + */ + RESERVED_BYTE, + + /** + * Representing a 1 Bit flag. + * + * When generation, the next bit is set to 1 if the associated value + * in the data struct is TRUE, 0 otherwise. The current write position + * is moved 1 bit forward afterwards. + * + * When parsing, the next bit is read and stored in the associated data + * struct. 0 means FALSE, 1 means TRUE, The current read pointer + * is moved 1 bit forward afterwards + */ + FLAG, + + /** + * Representating a length field of a payload. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + PAYLOAD_LENGTH, + + /** + * Representating a length field of a header. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 32 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 32 bit forward afterwards. + */ + HEADER_LENGTH, + + /** + * Representating a spi size field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 8 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 8 bit forward afterwards. + */ + SPI_SIZE, + + /** + * Representating a spi field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing SPI_SIZE bytes are read and written into the chunk pointing to. + */ + SPI, + + /** + * Representating a Key Exchange Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + KEY_EXCHANGE_DATA, + + /** + * Representating a Notification field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - spi size - 8) bytes are read and written into the chunk pointing to. + */ + NOTIFICATION_DATA, + + /** + * Representating one or more proposal substructures. + * + * The offset points to a linked_list_t pointer. + * + * When generating the proposal_substructure_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed proposal_substructure_t objects have + * to be stored in the pointed linked_list. + */ + PROPOSALS, + + /** + * Representating one or more transform substructures. + * + * The offset points to a linked_list_t pointer. + * + * When generating the transform_substructure_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed transform_substructure_t objects have + * to be stored in the pointed linked_list. + */ + TRANSFORMS, + + /** + * Representating one or more Attributes of a transform substructure. + * + * The offset points to a linked_list_t pointer. + * + * When generating the transform_attribute_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed transform_attribute_t objects have + * to be stored in the pointed linked_list. + */ + TRANSFORM_ATTRIBUTES, + + /** + * Representating one or more Attributes of a configuration payload. + * + * The offset points to a linked_list_t pointer. + * + * When generating the configuration_attribute_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed configuration_attribute_t objects have + * to be stored in the pointed linked_list. + */ + CONFIGURATION_ATTRIBUTES, + + /** + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + CONFIGURATION_ATTRIBUTE_VALUE, + + /** + * Representing a 1 Bit flag specifying the format of a transform attribute. + * + * When generation, the next bit is set to 1 if the associated value + * in the data struct is TRUE, 0 otherwise. The current write position + * is moved 1 bit forward afterwards. + * + * When parsing, the next bit is read and stored in the associated data + * struct. 0 means FALSE, 1 means TRUE, The current read pointer + * is moved 1 bit forward afterwards. + */ + ATTRIBUTE_FORMAT, + /** + * Representing a 15 Bit unsigned int value used as attribute type + * in an attribute transform. + * + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 15 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 15 bit forward afterwards. + */ + ATTRIBUTE_TYPE, + + /** + * Depending on the field of type ATTRIBUTE_FORMAT + * this field contains the length or the value of an transform attribute. + * Its stored in a 16 unsigned integer field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + ATTRIBUTE_LENGTH_OR_VALUE, + + /** + * This field contains the length or the value of an configuration attribute. + * Its stored in a 16 unsigned integer field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + CONFIGURATION_ATTRIBUTE_LENGTH, + + /** + * Depending on the field of type ATTRIBUTE_FORMAT + * this field is available or missing and so parsed/generated + * or not parsed/not generated. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing SPI_SIZE bytes are read and written into the chunk pointing to. + */ + ATTRIBUTE_VALUE, + + /** + * Representating one or more Traffic selectors of a TS payload. + * + * The offset points to a linked_list_t pointer. + * + * When generating the traffic_selector_substructure_t objects are stored + * in the pointed linked_list. + * + * When parsing the parsed traffic_selector_substructure_t objects have + * to be stored in the pointed linked_list. + */ + TRAFFIC_SELECTORS, + + /** + * Representating a Traffic selector type field. + * + * When generating it must be changed from host to network order. + * The value is read from the associated data struct. + * The current write position is moved 16 bit forward afterwards. + * + * When parsing it must be changed from network to host order. + * The value is written to the associated data struct. + * The current read pointer is moved 16 bit forward afterwards. + */ + TS_TYPE, + + /** + * Representating an address field in a traffic selector. + * + * Depending on the last field of type TS_TYPE + * this field is either 4 or 16 byte long. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing 4 or 16 bytes are read and written into the chunk pointing to. + */ + ADDRESS, + + /** + * Representating a Nonce Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + NONCE_DATA, + + /** + * Representating a ID Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + ID_DATA, + + /** + * Representating a AUTH Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + AUTH_DATA, + + /** + * Representating a CERT Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 5) bytes are read and written into the chunk pointing to. + */ + CERT_DATA, + + /** + * Representating a CERTREQ Data field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 5) bytes are read and written into the chunk pointing to. + */ + CERTREQ_DATA, + + /** + * Representating an EAP message field. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + EAP_DATA, + + /** + * Representating the SPIS field in a DELETE payload. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 8) bytes are read and written into the chunk pointing to. + */ + SPIS, + + /** + * Representating the VID DATA field in a VENDOR ID payload. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + VID_DATA, + + /** + * Representating the DATA of an unknown payload. + * + * When generating the content of the chunkt pointing to + * is written. + * + * When parsing (Payload Length - 4) bytes are read and written into the chunk pointing to. + */ + UNKNOWN_DATA, + + /** + * Representating an IKE_SPI field in an IKEv2 Header. + * + * When generating the value of the u_int64_t pointing to + * is written (host and networ order is not changed). + * + * When parsing 8 bytes are read and written into the u_int64_t pointing to. + */ + IKE_SPI, + + /** + * Representing the encrypted data body of a encryption payload. + */ + ENCRYPTED_DATA, +}; + +/** + * enum name for encoding_type_t + * + * @ingroup payloads + */ +extern enum_name_t *encoding_type_names; + +/** + * An encoding rule is a mapping of a specific encoding type to + * a location in the data struct where the current field is stored to + * or read from. + * + * For examples see files in this directory. + * + * This rules are used by parser and generator. + * + * @ingroup payloads + */ +struct encoding_rule_t { + + /** + * Encoding type. + */ + encoding_type_t type; + + /** + * Offset in the data struct. + * + * When parsing, data are written to this offset of the + * data struct. + * + * When generating, data are read from this offset in the + * data struct. + */ + u_int32_t offset; +}; + +#endif /*ENCODINGS_H_*/ diff --git a/src/charon/encoding/payloads/encryption_payload.c b/src/charon/encoding/payloads/encryption_payload.c new file mode 100644 index 000000000..23b6e8d9f --- /dev/null +++ b/src/charon/encoding/payloads/encryption_payload.c @@ -0,0 +1,646 @@ +/** + * @file encryption_payload.c + * + * @brief Implementation of encryption_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "encryption_payload.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_encryption_payload_t private_encryption_payload_t; + +/** + * Private data of an encryption_payload_t' Object. + * + */ +struct private_encryption_payload_t { + + /** + * Public encryption_payload_t interface. + */ + encryption_payload_t public; + + /** + * There is no next payload for an encryption payload, + * since encryption payload MUST be the last one. + * next_payload means here the first payload of the + * contained, encrypted payload. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload + */ + u_int16_t payload_length; + + /** + * Chunk containing the iv, data, padding, + * and (an eventually not calculated) signature. + */ + chunk_t encrypted; + + /** + * Chunk containing the data in decrypted (unpadded) form. + */ + chunk_t decrypted; + + /** + * Signer set by set_signer. + */ + signer_t *signer; + + /** + * Crypter, supplied by encrypt/decrypt + */ + crypter_t *crypter; + + /** + * Contained payloads of this encrpytion_payload. + */ + linked_list_t *payloads; +}; + +/** + * Encoding rules to parse or generate a IKEv2-Encryption Payload. + * + * The defined offsets are the positions in a object of type + * private_encryption_payload_t. + * + */ +encoding_rule_t encryption_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_encryption_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_encryption_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole encryption payload*/ + { PAYLOAD_LENGTH, offsetof(private_encryption_payload_t, payload_length) }, + /* encrypted data, stored in a chunk. contains iv, data, padding */ + { ENCRYPTED_DATA, offsetof(private_encryption_payload_t, encrypted) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Initialization Vector ! + ! (length is block size for encryption algorithm) ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Encrypted IKE Payloads ! + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! Padding (0-255 octets) ! + +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + ! ! Pad Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ Integrity Checksum Data ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_encryption_payload_t *this) +{ + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_encryption_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = encryption_payload_encodings; + *rule_count = sizeof(encryption_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_encryption_payload_t *this) +{ + return ENCRYPTED; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_encryption_payload_t *this) +{ + /* returns first contained payload here */ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_encryption_payload_t *this, payload_type_t type) +{ + /* set next type is not allowed, since this payload MUST be the last one + * and so nothing is done in here*/ +} + +/** + * (re-)compute the lenght of the whole payload + */ +static void compute_length(private_encryption_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_payload; + size_t block_size, length = 0; + iterator = this->payloads->create_iterator(this->payloads, TRUE); + + /* count payload length */ + while (iterator->iterate(iterator, (void **) ¤t_payload)) + { + length += current_payload->get_length(current_payload); + } + iterator->destroy(iterator); + + if (this->crypter && this->signer) + { + /* append one byte for padding length */ + length++; + /* append padding */ + block_size = this->crypter->get_block_size(this->crypter); + length += block_size - length % block_size; + /* add iv */ + length += block_size; + /* add signature */ + length += this->signer->get_block_size(this->signer); + } + length += ENCRYPTION_PAYLOAD_HEADER_LENGTH; + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_encryption_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of payload_t.create_payload_iterator. + */ +static iterator_t *create_payload_iterator (private_encryption_payload_t *this, bool forward) +{ + return (this->payloads->create_iterator(this->payloads, forward)); +} + +/** + * Implementation of payload_t.add_payload. + */ +static void add_payload(private_encryption_payload_t *this, payload_t *payload) +{ + payload_t *last_payload; + if (this->payloads->get_count(this->payloads) > 0) + { + this->payloads->get_last(this->payloads,(void **) &last_payload); + last_payload->set_next_type(last_payload, payload->get_type(payload)); + } + else + { + this->next_payload = payload->get_type(payload); + } + payload->set_next_type(payload, NO_PAYLOAD); + this->payloads->insert_last(this->payloads, (void*)payload); + compute_length(this); +} + +/** + * Implementation of encryption_payload_t.remove_first_payload. + */ +static status_t remove_first_payload(private_encryption_payload_t *this, payload_t **payload) +{ + return this->payloads->remove_first(this->payloads, (void**)payload); +} + +/** + * Implementation of encryption_payload_t.get_payload_count. + */ +static size_t get_payload_count(private_encryption_payload_t *this) +{ + return this->payloads->get_count(this->payloads); +} + +/** + * Generate payload before encryption. + */ +static void generate(private_encryption_payload_t *this) +{ + payload_t *current_payload, *next_payload; + generator_t *generator; + iterator_t *iterator; + + /* recalculate length before generating */ + compute_length(this); + + /* create iterator */ + iterator = this->payloads->create_iterator(this->payloads, TRUE); + + /* get first payload */ + if (iterator->iterate(iterator, (void**)¤t_payload)) + { + this->next_payload = current_payload->get_type(current_payload); + } + else + { + /* no paylads? */ + DBG2(DBG_ENC, "generating contained payloads, but none available"); + free(this->decrypted.ptr); + this->decrypted = chunk_empty; + iterator->destroy(iterator); + return; + } + + generator = generator_create(); + + /* build all payload, except last */ + while(iterator->iterate(iterator, (void**)&next_payload)) + { + current_payload->set_next_type(current_payload, next_payload->get_type(next_payload)); + generator->generate_payload(generator, current_payload); + current_payload = next_payload; + } + iterator->destroy(iterator); + + /* build last payload */ + current_payload->set_next_type(current_payload, NO_PAYLOAD); + generator->generate_payload(generator, current_payload); + + /* free already generated data */ + free(this->decrypted.ptr); + + generator->write_to_chunk(generator, &(this->decrypted)); + generator->destroy(generator); + DBG2(DBG_ENC, "successfully generated content in encryption payload"); +} + +/** + * Implementation of encryption_payload_t.encrypt. + */ +static status_t encrypt(private_encryption_payload_t *this) +{ + chunk_t iv, padding, to_crypt, result; + randomizer_t *randomizer; + status_t status; + size_t block_size; + + if (this->signer == NULL || this->crypter == NULL) + { + DBG1(DBG_ENC, "could not encrypt, signer/crypter not set"); + return INVALID_STATE; + } + + /* for random data in iv and padding */ + randomizer = randomizer_create(); + + /* build payload chunk */ + generate(this); + + DBG2(DBG_ENC, "encrypting payloads"); + DBG3(DBG_ENC, "data to encrypt %B", &this->decrypted); + + /* build padding */ + block_size = this->crypter->get_block_size(this->crypter); + padding.len = block_size - ((this->decrypted.len + 1) % block_size); + status = randomizer->allocate_pseudo_random_bytes(randomizer, padding.len, &padding); + if (status != SUCCESS) + { + randomizer->destroy(randomizer); + return status; + } + + /* concatenate payload data, padding, padding len */ + to_crypt.len = this->decrypted.len + padding.len + 1; + to_crypt.ptr = malloc(to_crypt.len); + + memcpy(to_crypt.ptr, this->decrypted.ptr, this->decrypted.len); + memcpy(to_crypt.ptr + this->decrypted.len, padding.ptr, padding.len); + *(to_crypt.ptr + to_crypt.len - 1) = padding.len; + + /* build iv */ + iv.len = block_size; + status = randomizer->allocate_pseudo_random_bytes(randomizer, iv.len, &iv); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + chunk_free(&to_crypt); + chunk_free(&padding); + return status; + } + + DBG3(DBG_ENC, "data before encryption with padding %B", &to_crypt); + + /* encrypt to_crypt chunk */ + free(this->encrypted.ptr); + status = this->crypter->encrypt(this->crypter, to_crypt, iv, &result); + free(padding.ptr); + free(to_crypt.ptr); + if (status != SUCCESS) + { + DBG2(DBG_ENC, "encryption failed"); + free(iv.ptr); + return status; + } + DBG3(DBG_ENC, "data after encryption %B", &result); + + /* build encrypted result with iv and signature */ + this->encrypted.len = iv.len + result.len + this->signer->get_block_size(this->signer); + free(this->encrypted.ptr); + this->encrypted.ptr = malloc(this->encrypted.len); + + /* fill in result, signature is left out */ + memcpy(this->encrypted.ptr, iv.ptr, iv.len); + memcpy(this->encrypted.ptr + iv.len, result.ptr, result.len); + + free(result.ptr); + free(iv.ptr); + DBG3(DBG_ENC, "data after encryption with IV and (invalid) signature %B", + &this->encrypted); + + return SUCCESS; +} + +/** + * Parse the payloads after decryption. + */ +static status_t parse(private_encryption_payload_t *this) +{ + parser_t *parser; + status_t status; + payload_type_t current_payload_type; + + /* build a parser on the decrypted data */ + parser = parser_create(this->decrypted); + + current_payload_type = this->next_payload; + /* parse all payloads */ + while (current_payload_type != NO_PAYLOAD) + { + payload_t *current_payload; + + status = parser->parse_payload(parser, current_payload_type, (payload_t**)¤t_payload); + if (status != SUCCESS) + { + parser->destroy(parser); + return PARSE_ERROR; + } + + status = current_payload->verify(current_payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "%N verification failed", + payload_type_names, current_payload->get_type(current_payload)); + current_payload->destroy(current_payload); + parser->destroy(parser); + return VERIFY_ERROR; + } + + /* get next payload type */ + current_payload_type = current_payload->get_next_type(current_payload); + + this->payloads->insert_last(this->payloads,current_payload); + } + parser->destroy(parser); + DBG2(DBG_ENC, "succesfully parsed content of encryption payload"); + return SUCCESS; +} + +/** + * Implementation of encryption_payload_t.encrypt. + */ +static status_t decrypt(private_encryption_payload_t *this) +{ + chunk_t iv, concatenated; + u_int8_t padding_length; + status_t status; + + DBG2(DBG_ENC, "decrypting encryption payload"); + DBG3(DBG_ENC, "data before decryption with IV and (invalid) signature %B", + &this->encrypted); + + if (this->signer == NULL || this->crypter == NULL) + { + DBG1(DBG_ENC, "could not decrypt, no crypter/signer set"); + return INVALID_STATE; + } + + /* get IV */ + iv.len = this->crypter->get_block_size(this->crypter); + + iv.ptr = this->encrypted.ptr; + + /* point concatenated to data + padding + padding_length*/ + concatenated.ptr = this->encrypted.ptr + iv.len; + concatenated.len = this->encrypted.len - iv.len - this->signer->get_block_size(this->signer); + + /* check the size of input: + * concatenated must be at least on block_size of crypter + */ + if (concatenated.len < iv.len) + { + DBG1(DBG_ENC, "could not decrypt, invalid input"); + return FAILED; + } + + /* free previus data, if any */ + free(this->decrypted.ptr); + + DBG3(DBG_ENC, "data before decryption %B", &concatenated); + + status = this->crypter->decrypt(this->crypter, concatenated, iv, &(this->decrypted)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "could not decrypt, decryption failed"); + return FAILED; + } + DBG3(DBG_ENC, "data after decryption with padding %B", &this->decrypted); + + + /* get padding length, sits just bevore signature */ + padding_length = *(this->decrypted.ptr + this->decrypted.len - 1); + /* add one byte to the padding length, since the padding_length field is not included */ + padding_length++; + this->decrypted.len -= padding_length; + + /* check size again */ + if (padding_length > concatenated.len || this->decrypted.len < 0) + { + DBG1(DBG_ENC, "decryption failed, invalid padding length found. Invalid key?"); + /* decryption failed :-/ */ + return FAILED; + } + + /* free padding */ + this->decrypted.ptr = realloc(this->decrypted.ptr, this->decrypted.len); + DBG3(DBG_ENC, "data after decryption without padding %B", &this->decrypted); + DBG2(DBG_ENC, "decryption successful, trying to parse content"); + return parse(this); +} + +/** + * Implementation of encryption_payload_t.set_transforms. + */ +static void set_transforms(private_encryption_payload_t *this, crypter_t* crypter, signer_t* signer) +{ + this->signer = signer; + this->crypter = crypter; +} + +/** + * Implementation of encryption_payload_t.build_signature. + */ +static status_t build_signature(private_encryption_payload_t *this, chunk_t data) +{ + chunk_t data_without_sig = data; + chunk_t sig; + + if (this->signer == NULL) + { + DBG1(DBG_ENC, "unable to build signature, no signer set"); + return INVALID_STATE; + } + + sig.len = this->signer->get_block_size(this->signer); + data_without_sig.len -= sig.len; + sig.ptr = data.ptr + data_without_sig.len; + DBG2(DBG_ENC, "building signature"); + this->signer->get_signature(this->signer, data_without_sig, sig.ptr); + return SUCCESS; +} + +/** + * Implementation of encryption_payload_t.verify_signature. + */ +static status_t verify_signature(private_encryption_payload_t *this, chunk_t data) +{ + chunk_t sig, data_without_sig; + bool valid; + + if (this->signer == NULL) + { + DBG1(DBG_ENC, "unable to verify signature, no signer set"); + return INVALID_STATE; + } + /* find signature in data chunk */ + sig.len = this->signer->get_block_size(this->signer); + if (data.len <= sig.len) + { + DBG1(DBG_ENC, "unable to verify signature, invalid input"); + return FAILED; + } + sig.ptr = data.ptr + data.len - sig.len; + + /* verify it */ + data_without_sig.len = data.len - sig.len; + data_without_sig.ptr = data.ptr; + valid = this->signer->verify_signature(this->signer, data_without_sig, sig); + + if (!valid) + { + DBG1(DBG_ENC, "signature verification failed"); + return FAILED; + } + + DBG2(DBG_ENC, "signature verification successful"); + return SUCCESS; +} + +/** + * Implementation of payload_t.destroy. + */ +static void destroy(private_encryption_payload_t *this) +{ + this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); + free(this->encrypted.ptr); + free(this->decrypted.ptr); + free(this); +} + +/* + * Described in header + */ +encryption_payload_t *encryption_payload_create() +{ + private_encryption_payload_t *this = malloc_thing(private_encryption_payload_t); + + /* payload_t interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_payload_iterator = (iterator_t * (*) (encryption_payload_t *,bool)) create_payload_iterator; + this->public.add_payload = (void (*) (encryption_payload_t *,payload_t *)) add_payload; + this->public.remove_first_payload = (status_t (*)(encryption_payload_t*, payload_t **)) remove_first_payload; + this->public.get_payload_count = (size_t (*)(encryption_payload_t*)) get_payload_count; + + this->public.encrypt = (status_t (*) (encryption_payload_t *)) encrypt; + this->public.decrypt = (status_t (*) (encryption_payload_t *)) decrypt; + this->public.set_transforms = (void (*) (encryption_payload_t*,crypter_t*,signer_t*)) set_transforms; + this->public.build_signature = (status_t (*) (encryption_payload_t*, chunk_t)) build_signature; + this->public.verify_signature = (status_t (*) (encryption_payload_t*, chunk_t)) verify_signature; + this->public.destroy = (void (*) (encryption_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = ENCRYPTION_PAYLOAD_HEADER_LENGTH; + this->encrypted = chunk_empty; + this->decrypted = chunk_empty; + this->signer = NULL; + this->crypter = NULL; + this->payloads = linked_list_create(); + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/encryption_payload.h b/src/charon/encoding/payloads/encryption_payload.h new file mode 100644 index 000000000..7cf53619f --- /dev/null +++ b/src/charon/encoding/payloads/encryption_payload.h @@ -0,0 +1,197 @@ +/** + * @file encryption_payload.h + * + * @brief Interface of encryption_payload_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef ENCRYPTION_PAYLOAD_H_ +#define ENCRYPTION_PAYLOAD_H_ + +typedef struct encryption_payload_t encryption_payload_t; + +#include +#include +#include +#include +#include + +/** + * Encrpytion payload length in bytes without IV and following data. + * + * @ingroup payloads + */ +#define ENCRYPTION_PAYLOAD_HEADER_LENGTH 4 + + +/** + * @brief The encryption payload as described in RFC section 3.14. + * + * Before any crypt/decrypt/sign/verify operation can occur, + * the transforms must be set. After that, a parsed encryption payload + * can be decrypted, which also will parse the contained payloads. + * Encryption is done the same way, added payloads will get generated + * and then encrypted. + * For signature building, there is the FULL packet needed. Meaning it + * must be builded after generation of all payloads and the encryption + * of the encryption payload. + * Signature verificatin is done before decryption. + * + * @b Constructors: + * - encryption_payload_create() + * + * @ingroup payloads + */ +struct encryption_payload_t { + /** + * Implements payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator for all contained payloads. + * + * @warning iterator_t object has to get destroyed by the caller. + * + * @param this calling encryption_payload_t object + * @param[in] forward iterator direction (TRUE: front to end) + * return created iterator_t object + */ + iterator_t *(*create_payload_iterator) (encryption_payload_t *this, bool forward); + + /** + * @brief Adds a payload to this encryption payload. + * + * @param this calling encryption_payload_t object + * @param payload payload_t object to add + */ + void (*add_payload) (encryption_payload_t *this, payload_t *payload); + + /** + * @brief Reove the last payload in the contained payload list. + * + * @param this calling encryption_payload_t object + * @param[out] payload removed payload + * @return + * - SUCCESS, or + * - NOT_FOUND if list empty + */ + status_t (*remove_first_payload) (encryption_payload_t *this, payload_t **payload); + + /** + * @brief Get the number of payloads. + * + * @param this calling encryption_payload_t object + * @return number of contained payloads + */ + size_t (*get_payload_count) (encryption_payload_t *this); + + /** + * @brief Set transforms to use. + * + * To decryption, encryption, signature building and verifying, + * the payload needs a crypter and a signer object. + * + * @warning Do NOT call this function again after encryption, since + * the signer must be the same while encrypting and signature building! + * + * @param this calling encryption_payload_t + * @param crypter crypter_t to use for data de-/encryption + * @param signer signer_t to use for data signing/verifying + */ + void (*set_transforms) (encryption_payload_t *this, crypter_t *crypter, signer_t *signer); + + /** + * @brief Generate and encrypt contained payloads. + * + * This function generates the content for added payloads + * and encrypts them. Signature is not built, since we need + * additional data (the full message). + * + * @param this calling encryption_payload_t + * @return + * - SUCCESS, or + * - INVALID_STATE if transforms not set + */ + status_t (*encrypt) (encryption_payload_t *this); + + /** + * @brief Decrypt and parse contained payloads. + * + * This function decrypts the contained data. After, + * the payloads are parsed internally and are accessible + * via the iterator. + * + * @param this calling encryption_payload_t + * @return + * - SUCCESS, or + * - INVALID_STATE if transforms not set, or + * - FAILED if data is invalid + */ + status_t (*decrypt) (encryption_payload_t *this); + + /** + * @brief Build the signature. + * + * The signature is built over the FULL message, so the header + * and every payload (inclusive this one) must already be generated. + * The generated message is supplied via the data paramater. + * + * @param this calling encryption_payload_t + * @param data chunk contains the already generated message + * @return + * - SUCCESS, or + * - INVALID_STATE if transforms not set + */ + status_t (*build_signature) (encryption_payload_t *this, chunk_t data); + + /** + * @brief Verify the signature. + * + * Since the signature is built over the full message, we need + * this data to do the verification. The message data + * is supplied via the data argument. + * + * @param this calling encryption_payload_t + * @param data chunk contains the message + * @return + * - SUCCESS, or + * - FAILED if signature invalid, or + * - INVALID_STATE if transforms not set + */ + status_t (*verify_signature) (encryption_payload_t *this, chunk_t data); + + /** + * @brief Destroys an encryption_payload_t object. + * + * @param this encryption_payload_t object to destroy + */ + void (*destroy) (encryption_payload_t *this); +}; + +/** + * @brief Creates an empty encryption_payload_t object. + * + * @return encryption_payload_t object + * + * @ingroup payloads + */ +encryption_payload_t *encryption_payload_create(void); + + +#endif /*ENCRYPTION_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/id_payload.c b/src/charon/encoding/payloads/id_payload.c new file mode 100644 index 000000000..74c0ce870 --- /dev/null +++ b/src/charon/encoding/payloads/id_payload.c @@ -0,0 +1,323 @@ +/** + * @file id_payload.h + * + * @brief Interface of id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "id_payload.h" + +#include +#include + +typedef struct private_id_payload_t private_id_payload_t; + +/** + * Private data of an id_payload_t object. + * + */ +struct private_id_payload_t { + /** + * Public id_payload_t interface. + */ + id_payload_t public; + + /** + * TRUE if this ID payload is of type IDi, FALSE for IDr. + */ + bool is_initiator; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Type of the ID Data. + */ + u_int8_t id_type; + + /** + * The contained id data value. + */ + chunk_t id_data; +}; + +/** + * Encoding rules to parse or generate a ID payload + * + * The defined offsets are the positions in a object of type + * private_id_payload_t. + * + */ +encoding_rule_t id_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_id_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_id_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_id_payload_t, payload_length) }, + /* 1 Byte ID type*/ + { U_INT_8, offsetof(private_id_payload_t, id_type) }, + /* 3 reserved bytes */ + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* some id data bytes, length is defined in PAYLOAD_LENGTH */ + { ID_DATA, offsetof(private_id_payload_t, id_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ID Type ! RESERVED | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Identification Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_id_payload_t *this) +{ + if ((this->id_type == 0) || + (this->id_type == 4) || + ((this->id_type >= 6) && (this->id_type <= 8)) || + ((this->id_type >= 12) && (this->id_type <= 200))) + { + /* reserved IDs */ + DBG1(DBG_ENC, "received ID with reserved type %d", this->id_type); + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of id_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_id_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = id_payload_encodings; + *rule_count = sizeof(id_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_id_payload_t *this) +{ + if (this->is_initiator) + { + return ID_INITIATOR; + } + else + { + return ID_RESPONDER; + } +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_id_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_id_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_id_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of id_payload_t.set_type. + */ +static void set_id_type (private_id_payload_t *this, id_type_t type) +{ + this->id_type = type; +} + +/** + * Implementation of id_payload_t.get_id_type. + */ +static id_type_t get_id_type (private_id_payload_t *this) +{ + return (this->id_type); +} + +/** + * Implementation of id_payload_t.set_data. + */ +static void set_data (private_id_payload_t *this, chunk_t data) +{ + if (this->id_data.ptr != NULL) + { + chunk_free(&(this->id_data)); + } + this->id_data.ptr = clalloc(data.ptr,data.len); + this->id_data.len = data.len; + this->payload_length = ID_PAYLOAD_HEADER_LENGTH + this->id_data.len; +} + + +/** + * Implementation of id_payload_t.get_data_clone. + */ +static chunk_t get_data (private_id_payload_t *this) +{ + return (this->id_data); +} + +/** + * Implementation of id_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_id_payload_t *this) +{ + chunk_t cloned_data; + if (this->id_data.ptr == NULL) + { + return (this->id_data); + } + cloned_data.ptr = clalloc(this->id_data.ptr,this->id_data.len); + cloned_data.len = this->id_data.len; + return cloned_data; +} + +/** + * Implementation of id_payload_t.get_initiator. + */ +static bool get_initiator (private_id_payload_t *this) +{ + return (this->is_initiator); +} + +/** + * Implementation of id_payload_t.set_initiator. + */ +static void set_initiator (private_id_payload_t *this,bool is_initiator) +{ + this->is_initiator = is_initiator; +} + +/** + * Implementation of id_payload_t.get_identification. + */ +static identification_t *get_identification (private_id_payload_t *this) +{ + return identification_create_from_encoding(this->id_type,this->id_data); +} + +/** + * Implementation of payload_t.destroy and id_payload_t.destroy. + */ +static void destroy(private_id_payload_t *this) +{ + if (this->id_data.ptr != NULL) + { + chunk_free(&(this->id_data)); + } + free(this); +} + +/* + * Described in header. + */ +id_payload_t *id_payload_create(bool is_initiator) +{ + private_id_payload_t *this = malloc_thing(private_id_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (id_payload_t *)) destroy; + this->public.set_id_type = (void (*) (id_payload_t *,id_type_t)) set_id_type; + this->public.get_id_type = (id_type_t (*) (id_payload_t *)) get_id_type; + this->public.set_data = (void (*) (id_payload_t *,chunk_t)) set_data; + this->public.get_data = (chunk_t (*) (id_payload_t *)) get_data; + this->public.get_data_clone = (chunk_t (*) (id_payload_t *)) get_data_clone; + + this->public.get_initiator = (bool (*) (id_payload_t *)) get_initiator; + this->public.set_initiator = (void (*) (id_payload_t *,bool)) set_initiator; + this->public.get_identification = (identification_t * (*) (id_payload_t *this)) get_identification; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =ID_PAYLOAD_HEADER_LENGTH; + this->id_data = chunk_empty; + this->is_initiator = is_initiator; + + return (&(this->public)); +} + +/* + * Described in header. + */ +id_payload_t *id_payload_create_from_identification(bool is_initiator,identification_t *identification) +{ + id_payload_t *this= id_payload_create(is_initiator); + this->set_data(this,identification->get_encoding(identification)); + this->set_id_type(this,identification->get_type(identification)); + return this; +} diff --git a/src/charon/encoding/payloads/id_payload.h b/src/charon/encoding/payloads/id_payload.h new file mode 100644 index 000000000..b67d85d2e --- /dev/null +++ b/src/charon/encoding/payloads/id_payload.h @@ -0,0 +1,172 @@ +/** + * @file id_payload.h + * + * @brief Interface of id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef ID_PAYLOAD_H_ +#define ID_PAYLOAD_H_ + +typedef struct id_payload_t id_payload_t; + +#include +#include +#include + +/** + * Length of a id payload without the data in bytes. + * + * @ingroup payloads + */ +#define ID_PAYLOAD_HEADER_LENGTH 8 + +/** + * Object representing an IKEv2 ID payload. + * + * The ID payload format is described in RFC section 3.5. + * + * @b Constructors: + * - id_payload_create_from_identification() + * - id_payload_create() + * + * @ingroup payloads + */ +struct id_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the ID type. + * + * @param this calling id_payload_t object + * @param type Type of ID + */ + void (*set_id_type) (id_payload_t *this, id_type_t type); + + /** + * @brief Get the ID type. + * + * @param this calling id_payload_t object + * @return type of the ID + */ + id_type_t (*get_id_type) (id_payload_t *this); + + /** + * @brief Set the ID data. + * + * Data are getting cloned. + * + * @param this calling id_payload_t object + * @param data ID data as chunk_t + */ + void (*set_data) (id_payload_t *this, chunk_t data); + + /** + * @brief Get the ID data. + * + * Returned data are a copy of the internal one + * + * @param this calling id_payload_t object + * @return ID data as chunk_t + */ + chunk_t (*get_data_clone) (id_payload_t *this); + + /** + * @brief Get the ID data. + * + * Returned data are NOT copied. + * + * @param this calling id_payload_t object + * @return ID data as chunk_t + */ + chunk_t (*get_data) (id_payload_t *this); + + /** + * @brief Creates an identification object of this id payload. + * + * Returned object has to get destroyed by the caller. + * + * @param this calling id_payload_t object + * @return identification_t object + */ + identification_t *(*get_identification) (id_payload_t *this); + + /** + * @brief Get the type of ID payload (IDi or IDr). + * + * @param this calling id_payload_t object + * @return + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * + */ + bool (*get_initiator) (id_payload_t *this); + + /** + * @brief Set the type of ID payload (IDi or IDr). + * + * @param this calling id_payload_t object + * @param is_initiator + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * + */ + void (*set_initiator) (id_payload_t *this,bool is_initiator); + + /** + * @brief Destroys an id_payload_t object. + * + * @param this id_payload_t object to destroy + */ + void (*destroy) (id_payload_t *this); +}; + +/** + * @brief Creates an empty id_payload_t object. + * + * @param is_initiator + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * + * @return id_payload_t object + * + * @ingroup payloads + */ +id_payload_t *id_payload_create(bool is_initiator); + +/** + * @brief Creates an id_payload_t from an existing identification_t object. + * + * @param is_initiator + * - TRUE if this payload is of type IDi + * - FALSE if this payload is of type IDr + * @param identification identification_t object + * @return id_payload_t object + * + * @ingroup payloads + */ +id_payload_t *id_payload_create_from_identification(bool is_initiator,identification_t *identification); + + + +#endif /* ID_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/ike_header.c b/src/charon/encoding/payloads/ike_header.c new file mode 100644 index 000000000..b1b4fbf87 --- /dev/null +++ b/src/charon/encoding/payloads/ike_header.c @@ -0,0 +1,406 @@ +/** + * @file ike_header.c + * + * @brief Implementation of ike_header_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +/* offsetof macro */ +#include + +#include "ike_header.h" + +#include + + +typedef struct private_ike_header_t private_ike_header_t; + +/** + * Private data of an ike_header_t object. + * + */ +struct private_ike_header_t { + /** + * Public interface. + */ + ike_header_t public; + + /** + * SPI of the initiator. + */ + u_int64_t initiator_spi; + + /** + * SPI of the responder. + */ + u_int64_t responder_spi; + + /** + * Next payload type. + */ + u_int8_t next_payload; + /** + * IKE major version. + */ + u_int8_t maj_version; + + /** + * IKE minor version. + */ + u_int8_t min_version; + + /** + * Exchange type . + */ + u_int8_t exchange_type; + + /** + * Flags of the Message. + * + */ + struct { + /** + * Sender is initiator of the associated IKE_SA_INIT-Exchange. + */ + bool initiator; + + /** + * Is protocol supporting higher version? + */ + bool version; + + /** + * TRUE, if this is a response, FALSE if its a Request. + */ + bool response; + } flags; + + /** + * Associated Message-ID. + */ + u_int32_t message_id; + + /** + * Length of the whole IKEv2-Message (header and all payloads). + */ + u_int32_t length; +}; + +ENUM_BEGIN(exchange_type_names, EXCHANGE_TYPE_UNDEFINED, EXCHANGE_TYPE_UNDEFINED, + "EXCHANGE_TYPE_UNDEFINED"); +ENUM_NEXT(exchange_type_names, IKE_SA_INIT, INFORMATIONAL, EXCHANGE_TYPE_UNDEFINED, + "IKE_SA_INIT", + "IKE_AUTH", + "CREATE_CHILD_SA", + "INFORMATIONAL"); +ENUM_END(exchange_type_names, INFORMATIONAL); + +/** + * Encoding rules to parse or generate a IKEv2-Header. + * + * The defined offsets are the positions in a object of type + * ike_header_t. + * + */ +encoding_rule_t ike_header_encodings[] = { + /* 8 Byte SPI, stored in the field initiator_spi */ + { IKE_SPI, offsetof(private_ike_header_t, initiator_spi) }, + /* 8 Byte SPI, stored in the field responder_spi */ + { IKE_SPI, offsetof(private_ike_header_t, responder_spi) }, + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_ike_header_t, next_payload) }, + /* 4 Bit major version, stored in the field maj_version */ + { U_INT_4, offsetof(private_ike_header_t, maj_version) }, + /* 4 Bit minor version, stored in the field min_version */ + { U_INT_4, offsetof(private_ike_header_t, min_version) }, + /* 8 Bit for the exchange type */ + { U_INT_8, offsetof(private_ike_header_t, exchange_type) }, + /* 2 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* 3 Bit flags, stored in the fields response, version and initiator */ + { FLAG, offsetof(private_ike_header_t, flags.response) }, + { FLAG, offsetof(private_ike_header_t, flags.version) }, + { FLAG, offsetof(private_ike_header_t, flags.initiator) }, + /* 3 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* 4 Byte message id, stored in the field message_id */ + { U_INT_32, offsetof(private_ike_header_t, message_id) }, + /* 4 Byte length fied, stored in the field length */ + { HEADER_LENGTH, offsetof(private_ike_header_t, length) } +}; + + +/* 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! IKE_SA Initiator's SPI ! + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! IKE_SA Responder's SPI ! + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Message ID ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_ike_header_t *this) +{ + if ((this->exchange_type < IKE_SA_INIT) || (this->exchange_type > INFORMATIONAL)) + { + /* unsupported exchange type */ + return FAILED; + } + if (this->initiator_spi == 0) + { + /* initiator spi not set */ + return FAILED; + } + + /* verification of version is not done in here */ + + return SUCCESS; +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(payload_t *this,payload_type_t type) +{ + ((private_ike_header_t *)this)->next_payload = type; +} +/** + * Implementation of ike_header_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi(private_ike_header_t *this) +{ + return this->initiator_spi; +} + +/** + * Implementation of ike_header_t.set_initiator_spi. + */ +static void set_initiator_spi(private_ike_header_t *this, u_int64_t initiator_spi) +{ + this->initiator_spi = initiator_spi; +} + +/** + * Implementation of ike_header_t.get_responder_spi. + */ +static u_int64_t get_responder_spi(private_ike_header_t *this) +{ + return this->responder_spi; +} + +/** + * Implementation of ike_header_t.set_responder_spi. + */ +static void set_responder_spi(private_ike_header_t *this, u_int64_t responder_spi) +{ + this->responder_spi = responder_spi; +} + +/** + * Implementation of ike_header_t.get_maj_version. + */ +static u_int8_t get_maj_version(private_ike_header_t *this) +{ + return this->maj_version; +} + +/** + * Implementation of ike_header_t.get_min_version. + */ +static u_int8_t get_min_version(private_ike_header_t *this) +{ + return this->min_version; +} + +/** + * Implementation of ike_header_t.get_response_flag. + */ +static bool get_response_flag(private_ike_header_t *this) +{ + return this->flags.response; +} + +/** + * Implementation of ike_header_t.set_response_flag. + */ +static void set_response_flag(private_ike_header_t *this, bool response) +{ + this->flags.response = response; +} + +/** + * Implementation of ike_header_t.get_version_flag. + */ +static bool get_version_flag(private_ike_header_t *this) +{ + return this->flags.version; +} + +/** + * Implementation of ike_header_t.get_initiator_flag. + */ +static bool get_initiator_flag(private_ike_header_t *this) +{ + return this->flags.initiator; +} + +/** + * Implementation of ike_header_t.set_initiator_flag. + */ +static void set_initiator_flag(private_ike_header_t *this, bool initiator) +{ + this->flags.initiator = initiator; +} + +/** + * Implementation of ike_header_t.get_exchange_type. + */ +static u_int8_t get_exchange_type(private_ike_header_t *this) +{ + return this->exchange_type; +} + +/** + * Implementation of ike_header_t.set_exchange_type. + */ +static void set_exchange_type(private_ike_header_t *this, u_int8_t exchange_type) +{ + this->exchange_type = exchange_type; +} + +/** + * Implements ike_header_t's get_message_id function. + * See #ike_header_t.get_message_id for description. + */ +static u_int32_t get_message_id(private_ike_header_t *this) +{ + return this->message_id; +} + +/** + * Implementation of ike_header_t.set_message_id. + */ +static void set_message_id(private_ike_header_t *this, u_int32_t message_id) +{ + this->message_id = message_id; +} + +/** + * Implementation of ike_header_t.destroy and payload_t.destroy. + */ +static void destroy(ike_header_t *this) +{ + free(this); +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = ike_header_encodings; + *rule_count = sizeof(ike_header_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(payload_t *this) +{ + return HEADER; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(payload_t *this) +{ + return (((private_ike_header_t*)this)->next_payload); +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(payload_t *this) +{ + return (((private_ike_header_t*)this)->length); +} + +/* + * Described in header. + */ +ike_header_t *ike_header_create() +{ + private_ike_header_t *this = malloc_thing(private_ike_header_t); + + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = get_encoding_rules; + this->public.payload_interface.get_length = get_length; + this->public.payload_interface.get_next_type = get_next_type; + this->public.payload_interface.set_next_type = set_next_type; + this->public.payload_interface.get_type = get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + this->public.destroy = destroy; + + this->public.get_initiator_spi = (u_int64_t (*) (ike_header_t*))get_initiator_spi; + this->public.set_initiator_spi = (void (*) (ike_header_t*,u_int64_t))set_initiator_spi; + this->public.get_responder_spi = (u_int64_t (*) (ike_header_t*))get_responder_spi; + this->public.set_responder_spi = (void (*) (ike_header_t *,u_int64_t))set_responder_spi; + this->public.get_maj_version = (u_int8_t (*) (ike_header_t*))get_maj_version; + this->public.get_min_version = (u_int8_t (*) (ike_header_t*))get_min_version; + this->public.get_response_flag = (bool (*) (ike_header_t*))get_response_flag; + this->public.set_response_flag = (void (*) (ike_header_t*,bool))set_response_flag; + this->public.get_version_flag = (bool (*) (ike_header_t*))get_version_flag; + this->public.get_initiator_flag = (bool (*) (ike_header_t*))get_initiator_flag; + this->public.set_initiator_flag = (void (*) (ike_header_t*,bool))set_initiator_flag; + this->public.get_exchange_type = (u_int8_t (*) (ike_header_t*))get_exchange_type; + this->public.set_exchange_type = (void (*) (ike_header_t*,u_int8_t))set_exchange_type; + this->public.get_message_id = (u_int32_t (*) (ike_header_t*))get_message_id; + this->public.set_message_id = (void (*) (ike_header_t*,u_int32_t))set_message_id; + + /* set default values of the fields */ + this->initiator_spi = 0; + this->responder_spi = 0; + this->next_payload = 0; + this->maj_version = IKE_MAJOR_VERSION; + this->min_version = IKE_MINOR_VERSION; + this->exchange_type = EXCHANGE_TYPE_UNDEFINED; + this->flags.initiator = TRUE; + this->flags.version = HIGHER_VERSION_SUPPORTED_FLAG; + this->flags.response = FALSE; + this->message_id = 0; + this->length = IKE_HEADER_LENGTH; + + return (ike_header_t*)this; +} diff --git a/src/charon/encoding/payloads/ike_header.h b/src/charon/encoding/payloads/ike_header.h new file mode 100644 index 000000000..95c20f810 --- /dev/null +++ b/src/charon/encoding/payloads/ike_header.h @@ -0,0 +1,260 @@ +/** + * @file ike_header.h + * + * @brief Interface of ike_header_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_HEADER_H_ +#define IKE_HEADER_H_ + +typedef enum exchange_type_t exchange_type_t; +typedef struct ike_header_t ike_header_t; + +#include +#include + +/** + * Major Version of IKEv2. + * + * @ingroup payloads + */ +#define IKE_MAJOR_VERSION 2 + +/** + * Minor Version of IKEv2. + * + * @ingroup payloads + */ +#define IKE_MINOR_VERSION 0 + +/** + * Flag in IKEv2-Header. Always 0. + * + * @ingroup payloads + */ +#define HIGHER_VERSION_SUPPORTED_FLAG 0 + +/** + * Length of IKE Header in Bytes. + * + * @ingroup payloads + */ +#define IKE_HEADER_LENGTH 28 + +/** + * @brief Different types of IKE-Exchanges. + * + * See Draft for different types. + * + * @ingroup payloads + */ +enum exchange_type_t{ + + /** + * EXCHANGE_TYPE_UNDEFINED. In private space, since not a official message type. + */ + EXCHANGE_TYPE_UNDEFINED = 240, + + /** + * IKE_SA_INIT. + */ + IKE_SA_INIT = 34, + + /** + * IKE_AUTH. + */ + IKE_AUTH = 35, + + /** + * CREATE_CHILD_SA. + */ + CREATE_CHILD_SA = 36, + + /** + * INFORMATIONAL. + */ + INFORMATIONAL = 37 +}; + +/** + * enum name for exchange_type_t + * + * @ingroup payloads + */ +extern enum_name_t *exchange_type_names; + +/** + * @brief An object of this type represents an IKEv2 header and is used to + * generate and parse IKEv2 headers. + * + * The header format of an IKEv2-Message is compatible to the + * ISAKMP-Header format to allow implementations supporting + * both versions of the IKE-protocol. + * + * @b Constructors: + * - ike_header_create() + * + * @ingroup payloads + */ +struct ike_header_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the initiator spi. + * + * @param this ike_header_t object + * @return initiator_spi + */ + u_int64_t (*get_initiator_spi) (ike_header_t *this); + + /** + * @brief Set the initiator spi. + * + * @param this ike_header_t object + * @param initiator_spi initiator_spi + */ + void (*set_initiator_spi) (ike_header_t *this, u_int64_t initiator_spi); + + /** + * @brief Get the responder spi. + * + * @param this ike_header_t object + * @return responder_spi + */ + u_int64_t (*get_responder_spi) (ike_header_t *this); + + /** + * @brief Set the responder spi. + * + * @param this ike_header_t object + * @param responder_spi responder_spi + */ + void (*set_responder_spi) (ike_header_t *this, u_int64_t responder_spi); + + /** + * @brief Get the major version. + * + * @param this ike_header_t object + * @return major version + */ + u_int8_t (*get_maj_version) (ike_header_t *this); + + /** + * @brief Get the minor version. + * + * @param this ike_header_t object + * @return minor version + */ + u_int8_t (*get_min_version) (ike_header_t *this); + + /** + * @brief Get the response flag. + * + * @param this ike_header_t object + * @return response flag + */ + bool (*get_response_flag) (ike_header_t *this); + + /** + * @brief Set the response flag- + * + * @param this ike_header_t object + * @param response response flag + * + */ + void (*set_response_flag) (ike_header_t *this, bool response); + /** + * @brief Get "higher version supported"-flag. + * + * @param this ike_header_t object + * @return version flag + */ + bool (*get_version_flag) (ike_header_t *this); + + /** + * @brief Get the initiator flag. + * + * @param this ike_header_t object + * @return initiator flag + */ + bool (*get_initiator_flag) (ike_header_t *this); + + /** + * @brief Set the initiator flag. + * + * @param this ike_header_t object + * @param initiator initiator flag + * + */ + void (*set_initiator_flag) (ike_header_t *this, bool initiator); + + /** + * @brief Get the exchange type. + * + * @param this ike_header_t object + * @return exchange type + */ + u_int8_t (*get_exchange_type) (ike_header_t *this); + + /** + * @brief Set the exchange type. + * + * @param this ike_header_t object + * @param exchange_type exchange type + */ + void (*set_exchange_type) (ike_header_t *this, u_int8_t exchange_type); + + /** + * @brief Get the message id. + * + * @param this ike_header_t object + * @return message id + */ + u_int32_t (*get_message_id) (ike_header_t *this); + + /** + * @brief Set the message id. + * + * @param this ike_header_t object + * @param initiator_spi message id + */ + void (*set_message_id) (ike_header_t *this, u_int32_t message_id); + + /** + * @brief Destroys a ike_header_t object. + * + * @param this ike_header_t object to destroy + */ + void (*destroy) (ike_header_t *this); +}; + +/** + * @brief Create an ike_header_t object + * + * @return ike_header_t object + * + * @ingroup payloads + */ +ike_header_t *ike_header_create(void); + +#endif /*IKE_HEADER_H_*/ diff --git a/src/charon/encoding/payloads/ke_payload.c b/src/charon/encoding/payloads/ke_payload.c new file mode 100644 index 000000000..8926b15f9 --- /dev/null +++ b/src/charon/encoding/payloads/ke_payload.c @@ -0,0 +1,277 @@ +/** + * @file ke_payload.c + * + * @brief Implementation of ke_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "ke_payload.h" + +#include + + +typedef struct private_ke_payload_t private_ke_payload_t; + +/** + * Private data of an ke_payload_t object. + * + */ +struct private_ke_payload_t { + /** + * Public ke_payload_t interface. + */ + ke_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * DH Group Number. + */ + u_int16_t dh_group_number; + + /** + * Key Exchange Data of this KE payload. + */ + chunk_t key_exchange_data; +}; + +/** + * Encoding rules to parse or generate a IKEv2-KE Payload. + * + * The defined offsets are the positions in a object of type + * private_ke_payload_t. + * + */ +encoding_rule_t ke_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_ke_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_ke_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_ke_payload_t, payload_length) }, + /* DH Group number as 16 bit field*/ + { U_INT_16, offsetof(private_ke_payload_t, dh_group_number) }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* Key Exchange Data is from variable size */ + { KEY_EXCHANGE_DATA, offsetof(private_ke_payload_t, key_exchange_data)} +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! DH Group # ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Key Exchange Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_ke_payload_t *this) +{ + /* dh group is not verified in here */ + return SUCCESS; +} + +/** + * Implementation of payload_t.destroy. + */ +static void destroy(private_ke_payload_t *this) +{ + if (this->key_exchange_data.ptr != NULL) + { + free(this->key_exchange_data.ptr); + } + free(this); +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_ke_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = ke_payload_encodings; + *rule_count = sizeof(ke_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_ke_payload_t *this) +{ + return KEY_EXCHANGE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_ke_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_ke_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length(private_ke_payload_t *this) +{ + size_t length = KE_PAYLOAD_HEADER_LENGTH; + if (this->key_exchange_data.ptr != NULL) + { + length += this->key_exchange_data.len; + } + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_ke_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of ke_payload_t.get_key_exchange_data. + */ +static chunk_t get_key_exchange_data(private_ke_payload_t *this) +{ + return (this->key_exchange_data); +} + +/** + * Implementation of ke_payload_t.set_key_exchange_data. + */ +static void set_key_exchange_data(private_ke_payload_t *this, chunk_t key_exchange_data) +{ + /* destroy existing data first */ + if (this->key_exchange_data.ptr != NULL) + { + /* free existing value */ + free(this->key_exchange_data.ptr); + this->key_exchange_data.ptr = NULL; + this->key_exchange_data.len = 0; + + } + + this->key_exchange_data = chunk_clone(key_exchange_data); + compute_length(this); +} + +/** + * Implementation of ke_payload_t.get_dh_group_number. + */ +static diffie_hellman_group_t get_dh_group_number(private_ke_payload_t *this) +{ + return this->dh_group_number; +} + +/** + * Implementation of ke_payload_t.set_dh_group_number. + */ +static void set_dh_group_number(private_ke_payload_t *this, diffie_hellman_group_t dh_group_number) +{ + this->dh_group_number = dh_group_number; +} + +/* + * Described in header + */ +ke_payload_t *ke_payload_create() +{ + private_ke_payload_t *this = malloc_thing(private_ke_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.get_key_exchange_data = (chunk_t (*) (ke_payload_t *)) get_key_exchange_data; + this->public.set_key_exchange_data = (void (*) (ke_payload_t *,chunk_t)) set_key_exchange_data; + this->public.get_dh_group_number = (diffie_hellman_group_t (*) (ke_payload_t *)) get_dh_group_number; + this->public.set_dh_group_number =(void (*) (ke_payload_t *,diffie_hellman_group_t)) set_dh_group_number; + this->public.destroy = (void (*) (ke_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = KE_PAYLOAD_HEADER_LENGTH; + this->key_exchange_data = chunk_empty; + this->dh_group_number = MODP_NONE; + + return &this->public; +} + +/* + * Described in header + */ +ke_payload_t *ke_payload_create_from_diffie_hellman(diffie_hellman_t *dh) +{ + private_ke_payload_t *this = (private_ke_payload_t*)ke_payload_create(); + + dh->get_my_public_value(dh, &this->key_exchange_data); + this->dh_group_number = dh->get_dh_group(dh); + compute_length(this); + + return &this->public; +} diff --git a/src/charon/encoding/payloads/ke_payload.h b/src/charon/encoding/payloads/ke_payload.h new file mode 100644 index 000000000..52be8ffe3 --- /dev/null +++ b/src/charon/encoding/payloads/ke_payload.h @@ -0,0 +1,121 @@ +/** + * @file ke_payload.h + * + * @brief Interface of ke_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef KE_PAYLOAD_H_ +#define KE_PAYLOAD_H_ + +typedef struct ke_payload_t ke_payload_t; + +#include +#include +#include +#include +#include + +/** + * KE payload length in bytes without any key exchange data. + * + * @ingroup payloads + */ +#define KE_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2-KE Payload. + * + * The KE Payload format is described in RFC section 3.4. + * + * @b Constructors: + * - ke_payload_create() + * + * @ingroup payloads + */ +struct ke_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Returns the currently set key exchange data of this KE payload. + * + * @warning Returned data are not copied. + * + * @param this calling ke_payload_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_key_exchange_data) (ke_payload_t *this); + + /** + * @brief Sets the key exchange data of this KE payload. + * + * @warning Value is getting copied. + * + * @param this calling ke_payload_t object + * @param key_exchange_data chunk_t pointing to the value to set + */ + void (*set_key_exchange_data) (ke_payload_t *this, chunk_t key_exchange_data); + + /** + * @brief Gets the Diffie-Hellman Group Number of this KE payload. + * + * @param this calling ke_payload_t object + * @return DH Group Number of this payload + */ + diffie_hellman_group_t (*get_dh_group_number) (ke_payload_t *this); + + /** + * @brief Sets the Diffie-Hellman Group Number of this KE payload. + * + * @param this calling ke_payload_t object + * @param dh_group_number DH Group to set + */ + void (*set_dh_group_number) (ke_payload_t *this, diffie_hellman_group_t dh_group_number); + + /** + * @brief Destroys an ke_payload_t object. + * + * @param this ke_payload_t object to destroy + */ + void (*destroy) (ke_payload_t *this); +}; + +/** + * @brief Creates an empty ke_payload_t object + * + * @return ke_payload_t object + * + * @ingroup payloads + */ +ke_payload_t *ke_payload_create(void); + +/** + * @brief Creates a ke_payload_t from a diffie_hellman_t + * + * @param diffie_hellman diffie hellman object containing group and key + * @return ke_payload_t object + * + * @ingroup payloads + */ +ke_payload_t *ke_payload_create_from_diffie_hellman(diffie_hellman_t *diffie_hellman); + +#endif /* KE_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/nonce_payload.c b/src/charon/encoding/payloads/nonce_payload.c new file mode 100644 index 000000000..8e1fc505e --- /dev/null +++ b/src/charon/encoding/payloads/nonce_payload.c @@ -0,0 +1,232 @@ +/** + * @file nonce_payload.h + * + * @brief Implementation of nonce_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +/* offsetof macro */ +#include + +#include "nonce_payload.h" + +#include + + +typedef struct private_nonce_payload_t private_nonce_payload_t; + +/** + * Private data of an nonce_payload_t object. + * + */ +struct private_nonce_payload_t { + /** + * Public nonce_payload_t interface. + */ + nonce_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * The contained nonce value. + */ + chunk_t nonce; +}; + +/** + * Encoding rules to parse or generate a nonce payload + * + * The defined offsets are the positions in a object of type + * private_nonce_payload_t. + * + */ +encoding_rule_t nonce_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_nonce_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_nonce_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole nonce payload*/ + { PAYLOAD_LENGTH, offsetof(private_nonce_payload_t, payload_length) }, + /* some nonce bytes, lenth is defined in PAYLOAD_LENGTH */ + { NONCE_DATA, offsetof(private_nonce_payload_t, nonce) } +}; + +/* 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Nonce Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_nonce_payload_t *this) +{ + if ((this->nonce.len < 16) || ((this->nonce.len > 256))) + { + /* nonce length is wrong */ + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of nonce_payload_t.set_nonce. + */ +static status_t set_nonce(private_nonce_payload_t *this, chunk_t nonce) +{ + this->nonce.ptr = clalloc(nonce.ptr, nonce.len); + this->nonce.len = nonce.len; + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH + nonce.len; + return SUCCESS; +} + +/** + * Implementation of nonce_payload_t.get_nonce. + */ +static chunk_t get_nonce(private_nonce_payload_t *this) +{ + chunk_t nonce; + nonce.ptr = clalloc(this->nonce.ptr,this->nonce.len); + nonce.len = this->nonce.len; + return nonce; +} + +/** + * Implementation of nonce_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_nonce_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = nonce_payload_encodings; + *rule_count = sizeof(nonce_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_nonce_payload_t *this) +{ + return NONCE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_nonce_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_nonce_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length(private_nonce_payload_t *this) +{ + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH + this->nonce.len; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_nonce_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of payload_t.destroy and nonce_payload_t.destroy. + */ +static void destroy(private_nonce_payload_t *this) +{ + if (this->nonce.ptr != NULL) + { + free(this->nonce.ptr); + } + + free(this); +} + +/* + * Described in header + */ +nonce_payload_t *nonce_payload_create() +{ + private_nonce_payload_t *this = malloc_thing(private_nonce_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (nonce_payload_t *)) destroy; + this->public.set_nonce = (void (*) (nonce_payload_t *,chunk_t)) set_nonce; + this->public.get_nonce = (chunk_t (*) (nonce_payload_t *)) get_nonce; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH; + this->nonce.ptr = NULL; + this->nonce.len = 0; + + return (&(this->public)); +} + + diff --git a/src/charon/encoding/payloads/nonce_payload.h b/src/charon/encoding/payloads/nonce_payload.h new file mode 100644 index 000000000..96d83b028 --- /dev/null +++ b/src/charon/encoding/payloads/nonce_payload.h @@ -0,0 +1,99 @@ +/** + * @file nonce_payload.h + * + * @brief Interface of nonce_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef NONCE_PAYLOAD_H_ +#define NONCE_PAYLOAD_H_ + +typedef struct nonce_payload_t nonce_payload_t; + +#include +#include + +/** + * Nonce size in bytes for nonces sending to other peer. + * + * @warning Nonce size MUST be between 16 and 256 bytes. + * + * @ingroup payloads + */ +#define NONCE_SIZE 16 + +/** + * Length of a nonce payload without a nonce in bytes. + * + * @ingroup payloads + */ +#define NONCE_PAYLOAD_HEADER_LENGTH 4 + +/** + * Object representing an IKEv2 Nonce payload. + * + * The Nonce payload format is described in RFC section 3.3. + * + * @b Constructors: + * - nonce_payload_create() + * + * @ingroup payloads + */ +struct nonce_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the nonce value. + * + * @param this calling nonce_payload_t object + * @param nonce chunk containing the nonce, will be cloned + */ + void (*set_nonce) (nonce_payload_t *this, chunk_t nonce); + + /** + * @brief Get the nonce value. + * + * @param this calling nonce_payload_t object + * @return a chunk containing the cloned nonce + */ + chunk_t (*get_nonce) (nonce_payload_t *this); + + /** + * @brief Destroys an nonce_payload_t object. + * + * @param this nonce_payload_t object to destroy + */ + void (*destroy) (nonce_payload_t *this); +}; + +/** + * @brief Creates an empty nonce_payload_t object + * + * @return nonce_payload_t object + * + * @ingroup payloads + */ + +nonce_payload_t *nonce_payload_create(void); + + +#endif /*NONCE_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c new file mode 100644 index 000000000..a04901a90 --- /dev/null +++ b/src/charon/encoding/payloads/notify_payload.c @@ -0,0 +1,481 @@ +/** + * @file notify_payload.c + * + * @brief Implementation of notify_payload_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "notify_payload.h" + +#include +#include +#include + +ENUM_BEGIN(notify_type_names, UNSUPPORTED_CRITICAL_PAYLOAD, UNSUPPORTED_CRITICAL_PAYLOAD, + "UNSUPPORTED_CRITICAL_PAYLOAD"); +ENUM_NEXT(notify_type_names, INVALID_IKE_SPI, INVALID_MAJOR_VERSION, UNSUPPORTED_CRITICAL_PAYLOAD, + "INVALID_IKE_SPI", + "INVALID_MAJOR_VERSION"); +ENUM_NEXT(notify_type_names, INVALID_SYNTAX, INVALID_SYNTAX, INVALID_MAJOR_VERSION, + "INVALID_SYNTAX"); +ENUM_NEXT(notify_type_names, INVALID_MESSAGE_ID, INVALID_MESSAGE_ID, INVALID_SYNTAX, + "INVALID_MESSAGE_ID"); +ENUM_NEXT(notify_type_names, INVALID_SPI, INVALID_SPI, INVALID_MESSAGE_ID, + "INVALID_SPI"); +ENUM_NEXT(notify_type_names, NO_PROPOSAL_CHOSEN, NO_PROPOSAL_CHOSEN, INVALID_SPI, + "NO_PROPOSAL_CHOSEN"); +ENUM_NEXT(notify_type_names, INVALID_KE_PAYLOAD, INVALID_KE_PAYLOAD, NO_PROPOSAL_CHOSEN, + "INVALID_KE_PAYLOAD"); +ENUM_NEXT(notify_type_names, AUTHENTICATION_FAILED, AUTHENTICATION_FAILED, INVALID_KE_PAYLOAD, + "AUTHENTICATION_FAILED"); +ENUM_NEXT(notify_type_names, SINGLE_PAIR_REQUIRED, INVALID_SELECTORS, AUTHENTICATION_FAILED, + "SINGLE_PAIR_REQUIRED", + "NO_ADDITIONAL_SAS", + "INTERNAL_ADDRESS_FAILURE", + "FAILED_CP_REQUIRED", + "TS_UNACCEPTABLE", + "INVALID_SELECTORS"); +ENUM_NEXT(notify_type_names, INITIAL_CONTACT, AUTH_LIFETIME, INVALID_SELECTORS, + "INITIAL_CONTACT", + "SET_WINDOW_SIZE", + "ADDITIONAL_TS_POSSIBLE", + "IPCOMP_SUPPORTED", + "NAT_DETECTION_SOURCE_IP", + "NAT_DETECTION_DESTINATION_IP", + "COOKIE", + "USE_TRANSPORT_MODE", + "HTTP_CERT_LOOKUP_SUPPORTED", + "REKEY_SA", + "ESP_TFC_PADDING_NOT_SUPPORTED", + "NON_FIRST_FRAGMENTS_ALSO", + "MOBIKE_SUPPORTED", + "ADDITIONAL_IP4_ADDRESS", + "ADDITIONAL_IP6_ADDRESS", + "NO_ADDITIONAL_ADDRESSES", + "UPDATE_SA_ADDRESSES", + "COOKIE2", + "NO_NATS_ALLOWED", + "AUTH_LIFETIME"); +ENUM_NEXT(notify_type_names, EAP_ONLY_AUTHENTICATION, EAP_ONLY_AUTHENTICATION, AUTH_LIFETIME, + "EAP_ONLY_AUTHENTICATION"); +ENUM_END(notify_type_names, EAP_ONLY_AUTHENTICATION); + +typedef struct private_notify_payload_t private_notify_payload_t; + +/** + * Private data of an notify_payload_t object. + * + */ +struct private_notify_payload_t { + /** + * Public notify_payload_t interface. + */ + notify_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Protocol id. + */ + u_int8_t protocol_id; + + /** + * Spi size. + */ + u_int8_t spi_size; + + /** + * Notify message type. + */ + u_int16_t notify_type; + + /** + * Security parameter index (spi). + */ + chunk_t spi; + + /** + * Notification data. + */ + chunk_t notification_data; +}; + +/** + * Encoding rules to parse or generate a IKEv2-Notify Payload. + * + * The defined offsets are the positions in a object of type + * private_notify_payload_t. + * + */ +encoding_rule_t notify_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_notify_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_notify_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_notify_payload_t, payload_length) }, + /* Protocol ID as 8 bit field*/ + { U_INT_8, offsetof(private_notify_payload_t, protocol_id) }, + /* SPI Size as 8 bit field*/ + { SPI_SIZE, offsetof(private_notify_payload_t, spi_size) }, + /* Notify message type as 16 bit field*/ + { U_INT_16, offsetof(private_notify_payload_t, notify_type) }, + /* SPI as variable length field*/ + { SPI, offsetof(private_notify_payload_t, spi) }, + /* Key Exchange Data is from variable size */ + { NOTIFICATION_DATA, offsetof(private_notify_payload_t, notification_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Protocol ID ! SPI Size ! Notify Message Type ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Security Parameter Index (SPI) ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Notification Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_notify_payload_t *this) +{ + switch (this->protocol_id) + { + case PROTO_NONE: + case PROTO_IKE: + case PROTO_AH: + case PROTO_ESP: + break; + default: + DBG1(DBG_ENC, "Unknown protocol (%d)", this->protocol_id); + return FAILED; + } + + switch (this->notify_type) + { + case INVALID_KE_PAYLOAD: + { + /* check notification data */ + diffie_hellman_group_t dh_group; + if (this->notification_data.len != 2) + { + DBG1(DBG_ENC, "invalid notify data length for %N (%d)", + notify_type_names, this->notify_type, + this->notification_data.len); + return FAILED; + } + dh_group = ntohs(*((u_int16_t*)this->notification_data.ptr)); + switch (dh_group) + { + case MODP_768_BIT: + case MODP_1024_BIT: + case MODP_1536_BIT: + case MODP_2048_BIT: + case MODP_3072_BIT: + case MODP_4096_BIT: + case MODP_6144_BIT: + case MODP_8192_BIT: + break; + default: + DBG1(DBG_ENC, "Bad DH group (%d)", dh_group); + return FAILED; + } + break; + } + case NAT_DETECTION_SOURCE_IP: + case NAT_DETECTION_DESTINATION_IP: + { + if (this->notification_data.len != HASH_SIZE_SHA1) + { + DBG1(DBG_ENC, "invalid %N notify length", + notify_type_names, this->notify_type); + return FAILED; + } + break; + } + case INVALID_SYNTAX: + case INVALID_MAJOR_VERSION: + case NO_PROPOSAL_CHOSEN: + { + if (this->notification_data.len != 0) + { + DBG1(DBG_ENC, "invalid %N notify", + notify_type_names, this->notify_type); + return FAILED; + } + break; + } + default: + /* TODO: verify */ + break; + } + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_notify_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = notify_payload_encodings; + *rule_count = sizeof(notify_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_notify_payload_t *this) +{ + return NOTIFY; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_notify_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_notify_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the payloads length. + */ +static void compute_length (private_notify_payload_t *this) +{ + size_t length = NOTIFY_PAYLOAD_HEADER_LENGTH; + if (this->notification_data.ptr != NULL) + { + length += this->notification_data.len; + } + if (this->spi.ptr != NULL) + { + length += this->spi.len; + } + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_notify_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of notify_payload_t.get_protocol_id. + */ +static u_int8_t get_protocol_id(private_notify_payload_t *this) +{ + return this->protocol_id; +} + +/** + * Implementation of notify_payload_t.set_protocol_id. + */ +static void set_protocol_id(private_notify_payload_t *this, u_int8_t protocol_id) +{ + this->protocol_id = protocol_id; +} + +/** + * Implementation of notify_payload_t.get_notify_type. + */ +static notify_type_t get_notify_type(private_notify_payload_t *this) +{ + return this->notify_type; +} + +/** + * Implementation of notify_payload_t.set_notify_type. + */ +static void set_notify_type(private_notify_payload_t *this, u_int16_t notify_type) +{ + this->notify_type = notify_type; +} + +/** + * Implementation of notify_payload_t.get_spi. + */ +static u_int32_t get_spi(private_notify_payload_t *this) +{ + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + if (this->spi.len == 4) + { + return *((u_int32_t*)this->spi.ptr); + } + default: + break; + } + return 0; +} + +/** + * Implementation of notify_payload_t.set_spi. + */ +static void set_spi(private_notify_payload_t *this, u_int32_t spi) +{ + chunk_free(&this->spi); + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + this->spi = chunk_alloc(4); + *((u_int32_t*)this->spi.ptr) = spi; + break; + default: + break; + } + this->spi_size = this->spi.len; + compute_length(this); +} + +/** + * Implementation of notify_payload_t.get_notification_data. + */ +static chunk_t get_notification_data(private_notify_payload_t *this) +{ + return (this->notification_data); +} + +/** + * Implementation of notify_payload_t.set_notification_data. + */ +static status_t set_notification_data(private_notify_payload_t *this, chunk_t notification_data) +{ + chunk_free(&this->notification_data); + if (notification_data.len > 0) + { + this->notification_data = chunk_clone(notification_data); + } + compute_length(this); + return SUCCESS; +} + +/** + * Implementation of notify_payload_t.destroy and notify_payload_t.destroy. + */ +static status_t destroy(private_notify_payload_t *this) +{ + chunk_free(&this->notification_data); + chunk_free(&this->spi); + free(this); + return SUCCESS; +} + +/* + * Described in header + */ +notify_payload_t *notify_payload_create() +{ + private_notify_payload_t *this = malloc_thing(private_notify_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.get_protocol_id = (u_int8_t (*) (notify_payload_t *)) get_protocol_id; + this->public.set_protocol_id = (void (*) (notify_payload_t *,u_int8_t)) set_protocol_id; + this->public.get_notify_type = (notify_type_t (*) (notify_payload_t *)) get_notify_type; + this->public.set_notify_type = (void (*) (notify_payload_t *,notify_type_t)) set_notify_type; + this->public.get_spi = (u_int32_t (*) (notify_payload_t *)) get_spi; + this->public.set_spi = (void (*) (notify_payload_t *,u_int32_t)) set_spi; + this->public.get_notification_data = (chunk_t (*) (notify_payload_t *)) get_notification_data; + this->public.set_notification_data = (void (*) (notify_payload_t *,chunk_t)) set_notification_data; + this->public.destroy = (void (*) (notify_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = NOTIFY_PAYLOAD_HEADER_LENGTH; + this->protocol_id = 0; + this->notify_type = 0; + this->spi.ptr = NULL; + this->spi.len = 0; + this->spi_size = 0; + this->notification_data.ptr = NULL; + this->notification_data.len = 0; + + return &this->public; +} + +/* + * Described in header. + */ +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_type_t notify_type) +{ + notify_payload_t *notify = notify_payload_create(); + + notify->set_notify_type(notify,notify_type); + notify->set_protocol_id(notify,protocol_id); + + return notify; +} diff --git a/src/charon/encoding/payloads/notify_payload.h b/src/charon/encoding/payloads/notify_payload.h new file mode 100644 index 000000000..431932631 --- /dev/null +++ b/src/charon/encoding/payloads/notify_payload.h @@ -0,0 +1,224 @@ +/** + * @file notify_payload.h + * + * @brief Interface of notify_payload_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef NOTIFY_PAYLOAD_H_ +#define NOTIFY_PAYLOAD_H_ + +typedef enum notify_type_t notify_type_t; +typedef struct notify_payload_t notify_payload_t; + +#include +#include +#include +#include + +/** + * Notify payload length in bytes without any spi and notification data. + * + * @ingroup payloads + */ +#define NOTIFY_PAYLOAD_HEADER_LENGTH 8 + +/** + * @brief Notify message types. + * + * See IKEv2 RFC 3.10.1. + * + * @ingroup payloads + */ +enum notify_type_t { + /* notify error messages */ + UNSUPPORTED_CRITICAL_PAYLOAD = 1, + INVALID_IKE_SPI = 4, + INVALID_MAJOR_VERSION = 5, + INVALID_SYNTAX = 7, + INVALID_MESSAGE_ID = 9, + INVALID_SPI = 11, + NO_PROPOSAL_CHOSEN = 14, + INVALID_KE_PAYLOAD = 17, + AUTHENTICATION_FAILED = 24, + SINGLE_PAIR_REQUIRED = 34, + NO_ADDITIONAL_SAS = 35, + INTERNAL_ADDRESS_FAILURE = 36, + FAILED_CP_REQUIRED = 37, + TS_UNACCEPTABLE = 38, + INVALID_SELECTORS = 39, + /* notify status messages */ + INITIAL_CONTACT = 16384, + SET_WINDOW_SIZE = 16385, + ADDITIONAL_TS_POSSIBLE = 16386, + IPCOMP_SUPPORTED = 16387, + NAT_DETECTION_SOURCE_IP = 16388, + NAT_DETECTION_DESTINATION_IP = 16389, + COOKIE = 16390, + USE_TRANSPORT_MODE = 16391, + HTTP_CERT_LOOKUP_SUPPORTED = 16392, + REKEY_SA = 16393, + ESP_TFC_PADDING_NOT_SUPPORTED = 16394, + NON_FIRST_FRAGMENTS_ALSO = 16395, + /* mobike extension, RFC4555 */ + MOBIKE_SUPPORTED = 16396, + ADDITIONAL_IP4_ADDRESS = 16397, + ADDITIONAL_IP6_ADDRESS = 16398, + NO_ADDITIONAL_ADDRESSES = 16399, + UPDATE_SA_ADDRESSES = 16400, + COOKIE2 = 16401, + NO_NATS_ALLOWED = 16402, + /* repeated authentication extension, RFC4478 */ + AUTH_LIFETIME = 16403, + /* draft-eronen-ipsec-ikev2-eap-auth, not assigned by IANA yet */ + EAP_ONLY_AUTHENTICATION = 40960, + /* BEET mode, not even a draft yet. private use */ + USE_BEET_MODE = 40961, +}; + +/** + * enum name for notify_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *notify_type_names; + +/** + * @brief Class representing an IKEv2-Notify Payload. + * + * The Notify Payload format is described in Draft section 3.10. + * + * @b Constructors: + * - notify_payload_create() + * - notify_payload_create_from_protocol_and_type() + * + * @todo Build specified constructor/getter for notify's + * + * @ingroup payloads + */ +struct notify_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Gets the protocol id of this payload. + * + * @param this calling notify_payload_t object + * @return protocol id of this payload + */ + u_int8_t (*get_protocol_id) (notify_payload_t *this); + + /** + * @brief Sets the protocol id of this payload. + * + * @param this calling notify_payload_t object + * @param protocol_id protocol id to set + */ + void (*set_protocol_id) (notify_payload_t *this, u_int8_t protocol_id); + + /** + * @brief Gets the notify message type of this payload. + * + * @param this calling notify_payload_t object + * @return notify message type of this payload + */ + notify_type_t (*get_notify_type) (notify_payload_t *this); + + /** + * @brief Sets notify message type of this payload. + * + * @param this calling notify_payload_t object + * @param type notify message type to set + */ + void (*set_notify_type) (notify_payload_t *this, notify_type_t type); + + /** + * @brief Returns the currently set spi of this payload. + * + * This is only valid for notifys with protocol AH|ESP + * + * @param this calling notify_payload_t object + * @return SPI value + */ + u_int32_t (*get_spi) (notify_payload_t *this); + + /** + * @brief Sets the spi of this payload. + * + * This is only valid for notifys with protocol AH|ESP + * + * @param this calling notify_payload_t object + * @param spi SPI value + */ + void (*set_spi) (notify_payload_t *this, u_int32_t spi); + + /** + * @brief Returns the currently set notification data of payload. + * + * @warning Returned data are not copied. + * + * @param this calling notify_payload_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_notification_data) (notify_payload_t *this); + + /** + * @brief Sets the notification data of this payload. + * + * @warning Value is getting copied. + * + * @param this calling notify_payload_t object + * @param notification_data chunk_t pointing to the value to set + */ + void (*set_notification_data) (notify_payload_t *this, chunk_t notification_data); + + /** + * @brief Destroys an notify_payload_t object. + * + * @param this notify_payload_t object to destroy + */ + void (*destroy) (notify_payload_t *this); +}; + +/** + * @brief Creates an empty notify_payload_t object + * + * @return created notify_payload_t object + * + * @ingroup payloads + */ +notify_payload_t *notify_payload_create(void); + +/** + * @brief Creates an notify_payload_t object of specific type for specific protocol id. + * + * @param protocol_id protocol id (IKE, AH or ESP) + * @param type notify type (see notify_type_t) + * @return notify_payload_t object + * + * @ingroup payloads + */ +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_type_t type); + + +#endif /*NOTIFY_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/payload.c b/src/charon/encoding/payloads/payload.c new file mode 100644 index 000000000..3bd4cdb13 --- /dev/null +++ b/src/charon/encoding/payloads/payload.c @@ -0,0 +1,161 @@ +/** + * @file payload.c + * + * @brief Generic constructor to the payload_t interface. + * + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "payload.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD, + "NO_PAYLOAD"); +ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NO_PAYLOAD, + "SECURITY_ASSOCIATION", + "KEY_EXCHANGE", + "ID_INITIATOR", + "ID_RESPONDER", + "CERTIFICATE", + "CERTIFICATE_REQUEST", + "AUTHENTICATION", + "NONCE", + "NOTIFY", + "DELETE", + "VENDOR_ID", + "TRAFFIC_SELECTOR_INITIATOR", + "TRAFFIC_SELECTOR_RESPONDER", + "ENCRYPTED", + "CONFIGURATION", + "EXTENSIBLE_AUTHENTICATION"); +ENUM_NEXT(payload_type_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION, + "HEADER", + "PROPOSAL_SUBSTRUCTURE", + "TRANSFORM_SUBSTRUCTURE", + "TRANSFORM_ATTRIBUTE", + "TRAFFIC_SELECTOR_SUBSTRUCTURE", + "CONFIGURATION_ATTRIBUTE", + "UNKNOWN_PAYLOAD"); +ENUM_END(payload_type_names, UNKNOWN_PAYLOAD); + +/* short forms of payload names */ +ENUM_BEGIN(payload_type_short_names, NO_PAYLOAD, NO_PAYLOAD, + "--"); +ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NO_PAYLOAD, + "SA", + "KE", + "IDi", + "IDr", + "CERT", + "CERTREQ", + "AUTH", + "No", + "N", + "D", + "V", + "TSi", + "TSr", + "E", + "CP", + "EAP"); +ENUM_NEXT(payload_type_short_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION, + "HDR", + "PROP", + "TRANS", + "TRANSATTR", + "TSSUB", + "CPATTR", + "??"); +ENUM_END(payload_type_short_names, UNKNOWN_PAYLOAD); + +/* + * see header + */ +payload_t *payload_create(payload_type_t type) +{ + switch (type) + { + case HEADER: + return (payload_t*)ike_header_create(); + case SECURITY_ASSOCIATION: + return (payload_t*)sa_payload_create(); + case PROPOSAL_SUBSTRUCTURE: + return (payload_t*)proposal_substructure_create(); + case TRANSFORM_SUBSTRUCTURE: + return (payload_t*)transform_substructure_create(); + case TRANSFORM_ATTRIBUTE: + return (payload_t*)transform_attribute_create(); + case NONCE: + return (payload_t*)nonce_payload_create(); + case ID_INITIATOR: + return (payload_t*)id_payload_create(TRUE); + case ID_RESPONDER: + return (payload_t*)id_payload_create(FALSE); + case AUTHENTICATION: + return (payload_t*)auth_payload_create(); + case CERTIFICATE: + return (payload_t*)cert_payload_create(); + case CERTIFICATE_REQUEST: + return (payload_t*)certreq_payload_create(); + case TRAFFIC_SELECTOR_SUBSTRUCTURE: + return (payload_t*)traffic_selector_substructure_create(); + case TRAFFIC_SELECTOR_INITIATOR: + return (payload_t*)ts_payload_create(TRUE); + case TRAFFIC_SELECTOR_RESPONDER: + return (payload_t*)ts_payload_create(FALSE); + case KEY_EXCHANGE: + return (payload_t*)ke_payload_create(); + case NOTIFY: + return (payload_t*)notify_payload_create(); + case DELETE: + return (payload_t*)delete_payload_create(0); + case VENDOR_ID: + return (payload_t*)vendor_id_payload_create(); + case CONFIGURATION: + return (payload_t*)cp_payload_create(); + case CONFIGURATION_ATTRIBUTE: + return (payload_t*)configuration_attribute_create(); + case EXTENSIBLE_AUTHENTICATION: + return (payload_t*)eap_payload_create(); + case ENCRYPTED: + return (payload_t*)encryption_payload_create(); + default: + return (payload_t*)unknown_payload_create(); + } +} + diff --git a/src/charon/encoding/payloads/payload.h b/src/charon/encoding/payloads/payload.h new file mode 100644 index 000000000..9a8c2f482 --- /dev/null +++ b/src/charon/encoding/payloads/payload.h @@ -0,0 +1,282 @@ +/** + * @file payload.h + * + * @brief Interface payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PAYLOAD_H_ +#define PAYLOAD_H_ + +typedef enum payload_type_t payload_type_t; +typedef struct payload_t payload_t; + +#include +#include + + +/** + * @brief Payload-Types of a IKEv2-Message. + * + * Header and substructures are also defined as + * payload types with values from PRIVATE USE space. + * + * @ingroup payloads + */ +enum payload_type_t{ + + /** + * End of payload list in next_payload + */ + NO_PAYLOAD = 0, + + /** + * The security association (SA) payload containing proposals. + */ + SECURITY_ASSOCIATION = 33, + + /** + * The key exchange (KE) payload containing diffie-hellman values. + */ + KEY_EXCHANGE = 34, + + /** + * Identification for the original initiator (IDi). + */ + ID_INITIATOR = 35, + + /** + * Identification for the original responder (IDr). + */ + ID_RESPONDER = 36, + + /** + * Certificate payload with certificates (CERT). + */ + CERTIFICATE = 37, + + /** + * Certificate request payload (CERTREQ). + */ + CERTIFICATE_REQUEST = 38, + + /** + * Authentication payload contains auth data (AUTH). + */ + AUTHENTICATION = 39, + + /** + * Nonces, for initator and responder (Ni, Nr, N) + */ + NONCE = 40, + + /** + * Notif paylaod (N). + */ + NOTIFY = 41, + + /** + * Delete payload (D) + */ + DELETE = 42, + + /** + * Vendor id paylpoad (V). + */ + VENDOR_ID = 43, + + /** + * Traffic selector for the original initiator (TSi). + */ + TRAFFIC_SELECTOR_INITIATOR = 44, + + /** + * Traffic selector for the original responser (TSr). + */ + TRAFFIC_SELECTOR_RESPONDER = 45, + + /** + * Encryption payload, contains other payloads (E). + */ + ENCRYPTED = 46, + + /** + * Configuration payload (CP). + */ + CONFIGURATION = 47, + + /** + * Extensible authentication payload (EAP). + */ + EXTENSIBLE_AUTHENTICATION = 48, + + /** + * Header has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle IKEv2-Header like a payload. + */ + HEADER = 140, + + /** + * PROPOSAL_SUBSTRUCTURE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a proposal substructure like a payload. + */ + PROPOSAL_SUBSTRUCTURE = 141, + + /** + * TRANSFORM_SUBSTRUCTURE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform substructure like a payload. + */ + TRANSFORM_SUBSTRUCTURE = 142, + + /** + * TRANSFORM_ATTRIBUTE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform attribute like a payload. + */ + TRANSFORM_ATTRIBUTE = 143, + + /** + * TRAFFIC_SELECTOR_SUBSTRUCTURE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform selector like a payload. + */ + TRAFFIC_SELECTOR_SUBSTRUCTURE = 144, + + /** + * CONFIGURATION_ATTRIBUTE has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a transform attribute like a payload. + */ + CONFIGURATION_ATTRIBUTE = 145, + + /** + * A unknown payload has a value of PRIVATE USE space. + * + * This payload type is not send over wire and just + * used internally to handle a unknown payload. + */ + UNKNOWN_PAYLOAD = 146, +}; + + +/** + * enum names for payload_type_t. + */ +extern enum_name_t *payload_type_names; + +/** + * enum names for payload_type_t in a short form. + */ +extern enum_name_t *payload_type_short_names; + +/** + * @brief Generic interface for all payload types (incl.header and substructures). + * + * To handle all kinds of payloads on a generic way, this interface must + * be implemented by every payload. This allows parser_t/generator_t a simple + * handling of all payloads. + * + * @b Constructors: + * - payload_create() with the payload to instantiate. + * + * @ingroup payloads + */ +struct payload_t { + + /** + * @brief Get encoding rules for this payload. + * + * @param this calling object + * @param[out] rules location to store pointer of first rule + * @param[out] rule_count location to store number of rules + */ + void (*get_encoding_rules) (payload_t *this, encoding_rule_t **rules, size_t *rule_count); + + /** + * @brief Get type of payload. + * + * @param this calling object + * @return type of this payload + */ + payload_type_t (*get_type) (payload_t *this); + + /** + * @brief Get type of next payload or NO_PAYLOAD (0) if this is the last one. + * + * @param this calling object + * @return type of next payload + */ + payload_type_t (*get_next_type) (payload_t *this); + + /** + * @brief Set type of next payload. + * + * @param this calling object + * @param type type of next payload + */ + void (*set_next_type) (payload_t *this,payload_type_t type); + + /** + * @brief Get length of payload. + * + * @param this calling object + * @return length of this payload + */ + size_t (*get_length) (payload_t *this); + + /** + * @brief Verifies payload structure and makes consistence check. + * + * @param this calling object + * @return + * - SUCCESS + * - FAILED if consistence not given + */ + status_t (*verify) (payload_t *this); + + /** + * @brief Destroys a payload and all included substructures. + * + * @param this payload to destroy + */ + void (*destroy) (payload_t *this); +}; + +/** + * @brief Create an empty payload. + * + * Useful for the parser, who wants a generic constructor for all payloads. + * It supports all payload_t methods. If a payload type is not known, + * an unknwon_paylod is created with the chunk of data in it. + * + * @param type type of the payload to create + * @return payload_t object + */ +payload_t *payload_create(payload_type_t type); + +#endif /*PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/proposal_substructure.c b/src/charon/encoding/payloads/proposal_substructure.c new file mode 100644 index 000000000..182d2b6e8 --- /dev/null +++ b/src/charon/encoding/payloads/proposal_substructure.c @@ -0,0 +1,603 @@ +/** + * @file proposal_substructure.h + * + * @brief Implementation of proposal_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "proposal_substructure.h" + +#include +#include +#include +#include +#include + + +/** + * IKEv1 Value for a proposal payload. + */ +#define PROPOSAL_TYPE_VALUE 2 + + +typedef struct private_proposal_substructure_t private_proposal_substructure_t; + +/** + * Private data of an proposal_substructure_t object. + * + */ +struct private_proposal_substructure_t { + /** + * Public proposal_substructure_t interface. + */ + proposal_substructure_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Length of this payload. + */ + u_int16_t proposal_length; + + /** + * Proposal number. + */ + u_int8_t proposal_number; + + /** + * Protocol ID. + */ + u_int8_t protocol_id; + + /** + * SPI size of the following SPI. + */ + u_int8_t spi_size; + + /** + * Number of transforms. + */ + u_int8_t transforms_count; + + /** + * SPI is stored as chunk. + */ + chunk_t spi; + + /** + * Transforms are stored in a linked_list_t. + */ + linked_list_t * transforms; +}; + +/** + * Encoding rules to parse or generate a Proposal substructure. + * + * The defined offsets are the positions in a object of type + * private_proposal_substructure_t. + */ +encoding_rule_t proposal_substructure_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_proposal_substructure_t, next_payload) }, + /* Reserved Byte is skipped */ + { RESERVED_BYTE, 0 }, + /* Length of the whole proposal substructure payload*/ + { PAYLOAD_LENGTH, offsetof(private_proposal_substructure_t, proposal_length) }, + /* proposal number is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, proposal_number) }, + /* protocol ID is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, protocol_id) }, + /* SPI Size has its own type */ + { SPI_SIZE, offsetof(private_proposal_substructure_t, spi_size) }, + /* Number of transforms is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, transforms_count) }, + /* SPI is a chunk of variable size*/ + { SPI, offsetof(private_proposal_substructure_t, spi) }, + /* Transforms are stored in a transform substructure, + offset points to a linked_list_t pointer */ + { TRANSFORMS, offsetof(private_proposal_substructure_t, transforms) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! 0 (last) or 2 ! RESERVED ! Proposal Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Proposal # ! Protocol ID ! SPI Size !# of Transforms! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ SPI (variable) ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_proposal_substructure_t *this) +{ + status_t status = SUCCESS; + iterator_t *iterator; + payload_t *current_transform; + + if ((this->next_payload != NO_PAYLOAD) && (this->next_payload != 2)) + { + /* must be 0 or 2 */ + DBG1(DBG_ENC, "inconsistent next payload"); + return FAILED; + } + if (this->transforms_count != this->transforms->get_count(this->transforms)) + { + /* must be the same! */ + DBG1(DBG_ENC, "transform count invalid"); + return FAILED; + } + + switch (this->protocol_id) + { + case PROTO_AH: + case PROTO_ESP: + if (this->spi.len != 4) + { + DBG1(DBG_ENC, "invalid SPI length in %N proposal", + protocol_id_names, this->protocol_id); + return FAILED; + } + break; + case PROTO_IKE: + if (this->spi.len != 0 && this->spi.len != 8) + { + DBG1(DBG_ENC, "invalid SPI length in IKE proposal"); + return FAILED; + } + break; + default: + DBG1(DBG_ENC, "invalid proposal protocol (%d)", this->protocol_id); + return FAILED; + } + if ((this->protocol_id == 0) || (this->protocol_id >= 4)) + { + /* reserved are not supported */ + DBG1(DBG_ENC, "invalid protocol"); + return FAILED; + } + + iterator = this->transforms->create_iterator(this->transforms,TRUE); + while(iterator->iterate(iterator, (void**)¤t_transform)) + { + status = current_transform->verify(current_transform); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "TRANSFORM_SUBSTRUCTURE verification failed"); + break; + } + } + iterator->destroy(iterator); + + /* proposal number is checked in SA payload */ + return status; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_proposal_substructure_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = proposal_substructure_encodings; + *rule_count = sizeof(proposal_substructure_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_proposal_substructure_t *this) +{ + return PROPOSAL_SUBSTRUCTURE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_proposal_substructure_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_proposal_substructure_t *this,payload_type_t type) +{ +} + +/** + * (re-)compute the length of the payload. + */ +static void compute_length(private_proposal_substructure_t *this) +{ + iterator_t *iterator; + payload_t *current_transform; + size_t transforms_count = 0; + size_t length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH; + + iterator = this->transforms->create_iterator(this->transforms,TRUE); + while (iterator->iterate(iterator, (void**)¤t_transform)) + { + length += current_transform->get_length(current_transform); + transforms_count++; + } + iterator->destroy(iterator); + + length += this->spi.len; + this->transforms_count = transforms_count; + this->proposal_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_proposal_substructure_t *this) +{ + compute_length(this); + return this->proposal_length; +} + +/** + * Implementation of proposal_substructure_t.create_transform_substructure_iterator. + */ +static iterator_t *create_transform_substructure_iterator (private_proposal_substructure_t *this,bool forward) +{ + return (this->transforms->create_iterator(this->transforms,forward)); +} + +/** + * Implementation of proposal_substructure_t.add_transform_substructure. + */ +static void add_transform_substructure (private_proposal_substructure_t *this,transform_substructure_t *transform) +{ + status_t status; + if (this->transforms->get_count(this->transforms) > 0) + { + transform_substructure_t *last_transform; + status = this->transforms->get_last(this->transforms,(void **) &last_transform); + /* last transform is now not anymore last one */ + last_transform->set_is_last_transform(last_transform,FALSE); + + } + transform->set_is_last_transform(transform,TRUE); + + this->transforms->insert_last(this->transforms,(void *) transform); + compute_length(this); +} + +/** + * Implementation of proposal_substructure_t.proposal_substructure_t. + */ +static void set_is_last_proposal (private_proposal_substructure_t *this, bool is_last) +{ + this->next_payload = (is_last) ? 0: PROPOSAL_TYPE_VALUE; +} + +/** + * Implementation of proposal_substructure_t.set_proposal_number. + */ +static void set_proposal_number(private_proposal_substructure_t *this,u_int8_t proposal_number) +{ + this->proposal_number = proposal_number; +} + +/** + * Implementation of proposal_substructure_t.get_proposal_number. + */ +static u_int8_t get_proposal_number (private_proposal_substructure_t *this) +{ + return (this->proposal_number); +} + +/** + * Implementation of proposal_substructure_t.set_protocol_id. + */ +static void set_protocol_id(private_proposal_substructure_t *this,u_int8_t protocol_id) +{ + this->protocol_id = protocol_id; +} + +/** + * Implementation of proposal_substructure_t.get_protocol_id. + */ +static u_int8_t get_protocol_id(private_proposal_substructure_t *this) +{ + return (this->protocol_id); +} + +/** + * Implementation of proposal_substructure_t.set_spi. + */ +static void set_spi(private_proposal_substructure_t *this, chunk_t spi) +{ + /* first delete already set spi value */ + if (this->spi.ptr != NULL) + { + free(this->spi.ptr); + this->spi.ptr = NULL; + this->spi.len = 0; + compute_length(this); + } + + this->spi.ptr = clalloc(spi.ptr,spi.len); + this->spi.len = spi.len; + this->spi_size = spi.len; + compute_length(this); +} + +/** + * Implementation of proposal_substructure_t.get_spi. + */ +static chunk_t get_spi(private_proposal_substructure_t *this) +{ + chunk_t spi; + spi.ptr = this->spi.ptr; + spi.len = this->spi.len; + + return spi; +} + +/** + * Implementation of proposal_substructure_t.get_transform_count. + */ +static size_t get_transform_count (private_proposal_substructure_t *this) +{ + return this->transforms->get_count(this->transforms); +} + +/** + * Implementation of proposal_substructure_t.get_spi_size. + */ +static size_t get_spi_size (private_proposal_substructure_t *this) +{ + return this->spi.len; +} + +/** + * Implementation of proposal_substructure_t.get_proposal. + */ +proposal_t* get_proposal(private_proposal_substructure_t *this) +{ + iterator_t *iterator; + transform_substructure_t *transform; + proposal_t *proposal; + u_int64_t spi; + + proposal = proposal_create(this->protocol_id); + + iterator = this->transforms->create_iterator(this->transforms, TRUE); + while (iterator->iterate(iterator, (void**)&transform)) + { + transform_type_t transform_type; + u_int16_t transform_id; + u_int16_t key_length = 0; + + transform_type = transform->get_transform_type(transform); + transform_id = transform->get_transform_id(transform); + transform->get_key_length(transform, &key_length); + + proposal->add_algorithm(proposal, transform_type, transform_id, key_length); + } + iterator->destroy(iterator); + + switch (this->spi.len) + { + case 4: + spi = *((u_int32_t*)this->spi.ptr); + break; + case 8: + spi = *((u_int64_t*)this->spi.ptr); + break; + default: + spi = 0; + } + proposal->set_spi(proposal, spi); + + return proposal; +} + +/** + * Implementation of proposal_substructure_t.clone. + */ +static private_proposal_substructure_t* clone_(private_proposal_substructure_t *this) +{ + private_proposal_substructure_t *clone; + iterator_t *transforms; + transform_substructure_t *current_transform; + + clone = (private_proposal_substructure_t *) proposal_substructure_create(); + clone->next_payload = this->next_payload; + clone->proposal_number = this->proposal_number; + clone->protocol_id = this->protocol_id; + clone->spi_size = this->spi_size; + if (this->spi.ptr != NULL) + { + clone->spi.ptr = clalloc(this->spi.ptr,this->spi.len); + clone->spi.len = this->spi.len; + } + + transforms = this->transforms->create_iterator(this->transforms,FALSE); + while (transforms->iterate(transforms, (void**)¤t_transform)) + { + current_transform = current_transform->clone(current_transform); + clone->public.add_transform_substructure(&clone->public, current_transform); + } + transforms->destroy(transforms); + + return clone; +} + +/** + * Implements payload_t's and proposal_substructure_t's destroy function. + * See #payload_s.destroy or proposal_substructure_s.destroy for description. + */ +static void destroy(private_proposal_substructure_t *this) +{ + this->transforms->destroy_offset(this->transforms, + offsetof(transform_substructure_t, destroy)); + chunk_free(&this->spi); + free(this); +} + +/* + * Described in header. + */ +proposal_substructure_t *proposal_substructure_create() +{ + private_proposal_substructure_t *this = malloc_thing(private_proposal_substructure_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + + /* public functions */ + this->public.create_transform_substructure_iterator = (iterator_t* (*) (proposal_substructure_t *,bool)) create_transform_substructure_iterator; + this->public.add_transform_substructure = (void (*) (proposal_substructure_t *,transform_substructure_t *)) add_transform_substructure; + this->public.set_proposal_number = (void (*) (proposal_substructure_t *,u_int8_t))set_proposal_number; + this->public.get_proposal_number = (u_int8_t (*) (proposal_substructure_t *)) get_proposal_number; + this->public.set_protocol_id = (void (*) (proposal_substructure_t *,u_int8_t))set_protocol_id; + this->public.get_protocol_id = (u_int8_t (*) (proposal_substructure_t *)) get_protocol_id; + this->public.set_is_last_proposal = (void (*) (proposal_substructure_t *,bool)) set_is_last_proposal; + this->public.get_proposal = (proposal_t* (*) (proposal_substructure_t*))get_proposal; + this->public.set_spi = (void (*) (proposal_substructure_t *,chunk_t))set_spi; + this->public.get_spi = (chunk_t (*) (proposal_substructure_t *)) get_spi; + this->public.get_transform_count = (size_t (*) (proposal_substructure_t *)) get_transform_count; + this->public.get_spi_size = (size_t (*) (proposal_substructure_t *)) get_spi_size; + this->public.clone = (proposal_substructure_t * (*) (proposal_substructure_t *)) clone_; + this->public.destroy = (void (*) (proposal_substructure_t *)) destroy; + + /* set default values of the fields */ + this->next_payload = NO_PAYLOAD; + this->proposal_length = 0; + this->proposal_number = 0; + this->protocol_id = 0; + this->transforms_count = 0; + this->spi_size = 0; + this->spi.ptr = NULL; + this->spi.len = 0; + + this->transforms = linked_list_create(); + + return (&(this->public)); +} + +/* + * Described in header. + */ +proposal_substructure_t *proposal_substructure_create_from_proposal(proposal_t *proposal) +{ + private_proposal_substructure_t *this = (private_proposal_substructure_t*) + proposal_substructure_create(); + iterator_t *iterator; + algorithm_t *algo; + transform_substructure_t *transform; + + /* encryption algorithm is only availble in ESP */ + iterator = proposal->create_algorithm_iterator(proposal, ENCRYPTION_ALGORITHM); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(ENCRYPTION_ALGORITHM, + algo->algorithm, algo->key_size); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* integrity algorithms */ + iterator = proposal->create_algorithm_iterator(proposal, INTEGRITY_ALGORITHM); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(INTEGRITY_ALGORITHM, + algo->algorithm, algo->key_size); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* prf algorithms */ + iterator = proposal->create_algorithm_iterator(proposal, PSEUDO_RANDOM_FUNCTION); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(PSEUDO_RANDOM_FUNCTION, + algo->algorithm, algo->key_size); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* dh groups */ + iterator = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(DIFFIE_HELLMAN_GROUP, algo->algorithm, 0); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* extended sequence numbers */ + iterator = proposal->create_algorithm_iterator(proposal, EXTENDED_SEQUENCE_NUMBERS); + while (iterator->iterate(iterator, (void**)&algo)) + { + transform = transform_substructure_create_type(EXTENDED_SEQUENCE_NUMBERS, + algo->algorithm, 0); + this->public.add_transform_substructure(&(this->public), transform); + } + iterator->destroy(iterator); + + /* add SPI, if necessary */ + switch (proposal->get_protocol(proposal)) + { + case PROTO_AH: + case PROTO_ESP: + this->spi_size = this->spi.len = 4; + this->spi.ptr = malloc(this->spi_size); + *((u_int32_t*)this->spi.ptr) = proposal->get_spi(proposal); + break; + case PROTO_IKE: + if (proposal->get_spi(proposal)) + { /* IKE only uses SPIS when rekeying, but on initial setup */ + this->spi_size = this->spi.len = 8; + this->spi.ptr = malloc(this->spi_size); + *((u_int64_t*)this->spi.ptr) = proposal->get_spi(proposal); + } + break; + default: + break; + } + this->proposal_number = 0; + this->protocol_id = proposal->get_protocol(proposal); + + return &this->public; +} diff --git a/src/charon/encoding/payloads/proposal_substructure.h b/src/charon/encoding/payloads/proposal_substructure.h new file mode 100644 index 000000000..93a8d7b2f --- /dev/null +++ b/src/charon/encoding/payloads/proposal_substructure.h @@ -0,0 +1,206 @@ +/** + * @file proposal_substructure.h + * + * @brief Interface of proposal_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PROPOSAL_SUBSTRUCTURE_H_ +#define PROPOSAL_SUBSTRUCTURE_H_ + +typedef struct proposal_substructure_t proposal_substructure_t; + +#include +#include +#include +#include +#include + + +/** + * Length of the proposal substructure header (without spi). + * + * @ingroup payloads + */ +#define PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2-PROPOSAL SUBSTRUCTURE. + * + * The PROPOSAL SUBSTRUCTURE format is described in RFC section 3.3.1. + * + * @b Constructors: + * - proposal_substructure_create() + * + * @ingroup payloads + */ +struct proposal_substructure_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored transform_substructure_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * When deleting any transform over this iterator, call + * get_size to make sure the length and number values are ok. + * + * @param this calling proposal_substructure_t object + * @param forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_transform_substructure_iterator) (proposal_substructure_t *this, bool forward); + + /** + * @brief Adds a transform_substructure_t object to this object. + * + * @warning The added transform_substructure_t object is + * getting destroyed in destroy function of proposal_substructure_t. + * + * @param this calling proposal_substructure_t object + * @param transform transform_substructure_t object to add + */ + void (*add_transform_substructure) (proposal_substructure_t *this,transform_substructure_t *transform); + + /** + * @brief Sets the proposal number of current proposal. + * + * @param this calling proposal_substructure_t object + * @param id proposal number to set + */ + void (*set_proposal_number) (proposal_substructure_t *this,u_int8_t proposal_number); + + /** + * @brief get proposal number of current proposal. + * + * @param this calling proposal_substructure_t object + * @return proposal number of current proposal substructure. + */ + u_int8_t (*get_proposal_number) (proposal_substructure_t *this); + + /** + * @brief get the number of transforms in current proposal. + * + * @param this calling proposal_substructure_t object + * @return transform count in current proposal + */ + size_t (*get_transform_count) (proposal_substructure_t *this); + + /** + * @brief get size of the set spi in bytes. + * + * @param this calling proposal_substructure_t object + * @return size of the spi in bytes + */ + size_t (*get_spi_size) (proposal_substructure_t *this); + + /** + * @brief Sets the protocol id of current proposal. + * + * @param this calling proposal_substructure_t object + * @param id protocol id to set + */ + void (*set_protocol_id) (proposal_substructure_t *this,u_int8_t protocol_id); + + /** + * @brief get protocol id of current proposal. + * + * @param this calling proposal_substructure_t object + * @return protocol id of current proposal substructure. + */ + u_int8_t (*get_protocol_id) (proposal_substructure_t *this); + + /** + * @brief Sets the next_payload field of this substructure + * + * If this is the last proposal, next payload field is set to 0, + * otherwise to 2 + * + * @param this calling proposal_substructure_t object + * @param is_last When TRUE, next payload field is set to 0, otherwise to 2 + */ + void (*set_is_last_proposal) (proposal_substructure_t *this, bool is_last); + + /** + * @brief Returns the currently set SPI of this proposal. + * + * @warning Returned data are not copied + * + * @param this calling proposal_substructure_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_spi) (proposal_substructure_t *this); + + /** + * @brief Sets the SPI of the current proposal. + * + * @warning SPI is getting copied + * + * @param this calling proposal_substructure_t object + * @param spi chunk_t pointing to the value to set + */ + void (*set_spi) (proposal_substructure_t *this, chunk_t spi); + + /** + * @brief Get a proposal_t from the propsal_substructure_t. + * + * @param this calling proposal_substructure_t object + * @return proposal_t + */ + proposal_t * (*get_proposal) (proposal_substructure_t *this); + + /** + * @brief Clones an proposal_substructure_t object. + * + * @param this proposal_substructure_t object to clone + * @return cloned object + */ + proposal_substructure_t* (*clone) (proposal_substructure_t *this); + + /** + * @brief Destroys an proposal_substructure_t object. + * + * @param this proposal_substructure_t object to destroy + */ + void (*destroy) (proposal_substructure_t *this); +}; + +/** + * @brief Creates an empty proposal_substructure_t object + * + * @return proposal_substructure_t object + * + * @ingroup payloads + */ +proposal_substructure_t *proposal_substructure_create(void); + +/** + * @brief Creates a proposal_substructure_t from a proposal_t. + * + * @param proposal proposal to build a substruct out of it + * @return proposal_substructure_t object + * + * @ingroup payloads + */ +proposal_substructure_t *proposal_substructure_create_from_proposal(proposal_t *proposal); + + +#endif /*PROPOSAL_SUBSTRUCTURE_H_*/ diff --git a/src/charon/encoding/payloads/sa_payload.c b/src/charon/encoding/payloads/sa_payload.c new file mode 100644 index 000000000..e264b2123 --- /dev/null +++ b/src/charon/encoding/payloads/sa_payload.c @@ -0,0 +1,375 @@ +/** + * @file sa_payload.c + * + * @brief Implementation of sa_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "sa_payload.h" + +#include +#include +#include + + +typedef struct private_sa_payload_t private_sa_payload_t; + +/** + * Private data of an sa_payload_t object. + * + */ +struct private_sa_payload_t { + /** + * Public sa_payload_t interface. + */ + sa_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Proposals in this payload are stored in a linked_list_t. + */ + linked_list_t * proposals; +}; + +/** + * Encoding rules to parse or generate a IKEv2-SA Payload + * + * The defined offsets are the positions in a object of type + * private_sa_payload_t. + * + */ +encoding_rule_t sa_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_sa_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_sa_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole SA payload*/ + { PAYLOAD_LENGTH, offsetof(private_sa_payload_t, payload_length) }, + /* Proposals are stored in a proposal substructure, + offset points to a linked_list_t pointer */ + { PROPOSALS, offsetof(private_sa_payload_t, proposals) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_sa_payload_t *this) +{ + int expected_number = 1, current_number; + status_t status = SUCCESS; + iterator_t *iterator; + proposal_substructure_t *current_proposal; + bool first = TRUE; + + /* check proposal numbering */ + iterator = this->proposals->create_iterator(this->proposals,TRUE); + + while(iterator->iterate(iterator, (void**)¤t_proposal)) + { + current_number = current_proposal->get_proposal_number(current_proposal); + if (current_number < expected_number) + { + if (current_number != (expected_number + 1)) + { + DBG1(DBG_ENC, "proposal number is %d, excepted %d or %d", + current_number, expected_number, expected_number + 1); + status = FAILED; + break; + } + } + else if (current_number < expected_number) + { + /* must not be smaller then proceeding one */ + DBG1(DBG_ENC, "proposal number smaller than that of previous proposal"); + status = FAILED; + break; + } + + status = current_proposal->payload_interface.verify(&(current_proposal->payload_interface)); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "PROPOSAL_SUBSTRUCTURE verification failed"); + break; + } + first = FALSE; + expected_number = current_number; + } + + iterator->destroy(iterator); + return status; +} + + +/** + * Implementation of payload_t.destroy and sa_payload_t.destroy. + */ +static status_t destroy(private_sa_payload_t *this) +{ + this->proposals->destroy_offset(this->proposals, + offsetof(proposal_substructure_t, destroy)); + free(this); + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_sa_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = sa_payload_encodings; + *rule_count = sizeof(sa_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_sa_payload_t *this) +{ + return SECURITY_ASSOCIATION; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_sa_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_sa_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute length of the payload. + */ +static void compute_length (private_sa_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_proposal; + size_t length = SA_PAYLOAD_HEADER_LENGTH; + + iterator = this->proposals->create_iterator(this->proposals,TRUE); + while (iterator->iterate(iterator, (void **)¤t_proposal)) + { + length += current_proposal->get_length(current_proposal); + } + iterator->destroy(iterator); + + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_sa_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of sa_payload_t.create_proposal_substructure_iterator. + */ +static iterator_t *create_proposal_substructure_iterator (private_sa_payload_t *this,bool forward) +{ + return this->proposals->create_iterator(this->proposals,forward); +} + +/** + * Implementation of sa_payload_t.add_proposal_substructure. + */ +static void add_proposal_substructure(private_sa_payload_t *this,proposal_substructure_t *proposal) +{ + status_t status; + u_int proposal_count = this->proposals->get_count(this->proposals); + + if (proposal_count > 0) + { + proposal_substructure_t *last_proposal; + status = this->proposals->get_last(this->proposals,(void **) &last_proposal); + /* last transform is now not anymore last one */ + last_proposal->set_is_last_proposal(last_proposal, FALSE); + } + proposal->set_is_last_proposal(proposal, TRUE); + proposal->set_proposal_number(proposal, proposal_count + 1); + this->proposals->insert_last(this->proposals,(void *) proposal); + compute_length(this); +} + +/** + * Implementation of sa_payload_t.add_proposal. + */ +static void add_proposal(private_sa_payload_t *this, proposal_t *proposal) +{ + proposal_substructure_t *substructure; + + substructure = proposal_substructure_create_from_proposal(proposal); + add_proposal_substructure(this, substructure); +} + +/** + * Implementation of sa_payload_t.get_proposals. + */ +static linked_list_t *get_proposals(private_sa_payload_t *this) +{ + int struct_number = 0; + int ignore_struct_number = 0; + iterator_t *iterator; + proposal_substructure_t *proposal_struct; + linked_list_t *proposal_list; + + /* this list will hold our proposals */ + proposal_list = linked_list_create(); + + /* we do not support proposals split up to two proposal substructures, as + * AH+ESP bundles are not supported in RFC4301 anymore. + * To handle such structures safely, we just skip proposals with multiple + * protocols. + */ + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->iterate(iterator, (void **)&proposal_struct)) + { + proposal_t *proposal; + + /* check if a proposal has a single protocol */ + if (proposal_struct->get_proposal_number(proposal_struct) == struct_number) + { + if (ignore_struct_number < struct_number) + { + /* remova an already added, if first of series */ + proposal_list->remove_last(proposal_list, (void**)&proposal); + proposal->destroy(proposal); + ignore_struct_number = struct_number; + } + continue; + } + struct_number++; + proposal = proposal_struct->get_proposal(proposal_struct); + if (proposal) + { + proposal_list->insert_last(proposal_list, proposal); + } + } + iterator->destroy(iterator); + return proposal_list; +} + +/* + * Described in header. + */ +sa_payload_t *sa_payload_create() +{ + private_sa_payload_t *this = malloc_thing(private_sa_payload_t); + + /* public interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_proposal_substructure_iterator = (iterator_t* (*) (sa_payload_t *,bool)) create_proposal_substructure_iterator; + this->public.add_proposal_substructure = (void (*) (sa_payload_t *,proposal_substructure_t *)) add_proposal_substructure; + this->public.add_proposal = (void (*) (sa_payload_t*,proposal_t*))add_proposal; + this->public.get_proposals = (linked_list_t* (*) (sa_payload_t *)) get_proposals; + this->public.destroy = (void (*) (sa_payload_t *)) destroy; + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = SA_PAYLOAD_HEADER_LENGTH; + this->proposals = linked_list_create(); + return &this->public; +} + +/* + * Described in header. + */ +sa_payload_t *sa_payload_create_from_proposal_list(linked_list_t *proposals) +{ + iterator_t *iterator; + proposal_t *proposal; + sa_payload_t *sa_payload = sa_payload_create(); + + /* add every payload from the list */ + iterator = proposals->create_iterator(proposals, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + add_proposal((private_sa_payload_t*)sa_payload, proposal); + } + iterator->destroy(iterator); + + return sa_payload; +} + +/* + * Described in header. + */ +sa_payload_t *sa_payload_create_from_proposal(proposal_t *proposal) +{ + sa_payload_t *sa_payload = sa_payload_create(); + + add_proposal((private_sa_payload_t*)sa_payload, proposal); + + return sa_payload; +} diff --git a/src/charon/encoding/payloads/sa_payload.h b/src/charon/encoding/payloads/sa_payload.h new file mode 100644 index 000000000..67d687857 --- /dev/null +++ b/src/charon/encoding/payloads/sa_payload.h @@ -0,0 +1,141 @@ +/** + * @file sa_payload.h + * + * @brief Interface of sa_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SA_PAYLOAD_H_ +#define SA_PAYLOAD_H_ + +typedef struct sa_payload_t sa_payload_t; + +#include +#include +#include +#include + +/** + * SA_PAYLOAD length in bytes without any proposal substructure. + * + * @ingroup payloads + */ +#define SA_PAYLOAD_HEADER_LENGTH 4 + +/** + * @brief Class representing an IKEv2-SA Payload. + * + * The SA Payload format is described in RFC section 3.3. + * + * @b Constructors: + * - sa_payload_create() + * - sa_payload_create_from_ike_proposals() + * - sa_payload_create_from_proposal() + * + * @todo Add support of algorithms without specified keylength in get_proposals and get_ike_proposals. + * + * @ingroup payloads + */ +struct sa_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored proposal_substructure_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning When deleting an proposal using this iterator, + * the length of this transform substructure has to be refreshed + * by calling get_length()! + * + * @param this calling sa_payload_t object + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_proposal_substructure_iterator) (sa_payload_t *this, bool forward); + + /** + * @brief Adds a proposal_substructure_t object to this object. + * + * @warning The added proposal_substructure_t object is + * getting destroyed in destroy function of sa_payload_t. + * + * @param this calling sa_payload_t object + * @param proposal proposal_substructure_t object to add + */ + void (*add_proposal_substructure) (sa_payload_t *this,proposal_substructure_t *proposal); + + /** + * @brief Gets the proposals in this payload as a list. + * + * @return a list containing proposal_t s + */ + linked_list_t *(*get_proposals) (sa_payload_t *this); + + /** + * @brief Add a child proposal (AH/ESP) to the payload. + * + * @param proposal child proposal to add to the payload + */ + void (*add_proposal) (sa_payload_t *this, proposal_t *proposal); + + /** + * @brief Destroys an sa_payload_t object. + * + * @param this sa_payload_t object to destroy + */ + void (*destroy) (sa_payload_t *this); +}; + +/** + * @brief Creates an empty sa_payload_t object + * + * @return created sa_payload_t object + * + * @ingroup payloads + */ +sa_payload_t *sa_payload_create(void); + +/** + * @brief Creates a sa_payload_t object from a list of proposals. + * + * @param proposals list of proposals to build the payload from + * @return sa_payload_t object + * + * @ingroup payloads + */ +sa_payload_t *sa_payload_create_from_proposal_list(linked_list_t *proposals); + +/** + * @brief Creates a sa_payload_t object from a single proposal. + * + * This is only for convenience. Use sa_payload_create_from_proposal_list + * if you want to add more than one proposal. + * + * @param proposal proposal from which the payload should be built. + * @return sa_payload_t object + * + * @ingroup payloads + */ +sa_payload_t *sa_payload_create_from_proposal(proposal_t *proposal); + +#endif /*SA_PAYLOAD_H_*/ diff --git a/src/charon/encoding/payloads/traffic_selector_substructure.c b/src/charon/encoding/payloads/traffic_selector_substructure.c new file mode 100644 index 000000000..573139bf3 --- /dev/null +++ b/src/charon/encoding/payloads/traffic_selector_substructure.c @@ -0,0 +1,283 @@ +/** + * @file traffic_selector_substructure.c + * + * @brief Interface of traffic_selector_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "traffic_selector_substructure.h" + +#include +#include + + +typedef struct private_traffic_selector_substructure_t private_traffic_selector_substructure_t; + +/** + * Private data of an traffic_selector_substructure_t object. + * + */ +struct private_traffic_selector_substructure_t { + /** + * Public traffic_selector_substructure_t interface. + */ + traffic_selector_substructure_t public; + + /** + * Type of traffic selector. + */ + u_int8_t ts_type; + + /** + * IP Protocol ID. + */ + u_int8_t ip_protocol_id; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Start port number. + */ + u_int16_t start_port; + + /** + * End port number. + */ + u_int16_t end_port; + + /** + * Starting address. + */ + chunk_t starting_address; + + /** + * Ending address. + */ + chunk_t ending_address; +}; + +/** + * Encoding rules to parse or generate a TS payload + * + * The defined offsets are the positions in a object of type + * private_traffic_selector_substructure_t. + * + */ +encoding_rule_t traffic_selector_substructure_encodings[] = { + /* 1 Byte next ts type*/ + { TS_TYPE, offsetof(private_traffic_selector_substructure_t, ts_type) }, + /* 1 Byte IP protocol id*/ + { U_INT_8, offsetof(private_traffic_selector_substructure_t, ip_protocol_id) }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_traffic_selector_substructure_t, payload_length) }, + /* 2 Byte start port*/ + { U_INT_16, offsetof(private_traffic_selector_substructure_t, start_port) }, + /* 2 Byte end port*/ + { U_INT_16, offsetof(private_traffic_selector_substructure_t, end_port) }, + /* starting address is either 4 or 16 byte */ + { ADDRESS, offsetof(private_traffic_selector_substructure_t, starting_address) }, + /* ending address is either 4 or 16 byte */ + { ADDRESS, offsetof(private_traffic_selector_substructure_t, ending_address) } + +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! TS Type !IP Protocol ID*| Selector Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Start Port* | End Port* | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Starting Address* ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Ending Address* ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_traffic_selector_substructure_t *this) +{ + if (this->start_port > this->end_port) + { + return FAILED; + } + switch (this->ts_type) + { + case TS_IPV4_ADDR_RANGE: + { + if ((this->starting_address.len != 4) || + (this->ending_address.len != 4)) + { + /* ipv4 address must be 4 bytes long */ + return FAILED; + } + break; + } + case TS_IPV6_ADDR_RANGE: + { + if ((this->starting_address.len != 16) || + (this->ending_address.len != 16)) + { + /* ipv6 address must be 16 bytes long */ + return FAILED; + } + break; + } + default: + { + /* not supported ts type */ + return FAILED; + } + } + + return SUCCESS; +} + +/** + * Implementation of traffic_selector_substructure_t.get_encoding_rules. + */ +static void get_encoding_rules(private_traffic_selector_substructure_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = traffic_selector_substructure_encodings; + *rule_count = sizeof(traffic_selector_substructure_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_traffic_selector_substructure_t *this) +{ + return TRAFFIC_SELECTOR_SUBSTRUCTURE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_traffic_selector_substructure_t *this) +{ + return 0; +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_traffic_selector_substructure_t *this,payload_type_t type) +{ + +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_traffic_selector_substructure_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of traffic_selector_substructure_t.get_traffic_selector. + */ +static traffic_selector_t *get_traffic_selector(private_traffic_selector_substructure_t *this) +{ + traffic_selector_t *ts; + ts = traffic_selector_create_from_bytes(this->ip_protocol_id, this->ts_type, + this->starting_address, this->start_port, + this->ending_address, this->end_port); + return ts; +} + +/** + * recompute length field of the payload + */ +void compute_length(private_traffic_selector_substructure_t *this) +{ + this->payload_length = TRAFFIC_SELECTOR_HEADER_LENGTH + + this->ending_address.len + this->starting_address.len; +} + +/** + * Implementation of payload_t.destroy and traffic_selector_substructure_t.destroy. + */ +static void destroy(private_traffic_selector_substructure_t *this) +{ + free(this->starting_address.ptr); + free(this->ending_address.ptr); + free(this); +} + +/* + * Described in header + */ +traffic_selector_substructure_t *traffic_selector_substructure_create() +{ + private_traffic_selector_substructure_t *this = malloc_thing(private_traffic_selector_substructure_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.get_traffic_selector = (traffic_selector_t* (*)(traffic_selector_substructure_t*))get_traffic_selector; + this->public.destroy = (void (*) (traffic_selector_substructure_t *)) destroy; + + /* private variables */ + this->payload_length = TRAFFIC_SELECTOR_HEADER_LENGTH; + this->start_port = 0; + this->end_port = 0; + this->starting_address = chunk_empty; + this->ending_address = chunk_empty; + this->ip_protocol_id = 0; + /* must be set to be valid */ + this->ts_type = TS_IPV4_ADDR_RANGE; + + return (&(this->public)); +} + +/* + * Described in header + */ +traffic_selector_substructure_t *traffic_selector_substructure_create_from_traffic_selector(traffic_selector_t *traffic_selector) +{ + private_traffic_selector_substructure_t *this = (private_traffic_selector_substructure_t*)traffic_selector_substructure_create(); + this->ts_type = traffic_selector->get_type(traffic_selector); + this->ip_protocol_id = traffic_selector->get_protocol(traffic_selector); + this->start_port = traffic_selector->get_from_port(traffic_selector); + this->end_port = traffic_selector->get_to_port(traffic_selector); + this->starting_address = traffic_selector->get_from_address(traffic_selector); + this->ending_address = traffic_selector->get_to_address(traffic_selector); + + compute_length(this); + + return &(this->public); +} diff --git a/src/charon/encoding/payloads/traffic_selector_substructure.h b/src/charon/encoding/payloads/traffic_selector_substructure.h new file mode 100644 index 000000000..14efccc89 --- /dev/null +++ b/src/charon/encoding/payloads/traffic_selector_substructure.h @@ -0,0 +1,172 @@ +/** + * @file traffic_selector_substructure.h + * + * @brief Interface of traffic_selector_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ +#define TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ + +typedef struct traffic_selector_substructure_t traffic_selector_substructure_t; + +#include +#include +#include +#include + +/** + * Length of a TRAFFIC SELECTOR SUBSTRUCTURE without start and end address. + * + * @ingroup payloads + */ +#define TRAFFIC_SELECTOR_HEADER_LENGTH 8 + +/** + * @brief Class representing an IKEv2 TRAFFIC SELECTOR. + * + * The TRAFFIC SELECTOR format is described in RFC section 3.13.1. + * + * @b Constructors: + * - traffic_selector_substructure_create() + * - traffic_selector_substructure_create_from_traffic_selector() + * + * @ingroup payloads + */ +struct traffic_selector_substructure_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the type of Traffic selector. + * + * @param this calling traffic_selector_substructure_t object + * @return type of traffic selector + * + */ + ts_type_t (*get_ts_type) (traffic_selector_substructure_t *this); + + /** + * @brief Set the type of Traffic selector. + * + * @param this calling traffic_selector_substructure_t object + * @param ts_type type of traffic selector + */ + void (*set_ts_type) (traffic_selector_substructure_t *this,ts_type_t ts_type); + + /** + * @brief Get the IP protocol ID of Traffic selector. + * + * @param this calling traffic_selector_substructure_t object + * @return type of traffic selector + * + */ + u_int8_t (*get_protocol_id) (traffic_selector_substructure_t *this); + + /** + * @brief Set the IP protocol ID of Traffic selector + * + * @param this calling traffic_selector_substructure_t object + * @param protocol_id protocol ID of traffic selector + */ + void (*set_protocol_id) (traffic_selector_substructure_t *this,u_int8_t protocol_id); + + /** + * @brief Get the start port and address as host_t object. + * + * Returned host_t object has to get destroyed by the caller. + * + * @param this calling traffic_selector_substructure_t object + * @return start host as host_t object + * + */ + host_t *(*get_start_host) (traffic_selector_substructure_t *this); + + /** + * @brief Set the start port and address as host_t object. + * + * @param this calling traffic_selector_substructure_t object + * @param start_host start host as host_t object + */ + void (*set_start_host) (traffic_selector_substructure_t *this,host_t *start_host); + + /** + * @brief Get the end port and address as host_t object. + * + * Returned host_t object has to get destroyed by the caller. + * + * @param this calling traffic_selector_substructure_t object + * @return end host as host_t object + * + */ + host_t *(*get_end_host) (traffic_selector_substructure_t *this); + + /** + * @brief Set the end port and address as host_t object. + * + * @param this calling traffic_selector_substructure_t object + * @param end_host end host as host_t object + */ + void (*set_end_host) (traffic_selector_substructure_t *this,host_t *end_host); + + /** + * @brief Get a traffic_selector_t from this substructure. + * + * @warning traffic_selector_t must be destroyed after usage. + * + * @param this calling traffic_selector_substructure_t object + * @return contained traffic_selector_t + */ + traffic_selector_t *(*get_traffic_selector) (traffic_selector_substructure_t *this); + + /** + * @brief Destroys an traffic_selector_substructure_t object. + * + * @param this traffic_selector_substructure_t object to destroy + */ + void (*destroy) (traffic_selector_substructure_t *this); +}; + +/** + * @brief Creates an empty traffic_selector_substructure_t object. + * + * TS type is set to default TS_IPV4_ADDR_RANGE! + * + * @return traffic_selector_substructure_t object + * + * @ingroup payloads + */ +traffic_selector_substructure_t *traffic_selector_substructure_create(void); + +/** + * @brief Creates an initialized traffif selector substructure using + * the values from a traffic_selector_t. + * + * @param traffic_selector traffic_selector_t to use for initialization + * @return traffic_selector_substructure_t object + * + * @ingroup payloads + */ +traffic_selector_substructure_t *traffic_selector_substructure_create_from_traffic_selector(traffic_selector_t *traffic_selector); + + +#endif /* /TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ */ diff --git a/src/charon/encoding/payloads/transform_attribute.c b/src/charon/encoding/payloads/transform_attribute.c new file mode 100644 index 000000000..066885c55 --- /dev/null +++ b/src/charon/encoding/payloads/transform_attribute.c @@ -0,0 +1,332 @@ +/** + * @file transform_attribute.c + * + * @brief Implementation of transform_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "transform_attribute.h" + +#include +#include + +typedef struct private_transform_attribute_t private_transform_attribute_t; + +/** + * Private data of an transform_attribute_t object. + * + */ +struct private_transform_attribute_t { + /** + * Public transform_attribute_t interface. + */ + transform_attribute_t public; + + /** + * Attribute Format Flag. + * + * - TRUE means value is stored in attribute_length_or_value + * - FALSE means value is stored in attribute_value + */ + bool attribute_format; + + /** + * Type of the attribute. + */ + u_int16_t attribute_type; + + /** + * Attribute Length if attribute_format is 0, attribute Value otherwise. + */ + u_int16_t attribute_length_or_value; + + /** + * Attribute value as chunk if attribute_format is 0 (FALSE). + */ + chunk_t attribute_value; +}; + + +ENUM_BEGIN(transform_attribute_type_name, ATTRIBUTE_UNDEFINED, ATTRIBUTE_UNDEFINED, + "ATTRIBUTE_UNDEFINED"); +ENUM_NEXT(transform_attribute_type_name, KEY_LENGTH, KEY_LENGTH, ATTRIBUTE_UNDEFINED, + "KEY_LENGTH"); +ENUM_END(transform_attribute_type_name, KEY_LENGTH); + +/** + * Encoding rules to parse or generate a Transform attribute. + * + * The defined offsets are the positions in a object of type + * private_transform_attribute_t. + * + */ +encoding_rule_t transform_attribute_encodings[] = { + /* Flag defining the format of this payload */ + { ATTRIBUTE_FORMAT, offsetof(private_transform_attribute_t, attribute_format) }, + /* type of the attribute as 15 bit unsigned integer */ + { ATTRIBUTE_TYPE, offsetof(private_transform_attribute_t, attribute_type) }, + /* Length or value, depending on the attribute format flag */ + { ATTRIBUTE_LENGTH_OR_VALUE, offsetof(private_transform_attribute_t, attribute_length_or_value) }, + /* Value of attribute if attribute format flag is zero */ + { ATTRIBUTE_VALUE, offsetof(private_transform_attribute_t, attribute_value) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !A! Attribute Type ! AF=0 Attribute Length ! + !F! ! AF=1 Attribute Value ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! AF=0 Attribute Value ! + ! AF=1 Not Transmitted ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_transform_attribute_t *this) +{ + if (this->attribute_type != KEY_LENGTH) + { + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_transform_attribute_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = transform_attribute_encodings; + *rule_count = sizeof(transform_attribute_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_transform_attribute_t *this) +{ + return TRANSFORM_ATTRIBUTE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_transform_attribute_t *this) +{ + return (NO_PAYLOAD); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_transform_attribute_t *this,payload_type_t type) +{ +} + +/** + * Implementation of transform_attribute_t.get_length. + */ +static size_t get_length(private_transform_attribute_t *this) +{ + if (this->attribute_format == TRUE) + { + /*Attribute size is only 4 byte */ + return 4; + } + return (this->attribute_length_or_value + 4); +} + +/** + * Implementation of transform_attribute_t.set_value_chunk. + */ +static void set_value_chunk(private_transform_attribute_t *this, chunk_t value) +{ + if (this->attribute_value.ptr != NULL) + { + /* free existing value */ + free(this->attribute_value.ptr); + this->attribute_value.ptr = NULL; + this->attribute_value.len = 0; + + } + + if (value.len > 2) + { + this->attribute_value.ptr = clalloc(value.ptr,value.len); + this->attribute_value.len = value.len; + this->attribute_length_or_value = value.len; + /* attribute has not a fixed length */ + this->attribute_format = FALSE; + } + else + { + memcpy(&(this->attribute_length_or_value),value.ptr,value.len); + } +} + +/** + * Implementation of transform_attribute_t.set_value. + */ +static void set_value(private_transform_attribute_t *this, u_int16_t value) +{ + if (this->attribute_value.ptr != NULL) + { + /* free existing value */ + free(this->attribute_value.ptr); + this->attribute_value.ptr = NULL; + this->attribute_value.len = 0; + + } + this->attribute_length_or_value = value; +} + +/** + * Implementation of transform_attribute_t.get_value_chunk. + */ +static chunk_t get_value_chunk (private_transform_attribute_t *this) +{ + chunk_t value; + + if (this->attribute_format == FALSE) + { + value.ptr = this->attribute_value.ptr; + value.len = this->attribute_value.len; + } + else + { + value.ptr = (void *) &(this->attribute_length_or_value); + value.len = 2; + } + + return value; +} + +/** + * Implementation of transform_attribute_t.get_value. + */ +static u_int16_t get_value (private_transform_attribute_t *this) +{ + return this->attribute_length_or_value; +} + + +/** + * Implementation of transform_attribute_t.set_attribute_type. + */ +static void set_attribute_type (private_transform_attribute_t *this, u_int16_t type) +{ + this->attribute_type = type & 0x7FFF; +} + +/** + * Implementation of transform_attribute_t.get_attribute_type. + */ +static u_int16_t get_attribute_type (private_transform_attribute_t *this) +{ + return this->attribute_type; +} + +/** + * Implementation of transform_attribute_t.clone. + */ +static transform_attribute_t * clone(private_transform_attribute_t *this) +{ + private_transform_attribute_t *new_clone; + + new_clone = (private_transform_attribute_t *) transform_attribute_create(); + + new_clone->attribute_format = this->attribute_format; + new_clone->attribute_type = this->attribute_type; + new_clone->attribute_length_or_value = this->attribute_length_or_value; + + if (!new_clone->attribute_format) + { + new_clone->attribute_value.ptr = clalloc(this->attribute_value.ptr,this->attribute_value.len); + new_clone->attribute_value.len = this->attribute_value.len; + } + + return (transform_attribute_t *) new_clone; +} + +/** + * Implementation of transform_attribute_t.destroy and payload_t.destroy. + */ +static void destroy(private_transform_attribute_t *this) +{ + if (this->attribute_value.ptr != NULL) + { + free(this->attribute_value.ptr); + } + free(this); +} + +/* + * Described in header. + */ +transform_attribute_t *transform_attribute_create() +{ + private_transform_attribute_t *this = malloc_thing(private_transform_attribute_t); + + /* payload interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.set_value_chunk = (void (*) (transform_attribute_t *,chunk_t)) set_value_chunk; + this->public.set_value = (void (*) (transform_attribute_t *,u_int16_t)) set_value; + this->public.get_value_chunk = (chunk_t (*) (transform_attribute_t *)) get_value_chunk; + this->public.get_value = (u_int16_t (*) (transform_attribute_t *)) get_value; + this->public.set_attribute_type = (void (*) (transform_attribute_t *,u_int16_t type)) set_attribute_type; + this->public.get_attribute_type = (u_int16_t (*) (transform_attribute_t *)) get_attribute_type; + this->public.clone = (transform_attribute_t * (*) (transform_attribute_t *)) clone; + this->public.destroy = (void (*) (transform_attribute_t *)) destroy; + + /* set default values of the fields */ + this->attribute_format = TRUE; + this->attribute_type = 0; + this->attribute_length_or_value = 0; + this->attribute_value.ptr = NULL; + this->attribute_value.len = 0; + + return (&(this->public)); +} + +/* + * Described in header. + */ +transform_attribute_t *transform_attribute_create_key_length(u_int16_t key_length) +{ + transform_attribute_t *attribute = transform_attribute_create(); + attribute->set_attribute_type(attribute,KEY_LENGTH); + attribute->set_value(attribute,key_length); + return attribute; +} diff --git a/src/charon/encoding/payloads/transform_attribute.h b/src/charon/encoding/payloads/transform_attribute.h new file mode 100644 index 000000000..30583b23f --- /dev/null +++ b/src/charon/encoding/payloads/transform_attribute.h @@ -0,0 +1,154 @@ +/** + * @file transform_attribute.h + * + * @brief Interface of transform_attribute_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef TRANSFORM_ATTRIBUTE_H_ +#define TRANSFORM_ATTRIBUTE_H_ + +typedef enum transform_attribute_type_t transform_attribute_type_t; +typedef struct transform_attribute_t transform_attribute_t; + +#include +#include + + +/** + * Type of the attribute, as in IKEv2 RFC 3.3.5. + * + * @ingroup payloads + */ +enum transform_attribute_type_t { + ATTRIBUTE_UNDEFINED = 16384, + KEY_LENGTH = 14 +}; + +/** + * enum name for transform_attribute_type_t. + * + * @ingroup payloads + */ +extern enum_name_t *transform_attribute_type_names; + +/** + * @brief Class representing an IKEv2- TRANSFORM Attribute. + * + * The TRANSFORM ATTRIBUTE format is described in RFC section 3.3.5. + * + * @ingroup payloads + */ +struct transform_attribute_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Returns the currently set value of the attribute. + * + * @warning Returned data are not copied. + * + * @param this calling transform_attribute_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_value_chunk) (transform_attribute_t *this); + + /** + * @brief Returns the currently set value of the attribute. + * + * @warning Returned data are not copied. + * + * @param this calling transform_attribute_t object + * @return value + */ + u_int16_t (*get_value) (transform_attribute_t *this); + + /** + * @brief Sets the value of the attribute. + * + * @warning Value is getting copied. + * + * @param this calling transform_attribute_t object + * @param value chunk_t pointing to the value to set + */ + void (*set_value_chunk) (transform_attribute_t *this, chunk_t value); + + /** + * @brief Sets the value of the attribute. + * + * @param this calling transform_attribute_t object + * @param value value to set + */ + void (*set_value) (transform_attribute_t *this, u_int16_t value); + + /** + * @brief Sets the type of the attribute. + * + * @param this calling transform_attribute_t object + * @param type type to set (most significant bit is set to zero) + */ + void (*set_attribute_type) (transform_attribute_t *this, u_int16_t type); + + /** + * @brief get the type of the attribute. + * + * @param this calling transform_attribute_t object + * @return type of the value + */ + u_int16_t (*get_attribute_type) (transform_attribute_t *this); + + /** + * @brief Clones an transform_attribute_t object. + * + * @param this transform_attribute_t object to clone + * @return cloned transform_attribute_t object + */ + transform_attribute_t * (*clone) (transform_attribute_t *this); + + /** + * @brief Destroys an transform_attribute_t object. + * + * @param this transform_attribute_t object to destroy + */ + void (*destroy) (transform_attribute_t *this); +}; + +/** + * @brief Creates an empty transform_attribute_t object. + * + * @return transform_attribute_t object + * + * @ingroup payloads + */ +transform_attribute_t *transform_attribute_create(void); + +/** + * @brief Creates an transform_attribute_t of type KEY_LENGTH. + * + * @param key_length key length in bytes + * @return transform_attribute_t object + * + * @ingroup payloads + */ +transform_attribute_t *transform_attribute_create_key_length(u_int16_t key_length); + + +#endif /*TRANSFORM_ATTRIBUTE_H_*/ diff --git a/src/charon/encoding/payloads/transform_substructure.c b/src/charon/encoding/payloads/transform_substructure.c new file mode 100644 index 000000000..d64d6c754 --- /dev/null +++ b/src/charon/encoding/payloads/transform_substructure.c @@ -0,0 +1,409 @@ +/** + * @file transform_substructure.h + * + * @brief Implementation of transform_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "transform_substructure.h" + +#include +#include +#include +#include +#include + + +typedef struct private_transform_substructure_t private_transform_substructure_t; + +/** + * Private data of an transform_substructure_t object. + * + */ +struct private_transform_substructure_t { + /** + * Public transform_substructure_t interface. + */ + transform_substructure_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + + /** + * Length of this payload. + */ + u_int16_t transform_length; + + + /** + * Type of the transform. + */ + u_int8_t transform_type; + + /** + * Transform ID. + */ + u_int16_t transform_id; + + /** + * Transforms Attributes are stored in a linked_list_t. + */ + linked_list_t *attributes; +}; + + +/** + * Encoding rules to parse or generate a Transform substructure. + * + * The defined offsets are the positions in a object of type + * private_transform_substructure_t. + * + */ +encoding_rule_t transform_substructure_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_transform_substructure_t, next_payload) }, + /* Reserved Byte is skipped */ + { RESERVED_BYTE, 0 }, + /* Length of the whole transform substructure*/ + { PAYLOAD_LENGTH, offsetof(private_transform_substructure_t, transform_length) }, + /* transform type is a number of 8 bit */ + { U_INT_8, offsetof(private_transform_substructure_t, transform_type) }, + /* Reserved Byte is skipped */ + { RESERVED_BYTE, 0 }, + /* tranform ID is a number of 8 bit */ + { U_INT_16, offsetof(private_transform_substructure_t, transform_id) }, + /* Attributes are stored in a transform attribute, + offset points to a linked_list_t pointer */ + { TRANSFORM_ATTRIBUTES, offsetof(private_transform_substructure_t, attributes) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! 0 (last) or 3 ! RESERVED ! Transform Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + !Transform Type ! RESERVED ! Transform ID ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Transform Attributes ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_transform_substructure_t *this) +{ + status_t status = SUCCESS; + iterator_t *iterator; + payload_t *current_attributes; + + if ((this->next_payload != NO_PAYLOAD) && (this->next_payload != 3)) + { + /* must be 0 or 3 */ + DBG1(DBG_ENC, "inconsistent next payload"); + return FAILED; + } + + switch (this->transform_type) + { + case ENCRYPTION_ALGORITHM: + case PSEUDO_RANDOM_FUNCTION: + case INTEGRITY_ALGORITHM: + case DIFFIE_HELLMAN_GROUP: + case EXTENDED_SEQUENCE_NUMBERS: + /* we don't check transform ID, we want to reply + * cleanly with NO_PROPOSAL_CHOSEN or so if we don't support it */ + break; + default: + { + DBG1(DBG_ENC, "invalid transform type: %d", this->transform_type); + return FAILED; + } + } + iterator = this->attributes->create_iterator(this->attributes,TRUE); + + while(iterator->iterate(iterator, (void**)¤t_attributes)) + { + status = current_attributes->verify(current_attributes); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "TRANSFORM_ATTRIBUTE verification failed"); + } + } + iterator->destroy(iterator); + + /* proposal number is checked in SA payload */ + return status; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_transform_substructure_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = transform_substructure_encodings; + *rule_count = sizeof(transform_substructure_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_type(private_transform_substructure_t *this) +{ + return TRANSFORM_SUBSTRUCTURE; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_transform_substructure_t *this) +{ + return (this->next_payload); +} + +/** + * recompute the length of the payload. + */ +static void compute_length (private_transform_substructure_t *this) +{ + iterator_t *iterator; + payload_t *current_attribute; + size_t length = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while (iterator->iterate(iterator, (void**)¤t_attribute)) + { + length += current_attribute->get_length(current_attribute); + } + iterator->destroy(iterator); + + this->transform_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_transform_substructure_t *this) +{ + compute_length(this); + return this->transform_length; +} + +/** + * Implementation of transform_substructure_t.create_transform_attribute_iterator. + */ +static iterator_t *create_transform_attribute_iterator (private_transform_substructure_t *this,bool forward) +{ + return this->attributes->create_iterator(this->attributes,forward); +} + +/** + * Implementation of transform_substructure_t.add_transform_attribute. + */ +static void add_transform_attribute (private_transform_substructure_t *this,transform_attribute_t *attribute) +{ + this->attributes->insert_last(this->attributes,(void *) attribute); + compute_length(this); +} + +/** + * Implementation of transform_substructure_t.set_is_last_transform. + */ +static void set_is_last_transform (private_transform_substructure_t *this, bool is_last) +{ + this->next_payload = (is_last) ? 0: TRANSFORM_TYPE_VALUE; +} + +/** + * Implementation of transform_substructure_t.get_is_last_transform. + */ +static bool get_is_last_transform (private_transform_substructure_t *this) +{ + return ((this->next_payload == TRANSFORM_TYPE_VALUE) ? FALSE : TRUE); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_transform_substructure_t *this,payload_type_t type) +{ +} + +/** + * Implementation of transform_substructure_t.set_transform_type. + */ +static void set_transform_type (private_transform_substructure_t *this,u_int8_t type) +{ + this->transform_type = type; +} + +/** + * Implementation of transform_substructure_t.get_transform_type. + */ +static u_int8_t get_transform_type (private_transform_substructure_t *this) +{ + return this->transform_type; +} + +/** + * Implementation of transform_substructure_t.set_transform_id. + */ +static void set_transform_id (private_transform_substructure_t *this,u_int16_t id) +{ + this->transform_id = id; +} + +/** + * Implementation of transform_substructure_t.get_transform_id. + */ +static u_int16_t get_transform_id (private_transform_substructure_t *this) +{ + return this->transform_id; +} + +/** + * Implementation of transform_substructure_t.clone. + */ +static transform_substructure_t *clone_(private_transform_substructure_t *this) +{ + private_transform_substructure_t *clone; + iterator_t *attributes; + transform_attribute_t *current_attribute; + + clone = (private_transform_substructure_t *) transform_substructure_create(); + clone->next_payload = this->next_payload; + clone->transform_type = this->transform_type; + clone->transform_id = this->transform_id; + + attributes = this->attributes->create_iterator(this->attributes, FALSE); + while (attributes->iterate(attributes, (void**)¤t_attribute)) + { + current_attribute = current_attribute->clone(current_attribute); + clone->public.add_transform_attribute(&clone->public, current_attribute); + } + attributes->destroy(attributes); + + return &clone->public; +} + + +/** + * Implementation of transform_substructure_t.get_key_length. + */ +static status_t get_key_length(private_transform_substructure_t *this, u_int16_t *key_length) +{ + iterator_t *attributes; + transform_attribute_t *current_attribute; + + attributes = this->attributes->create_iterator(this->attributes, TRUE); + while (attributes->iterate(attributes, (void**)¤t_attribute)) + { + if (current_attribute->get_attribute_type(current_attribute) == KEY_LENGTH) + { + *key_length = current_attribute->get_value(current_attribute); + attributes->destroy(attributes); + return SUCCESS; + } + } + attributes->destroy(attributes); + return FAILED; +} + + +/** + * Implementation of transform_substructure_t.destroy and payload_t.destroy. + */ +static void destroy(private_transform_substructure_t *this) +{ + this->attributes->destroy_offset(this->attributes, + offsetof(transform_attribute_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +transform_substructure_t *transform_substructure_create() +{ + private_transform_substructure_t *this = malloc_thing(private_transform_substructure_t); + + /* payload interface */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.create_transform_attribute_iterator = (iterator_t * (*) (transform_substructure_t *,bool)) create_transform_attribute_iterator; + this->public.add_transform_attribute = (void (*) (transform_substructure_t *,transform_attribute_t *)) add_transform_attribute; + this->public.set_is_last_transform = (void (*) (transform_substructure_t *,bool)) set_is_last_transform; + this->public.get_is_last_transform = (bool (*) (transform_substructure_t *)) get_is_last_transform; + this->public.set_transform_type = (void (*) (transform_substructure_t *,u_int8_t)) set_transform_type; + this->public.get_transform_type = (u_int8_t (*) (transform_substructure_t *)) get_transform_type; + this->public.set_transform_id = (void (*) (transform_substructure_t *,u_int16_t)) set_transform_id; + this->public.get_transform_id = (u_int16_t (*) (transform_substructure_t *)) get_transform_id; + this->public.get_key_length = (status_t (*) (transform_substructure_t *,u_int16_t *)) get_key_length; + this->public.clone = (transform_substructure_t* (*) (transform_substructure_t *)) clone_; + this->public.destroy = (void (*) (transform_substructure_t *)) destroy; + + /* set default values of the fields */ + this->next_payload = NO_PAYLOAD; + this->transform_length = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + this->transform_id = 0; + this->transform_type = 0; + this->attributes = linked_list_create(); + + return (&(this->public)); +} + +/* + * Described in header + */ +transform_substructure_t *transform_substructure_create_type(transform_type_t transform_type, u_int16_t transform_id, u_int16_t key_length) +{ + transform_substructure_t *transform = transform_substructure_create(); + + transform->set_transform_type(transform,transform_type); + transform->set_transform_id(transform,transform_id); + + /* a keylength attribute is only created for variable length algos */ + if (transform_type == ENCRYPTION_ALGORITHM && + (transform_id == ENCR_AES_CBC || + transform_id == ENCR_IDEA || + transform_id == ENCR_CAST || + transform_id == ENCR_BLOWFISH)) + { + transform_attribute_t *attribute = transform_attribute_create_key_length(key_length); + transform->add_transform_attribute(transform,attribute); + } + + return transform; +} diff --git a/src/charon/encoding/payloads/transform_substructure.h b/src/charon/encoding/payloads/transform_substructure.h new file mode 100644 index 000000000..97f587d5d --- /dev/null +++ b/src/charon/encoding/payloads/transform_substructure.h @@ -0,0 +1,198 @@ +/** + * @file transform_substructure.h + * + * @brief Interface of transform_substructure_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef TRANSFORM_SUBSTRUCTURE_H_ +#define TRANSFORM_SUBSTRUCTURE_H_ + +typedef struct transform_substructure_t transform_substructure_t; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * IKEv1 Value for a transform payload. + * + * @ingroup payloads + */ +#define TRANSFORM_TYPE_VALUE 3 + +/** + * Length of the transform substructure header in bytes. + * + * @ingroup payloads + */ +#define TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH 8 + + +/** + * @brief Class representing an IKEv2- TRANSFORM SUBSTRUCTURE. + * + * The TRANSFORM SUBSTRUCTURE format is described in RFC section 3.3.2. + * + * @ingroup payloads + */ +struct transform_substructure_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Creates an iterator of stored transform_attribute_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning When deleting an transform attribute using this iterator, + * the length of this transform substructure has to be refreshed + * by calling get_length()! + * + * @param this calling transform_substructure_t object + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object. + */ + iterator_t * (*create_transform_attribute_iterator) (transform_substructure_t *this, bool forward); + + /** + * @brief Adds a transform_attribute_t object to this object. + * + * @warning The added proposal_substructure_t object is + * getting destroyed in destroy function of transform_substructure_t. + * + * @param this calling transform_substructure_t object + * @param proposal transform_attribute_t object to add + */ + void (*add_transform_attribute) (transform_substructure_t *this,transform_attribute_t *attribute); + + /** + * @brief Sets the next_payload field of this substructure + * + * If this is the last transform, next payload field is set to 0, + * otherwise to 3 + * + * @param this calling transform_substructure_t object + * @param is_last When TRUE, next payload field is set to 0, otherwise to 3 + */ + void (*set_is_last_transform) (transform_substructure_t *this, bool is_last); + + /** + * @brief Checks if this is the last transform. + * + * @param this calling transform_substructure_t object + * @return TRUE if this is the last Transform, FALSE otherwise + */ + bool (*get_is_last_transform) (transform_substructure_t *this); + + /** + * @brief Sets transform type of the current transform substructure. + * + * @param this calling transform_substructure_t object + * @param type type value to set + */ + void (*set_transform_type) (transform_substructure_t *this,u_int8_t type); + + /** + * @brief get transform type of the current transform. + * + * @param this calling transform_substructure_t object + * @return Transform type of current transform substructure. + */ + u_int8_t (*get_transform_type) (transform_substructure_t *this); + + /** + * @brief Sets transform id of the current transform substructure. + * + * @param this calling transform_substructure_t object + * @param id transform id to set + */ + void (*set_transform_id) (transform_substructure_t *this,u_int16_t id); + + /** + * @brief get transform id of the current transform. + * + * @param this calling transform_substructure_t object + * @return Transform id of current transform substructure. + */ + u_int16_t (*get_transform_id) (transform_substructure_t *this); + + /** + * @brief get transform id of the current transform. + * + * @param this calling transform_substructure_t object + * @param key_length The key length is written to this location + * @return + * - SUCCESS if a key length attribute is contained + * - FAILED if no key length attribute is part of this + * transform or key length uses more then 16 bit! + */ + status_t (*get_key_length) (transform_substructure_t *this,u_int16_t *key_length); + + /** + * @brief Clones an transform_substructure_t object. + * + * @param this transform_substructure_t object to clone + * @return cloned transform_substructure_t object + */ + transform_substructure_t* (*clone) (transform_substructure_t *this); + + /** + * @brief Destroys an transform_substructure_t object. + * + * @param this transform_substructure_t object to destroy + */ + void (*destroy) (transform_substructure_t *this); +}; + +/** + * @brief Creates an empty transform_substructure_t object. + * + * @return created transform_substructure_t object + * + * @ingroup payloads + */ +transform_substructure_t *transform_substructure_create(void); + +/** + * @brief Creates an empty transform_substructure_t object. + * + * The key length is used for the transport types ENCRYPTION_ALGORITHM, + * PSEUDO_RANDOM_FUNCTION, INTEGRITY_ALGORITHM. For all + * other transport types the key_length parameter is not used + * + * @param transform_type type of transform to create + * @param transform_id transform id specifying the specific algorithm of a transform type + * @param key_length Key length for key lenght attribute + * @return transform_substructure_t object + * + * @ingroup payloads + */ +transform_substructure_t *transform_substructure_create_type(transform_type_t transform_type, u_int16_t transform_id, u_int16_t key_length); + +#endif /*TRANSFORM_SUBSTRUCTURE_H_*/ diff --git a/src/charon/encoding/payloads/ts_payload.c b/src/charon/encoding/payloads/ts_payload.c new file mode 100644 index 000000000..ae89919f6 --- /dev/null +++ b/src/charon/encoding/payloads/ts_payload.c @@ -0,0 +1,341 @@ +/** + * @file ts_payload.c + * + * @brief Implementation of ts_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "ts_payload.h" + +#include +#include + +typedef struct private_ts_payload_t private_ts_payload_t; + +/** + * Private data of an ts_payload_t object. + * + */ +struct private_ts_payload_t { + /** + * Public ts_payload_t interface. + */ + ts_payload_t public; + + /** + * TRUE if this TS payload is of type TSi, FALSE for TSr. + */ + bool is_initiator; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Number of traffic selectors + */ + u_int8_t number_of_traffic_selectors; + + /** + * Contains the traffic selectors of type traffic_selector_substructure_t. + */ + linked_list_t *traffic_selectors; +}; + +/** + * Encoding rules to parse or generate a TS payload + * + * The defined offsets are the positions in a object of type + * private_ts_payload_t. + * + */ +encoding_rule_t ts_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_ts_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_ts_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_ts_payload_t, payload_length)}, + /* 1 Byte TS type*/ + { U_INT_8, offsetof(private_ts_payload_t, number_of_traffic_selectors) }, + /* 3 reserved bytes */ + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + { RESERVED_BYTE, 0 }, + /* some ts data bytes, length is defined in PAYLOAD_LENGTH */ + { TRAFFIC_SELECTORS, offsetof(private_ts_payload_t, traffic_selectors) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Number of TSs ! RESERVED ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_ts_payload_t *this) +{ + iterator_t *iterator; + payload_t *current_traffic_selector; + status_t status = SUCCESS; + + if (this->number_of_traffic_selectors != (this->traffic_selectors->get_count(this->traffic_selectors))) + { + /* must be the same */ + return FAILED; + } + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors,TRUE); + while(iterator->iterate(iterator, (void**)¤t_traffic_selector)) + { + status = current_traffic_selector->verify(current_traffic_selector); + if (status != SUCCESS) + { + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implementation of ts_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_ts_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = ts_payload_encodings; + *rule_count = sizeof(ts_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_ts_payload_t *this) +{ + if (this->is_initiator) + { + return TRAFFIC_SELECTOR_INITIATOR; + } + else + { + return TRAFFIC_SELECTOR_RESPONDER; + } +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_ts_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_ts_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * recompute the length of the payload. + */ +static void compute_length (private_ts_payload_t *this) +{ + iterator_t *iterator; + size_t ts_count = 0; + size_t length = TS_PAYLOAD_HEADER_LENGTH; + payload_t *current_traffic_selector; + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors,TRUE); + while (iterator->iterate(iterator, (void**)¤t_traffic_selector)) + { + length += current_traffic_selector->get_length(current_traffic_selector); + ts_count++; + } + iterator->destroy(iterator); + + this->number_of_traffic_selectors= ts_count; + this->payload_length = length; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_ts_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +/** + * Implementation of ts_payload_t.get_initiator. + */ +static bool get_initiator (private_ts_payload_t *this) +{ + return (this->is_initiator); +} + +/** + * Implementation of ts_payload_t.set_initiator. + */ +static void set_initiator (private_ts_payload_t *this,bool is_initiator) +{ + this->is_initiator = is_initiator; +} + +/** + * Implementation of ts_payload_t.add_traffic_selector_substructure. + */ +static void add_traffic_selector_substructure (private_ts_payload_t *this,traffic_selector_substructure_t *traffic_selector) +{ + this->traffic_selectors->insert_last(this->traffic_selectors,traffic_selector); + this->number_of_traffic_selectors = this->traffic_selectors->get_count(this->traffic_selectors); +} + +/** + * Implementation of ts_payload_t.create_traffic_selector_substructure_iterator. + */ +static iterator_t * create_traffic_selector_substructure_iterator (private_ts_payload_t *this, bool forward) +{ + return this->traffic_selectors->create_iterator(this->traffic_selectors,forward); +} + +/** + * Implementation of ts_payload_t.get_traffic_selectors. + */ +static linked_list_t *get_traffic_selectors(private_ts_payload_t *this) +{ + traffic_selector_t *ts; + iterator_t *iterator; + traffic_selector_substructure_t *ts_substructure; + linked_list_t *ts_list = linked_list_create(); + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors, TRUE); + while (iterator->iterate(iterator, (void**)&ts_substructure)) + { + ts = ts_substructure->get_traffic_selector(ts_substructure); + ts_list->insert_last(ts_list, (void*)ts); + } + iterator->destroy(iterator); + + return ts_list; +} + +/** + * Implementation of payload_t.destroy and ts_payload_t.destroy. + */ +static void destroy(private_ts_payload_t *this) +{ + this->traffic_selectors->destroy_offset(this->traffic_selectors, + offsetof(payload_t, destroy)); + free(this); +} + +/* + * Described in header + */ +ts_payload_t *ts_payload_create(bool is_initiator) +{ + private_ts_payload_t *this = malloc_thing(private_ts_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (ts_payload_t *)) destroy; + this->public.get_initiator = (bool (*) (ts_payload_t *)) get_initiator; + this->public.set_initiator = (void (*) (ts_payload_t *,bool)) set_initiator; + this->public.add_traffic_selector_substructure = (void (*) (ts_payload_t *,traffic_selector_substructure_t *)) add_traffic_selector_substructure; + this->public.create_traffic_selector_substructure_iterator = (iterator_t* (*) (ts_payload_t *,bool)) create_traffic_selector_substructure_iterator; + this->public.get_traffic_selectors = (linked_list_t *(*) (ts_payload_t *)) get_traffic_selectors; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =TS_PAYLOAD_HEADER_LENGTH; + this->is_initiator = is_initiator; + this->number_of_traffic_selectors = 0; + this->traffic_selectors = linked_list_create(); + + return &(this->public); +} + +/* + * Described in header + */ +ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator, linked_list_t *traffic_selectors) +{ + iterator_t *iterator; + traffic_selector_t *ts; + traffic_selector_substructure_t *ts_substructure; + private_ts_payload_t *this; + + this = (private_ts_payload_t*)ts_payload_create(is_initiator); + + iterator = traffic_selectors->create_iterator(traffic_selectors, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) + { + ts_substructure = traffic_selector_substructure_create_from_traffic_selector(ts); + this->public.add_traffic_selector_substructure(&(this->public), ts_substructure); + } + iterator->destroy(iterator); + + return &(this->public); +} + diff --git a/src/charon/encoding/payloads/ts_payload.h b/src/charon/encoding/payloads/ts_payload.h new file mode 100644 index 000000000..1addee22c --- /dev/null +++ b/src/charon/encoding/payloads/ts_payload.h @@ -0,0 +1,153 @@ +/** + * @file ts_payload.h + * + * @brief Interface of ts_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef TS_PAYLOAD_H_ +#define TS_PAYLOAD_H_ + +typedef struct ts_payload_t ts_payload_t; + +#include +#include +#include +#include +#include + +/** + * Length of a TS payload without the Traffic selectors. + * + * @ingroup payloads + */ +#define TS_PAYLOAD_HEADER_LENGTH 8 + + +/** + * @brief Class representing an IKEv2 TS payload. + * + * The TS payload format is described in RFC section 3.13. + * + * @b Constructors: + * - ts_payload_create() + * - ts_payload_create_from_traffic_selectors() + * + * @ingroup payloads + */ +struct ts_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the type of TSpayload (TSi or TSr). + * + * @param this calling id_payload_t object + * @return + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + */ + bool (*get_initiator) (ts_payload_t *this); + + /** + * @brief Set the type of TS payload (TSi or TSr). + * + * @param this calling id_payload_t object + * @param is_initiator + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + */ + void (*set_initiator) (ts_payload_t *this,bool is_initiator); + + /** + * @brief Adds a traffic_selector_substructure_t object to this object. + * + * @warning The added traffic_selector_substructure_t object is + * getting destroyed in destroy function of ts_payload_t. + * + * @param this calling ts_payload_t object + * @param traffic_selector traffic_selector_substructure_t object to add + */ + void (*add_traffic_selector_substructure) (ts_payload_t *this,traffic_selector_substructure_t *traffic_selector); + + /** + * @brief Creates an iterator of stored traffic_selector_substructure_t objects. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning When removing an traffic_selector_substructure_t object + * using this iterator, the length of this payload + * has to get refreshed by calling payload_t.get_length! + * + * @param this calling ts_payload_t object + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_traffic_selector_substructure_iterator) (ts_payload_t *this, bool forward); + + /** + * @brief Get a list of nested traffic selectors as traffic_selector_t. + * + * Resulting list and its traffic selectors must be destroyed after usage + * + * @param this calling ts_payload_t object + * @return list of traffic selectors + */ + linked_list_t *(*get_traffic_selectors) (ts_payload_t *this); + + /** + * @brief Destroys an ts_payload_t object. + * + * @param this ts_payload_t object to destroy + */ + void (*destroy) (ts_payload_t *this); +}; + +/** + * @brief Creates an empty ts_payload_t object. + * + * + * @param is_initiator + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + * @return ts_payload_t object + * + * @ingroup payloads + */ +ts_payload_t *ts_payload_create(bool is_initiator); + +/** + * @brief Creates ts_payload with a list of traffic_selector_t + * + * + * @param is_initiator + * - TRUE if this payload is of type TSi + * - FALSE if this payload is of type TSr + * @param traffic_selectors list of traffic selectors to include + * @return ts_payload_t object + * + * @ingroup payloads + */ +ts_payload_t *ts_payload_create_from_traffic_selectors(bool is_initiator, linked_list_t *traffic_selectors); + + +#endif /* TS_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/unknown_payload.c b/src/charon/encoding/payloads/unknown_payload.c new file mode 100644 index 000000000..bbe736085 --- /dev/null +++ b/src/charon/encoding/payloads/unknown_payload.c @@ -0,0 +1,208 @@ +/** + * @file unknown_payload.c + * + * @brief Implementation of unknown_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "unknown_payload.h" + + + +typedef struct private_unknown_payload_t private_unknown_payload_t; + +/** + * Private data of an unknown_payload_t object. + */ +struct private_unknown_payload_t { + + /** + * Public unknown_payload_t interface. + */ + unknown_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * The contained data. + */ + chunk_t data; +}; + +/** + * Encoding rules to parse an payload which is not further specified. + * + * The defined offsets are the positions in a object of type + * private_unknown_payload_t. + * + */ +encoding_rule_t unknown_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_unknown_payload_t, next_payload)}, + /* the critical bit */ + { FLAG, offsetof(private_unknown_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_unknown_payload_t, payload_length)}, + /* some unknown data bytes, length is defined in PAYLOAD_LENGTH */ + { UNKNOWN_DATA, offsetof(private_unknown_payload_t, data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Data of any type ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_unknown_payload_t *this) +{ + /* can't do any checks, so we assume its good */ + return SUCCESS; +} + +/** + * Implementation of payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_unknown_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = unknown_payload_encodings; + *rule_count = sizeof(unknown_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_unknown_payload_t *this) +{ + return UNKNOWN_PAYLOAD; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_unknown_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_unknown_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_unknown_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of unknown_payload_t.get_data. + */ +static bool is_critical(private_unknown_payload_t *this) +{ + return this->critical; +} + +/** + * Implementation of unknown_payload_t.get_data. + */ +static chunk_t get_data (private_unknown_payload_t *this) +{ + return (this->data); +} + +/** + * Implementation of payload_t.destroy and unknown_payload_t.destroy. + */ +static void destroy(private_unknown_payload_t *this) +{ + if (this->data.ptr != NULL) + { + chunk_free(&(this->data)); + } + + free(this); +} + +/* + * Described in header + */ +unknown_payload_t *unknown_payload_create() +{ + private_unknown_payload_t *this = malloc_thing(private_unknown_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (unknown_payload_t *)) destroy; + this->public.is_critical = (bool (*) (unknown_payload_t *)) is_critical; + this->public.get_data = (chunk_t (*) (unknown_payload_t *)) get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = UNKNOWN_PAYLOAD_HEADER_LENGTH; + this->data = chunk_empty; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/unknown_payload.h b/src/charon/encoding/payloads/unknown_payload.h new file mode 100644 index 000000000..8d13a03a3 --- /dev/null +++ b/src/charon/encoding/payloads/unknown_payload.h @@ -0,0 +1,95 @@ +/** + * @file unknown_payload.h + * + * @brief Interface of unknown_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef UNKNOWN_PAYLOAD_H_ +#define UNKNOWN_PAYLOAD_H_ + +typedef struct unknown_payload_t unknown_payload_t; + +#include +#include + +/** + * Header length of the unknown payload. + * + * @ingroup payloads + */ +#define UNKNOWN_PAYLOAD_HEADER_LENGTH 4 + +/** + * @brief Payload which can't be processed further. + * + * When the parser finds an unknown payload, he builds an instance of + * this class. This allows further processing of this payload, such as + * a check for the critical bit in the header. + * + * @b Constructors: + * - unknown_payload_create() + * + * @ingroup payloads + */ +struct unknown_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Get the raw data of this payload, without + * the generic payload header. + * + * Returned data are NOT copied and must not be freed. + * + * @param this calling unknown_payload_t object + * @return data as chunk_t + */ + chunk_t (*get_data) (unknown_payload_t *this); + + /** + * @brief Get the critical flag. + * + * @param this calling unknown_payload_t object + * @return TRUE if payload is critical, FALSE if not + */ + bool (*is_critical) (unknown_payload_t *this); + + /** + * @brief Destroys an unknown_payload_t object. + * + * @param this unknown_payload_t object to destroy + */ + void (*destroy) (unknown_payload_t *this); +}; + +/** + * @brief Creates an empty unknown_payload_t object. + * + * @return unknown_payload_t object + * + * @ingroup payloads + */ +unknown_payload_t *unknown_payload_create(void); + + +#endif /* UNKNOWN_PAYLOAD_H_ */ diff --git a/src/charon/encoding/payloads/vendor_id_payload.c b/src/charon/encoding/payloads/vendor_id_payload.c new file mode 100644 index 000000000..e3a4d2e1f --- /dev/null +++ b/src/charon/encoding/payloads/vendor_id_payload.c @@ -0,0 +1,228 @@ +/** + * @file vendor_id_payload.c + * + * @brief Implementation of vendor_id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "vendor_id_payload.h" + + +typedef struct private_vendor_id_payload_t private_vendor_id_payload_t; + +/** + * Private data of an vendor_id_payload_t object. + * + */ +struct private_vendor_id_payload_t { + /** + * Public vendor_id_payload_t interface. + */ + vendor_id_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Critical flag. + */ + bool critical; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * The contained vendor_id data value. + */ + chunk_t vendor_id_data; +}; + +/** + * Encoding rules to parse or generate a VENDOR ID payload + * + * The defined offsets are the positions in a object of type + * private_vendor_id_payload_t. + * + */ +encoding_rule_t vendor_id_payload_encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_vendor_id_payload_t, next_payload) }, + /* the critical bit */ + { FLAG, offsetof(private_vendor_id_payload_t, critical) }, + /* 7 Bit reserved bits, nowhere stored */ + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + { RESERVED_BIT, 0 }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_vendor_id_payload_t, payload_length)}, + /* some vendor_id data bytes, length is defined in PAYLOAD_LENGTH */ + { VID_DATA, offsetof(private_vendor_id_payload_t, vendor_id_data) } +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Cert Encoding ! ! + +-+-+-+-+-+-+-+-+ ! + ~ Certificate Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_vendor_id_payload_t *this) +{ + return SUCCESS; +} + +/** + * Implementation of vendor_id_payload_t.get_encoding_rules. + */ +static void get_encoding_rules(private_vendor_id_payload_t *this, encoding_rule_t **rules, size_t *rule_count) +{ + *rules = vendor_id_payload_encodings; + *rule_count = sizeof(vendor_id_payload_encodings) / sizeof(encoding_rule_t); +} + +/** + * Implementation of payload_t.get_type. + */ +static payload_type_t get_payload_type(private_vendor_id_payload_t *this) +{ + return VENDOR_ID; +} + +/** + * Implementation of payload_t.get_next_type. + */ +static payload_type_t get_next_type(private_vendor_id_payload_t *this) +{ + return (this->next_payload); +} + +/** + * Implementation of payload_t.set_next_type. + */ +static void set_next_type(private_vendor_id_payload_t *this,payload_type_t type) +{ + this->next_payload = type; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_vendor_id_payload_t *this) +{ + return this->payload_length; +} + +/** + * Implementation of vendor_id_payload_t.set_data. + */ +static void set_data (private_vendor_id_payload_t *this, chunk_t data) +{ + if (this->vendor_id_data.ptr != NULL) + { + chunk_free(&(this->vendor_id_data)); + } + this->vendor_id_data.ptr = clalloc(data.ptr,data.len); + this->vendor_id_data.len = data.len; + this->payload_length = VENDOR_ID_PAYLOAD_HEADER_LENGTH + this->vendor_id_data.len; +} + +/** + * Implementation of vendor_id_payload_t.get_data. + */ +static chunk_t get_data (private_vendor_id_payload_t *this) +{ + return (this->vendor_id_data); +} + +/** + * Implementation of vendor_id_payload_t.get_data_clone. + */ +static chunk_t get_data_clone (private_vendor_id_payload_t *this) +{ + chunk_t cloned_data; + if (this->vendor_id_data.ptr == NULL) + { + return (this->vendor_id_data); + } + cloned_data.ptr = clalloc(this->vendor_id_data.ptr,this->vendor_id_data.len); + cloned_data.len = this->vendor_id_data.len; + return cloned_data; +} + +/** + * Implementation of payload_t.destroy and vendor_id_payload_t.destroy. + */ +static void destroy(private_vendor_id_payload_t *this) +{ + if (this->vendor_id_data.ptr != NULL) + { + chunk_free(&(this->vendor_id_data)); + } + free(this); +} + +/* + * Described in header + */ +vendor_id_payload_t *vendor_id_payload_create() +{ + private_vendor_id_payload_t *this = malloc_thing(private_vendor_id_payload_t); + + /* interface functions */ + this->public.payload_interface.verify = (status_t (*) (payload_t *))verify; + this->public.payload_interface.get_encoding_rules = (void (*) (payload_t *, encoding_rule_t **, size_t *) ) get_encoding_rules; + this->public.payload_interface.get_length = (size_t (*) (payload_t *)) get_length; + this->public.payload_interface.get_next_type = (payload_type_t (*) (payload_t *)) get_next_type; + this->public.payload_interface.set_next_type = (void (*) (payload_t *,payload_type_t)) set_next_type; + this->public.payload_interface.get_type = (payload_type_t (*) (payload_t *)) get_payload_type; + this->public.payload_interface.destroy = (void (*) (payload_t *))destroy; + + /* public functions */ + this->public.destroy = (void (*) (vendor_id_payload_t *)) destroy; + this->public.set_data = (void (*) (vendor_id_payload_t *,chunk_t)) set_data; + this->public.get_data_clone = (chunk_t (*) (vendor_id_payload_t *)) get_data_clone; + this->public.get_data = (chunk_t (*) (vendor_id_payload_t *)) get_data; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = VENDOR_ID_PAYLOAD_HEADER_LENGTH; + this->vendor_id_data = chunk_empty; + + return (&(this->public)); +} diff --git a/src/charon/encoding/payloads/vendor_id_payload.h b/src/charon/encoding/payloads/vendor_id_payload.h new file mode 100644 index 000000000..c7eebc155 --- /dev/null +++ b/src/charon/encoding/payloads/vendor_id_payload.h @@ -0,0 +1,104 @@ +/** + * @file vendor_id_payload.h + * + * @brief Interface of vendor_id_payload_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef VENDOR_ID_PAYLOAD_H_ +#define VENDOR_ID_PAYLOAD_H_ + +typedef struct vendor_id_payload_t vendor_id_payload_t; + +#include +#include + +/** + * Length of a VENDOR ID payload without the VID data in bytes. + * + * @ingroup payloads + */ +#define VENDOR_ID_PAYLOAD_HEADER_LENGTH 4 + + +/** + * @brief Class representing an IKEv2 VENDOR ID payload. + * + * The VENDOR ID payload format is described in RFC section 3.12. + * + * @b Constructors: + * - vendor_id_payload_create() + * + * @ingroup payloads + */ +struct vendor_id_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the VID data. + * + * Data are getting cloned. + * + * @param this calling vendor_id_payload_t object + * @param data VID data as chunk_t + */ + void (*set_data) (vendor_id_payload_t *this, chunk_t data); + + /** + * @brief Get the VID data. + * + * Returned data are a copy of the internal one. + * + * @param this calling vendor_id_payload_t object + * @return VID data as chunk_t + */ + chunk_t (*get_data_clone) (vendor_id_payload_t *this); + + /** + * @brief Get the VID data. + * + * Returned data are NOT copied. + * + * @param this calling vendor_id_payload_t object + * @return VID data as chunk_t + */ + chunk_t (*get_data) (vendor_id_payload_t *this); + + /** + * @brief Destroys an vendor_id_payload_t object. + * + * @param this vendor_id_payload_t object to destroy + */ + void (*destroy) (vendor_id_payload_t *this); +}; + +/** + * @brief Creates an empty vendor_id_payload_t object. + * + * @return vendor_id_payload_t object + * + * @ingroup payloads + */ +vendor_id_payload_t *vendor_id_payload_create(void); + + +#endif /* VENDOR_ID_PAYLOAD_H_ */ diff --git a/src/charon/network/packet.c b/src/charon/network/packet.c new file mode 100644 index 000000000..f2fa91569 --- /dev/null +++ b/src/charon/network/packet.c @@ -0,0 +1,168 @@ +/** + * @file packet.c + * + * @brief Implementation of packet_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "packet.h" + + +typedef struct private_packet_t private_packet_t; + +/** + * Private data of an packet_t object. + */ +struct private_packet_t { + + /** + * Public part of a packet_t object. + */ + packet_t public; + + /** + * source address + */ + host_t *source; + + /** + * destination address + */ + host_t *destination; + + /** + * message data + */ + chunk_t data; +}; + +/** + * Implements packet_t.get_source + */ +static void set_source(private_packet_t *this, host_t *source) +{ + DESTROY_IF(this->source); + this->source = source; +} + +/** + * Implements packet_t.set_destination + */ +static void set_destination(private_packet_t *this, host_t *destination) +{ + DESTROY_IF(this->destination); + this->destination = destination; +} + +/** + * Implements packet_t.get_source + */ +static host_t *get_source(private_packet_t *this) +{ + return this->source; +} + +/** + * Implements packet_t.get_destination + */ +static host_t *get_destination(private_packet_t *this) +{ + return this->destination; +} + +/** + * Implements packet_t.get_data + */ +static chunk_t get_data(private_packet_t *this) +{ + return this->data; +} + +/** + * Implements packet_t.set_data + */ +static void set_data(private_packet_t *this, chunk_t data) +{ + free(this->data.ptr); + this->data = data; +} + +/** + * Implements packet_t.destroy. + */ +static void destroy(private_packet_t *this) +{ + if (this->source != NULL) + { + this->source->destroy(this->source); + } + if (this->destination != NULL) + { + this->destination->destroy(this->destination); + } + free(this->data.ptr); + free(this); +} + +/** + * Implements packet_t.clone. + */ +static packet_t *clone_(private_packet_t *this) +{ + private_packet_t *other = (private_packet_t*)packet_create(); + + if (this->destination != NULL) + { + other->destination = this->destination->clone(this->destination); + } + if (this->source != NULL) + { + other->source = this->source->clone(this->source); + } + if (this->data.ptr != NULL) + { + other->data.ptr = clalloc(this->data.ptr,this->data.len); + other->data.len = this->data.len; + } + return &(other->public); +} + +/* + * Documented in header + */ +packet_t *packet_create(void) +{ + private_packet_t *this = malloc_thing(private_packet_t); + + this->public.set_data = (void(*) (packet_t *,chunk_t)) set_data; + this->public.get_data = (chunk_t(*) (packet_t *)) get_data; + this->public.set_source = (void(*) (packet_t *,host_t*)) set_source; + this->public.get_source = (host_t*(*) (packet_t *)) get_source; + this->public.set_destination = (void(*) (packet_t *,host_t*)) set_destination; + this->public.get_destination = (host_t*(*) (packet_t *)) get_destination; + this->public.clone = (packet_t*(*) (packet_t *))clone_; + this->public.destroy = (void(*) (packet_t *)) destroy; + + this->destination = NULL; + this->source = NULL; + this->data = chunk_empty; + + return &(this->public); +} diff --git a/src/charon/network/packet.h b/src/charon/network/packet.h new file mode 100644 index 000000000..acf953032 --- /dev/null +++ b/src/charon/network/packet.h @@ -0,0 +1,134 @@ +/** + * @file packet.h + * + * @brief Interface of packet_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PACKET_H_ +#define PACKET_H_ + +typedef struct packet_t packet_t; + +#include +#include + +/** + * @brief Abstraction of an UDP-Packet, contains data, sender and receiver. + * + * @b Constructors: + * - packet_create() + * + * @ingroup network + */ +struct packet_t { + + /** + * @brief Set the source address. + * + * Set host_t is now owned by packet_t, it will destroy + * it if necessary. + * + * @param this calling object + * @param source address to set as source + */ + void (*set_source) (packet_t *packet, host_t *source); + + /** + * @brief Set the destination address. + * + * Set host_t is now owned by packet_t, it will destroy + * it if necessary. + * + * @param this calling object + * @param source address to set as destination + */ + void (*set_destination) (packet_t *packet, host_t *destination); + + /** + * @brief Get the source address. + * + * Set host_t is still owned by packet_t, clone it + * if needed. + * + * @param this calling object + * @return source address + */ + host_t *(*get_source) (packet_t *packet); + + /** + * @brief Get the destination address. + * + * Set host_t is still owned by packet_t, clone it + * if needed. + * + * @param this calling object + * @return destination address + */ + host_t *(*get_destination) (packet_t *packet); + + /** + * @brief Get the data from the packet. + * + * The data pointed by the chunk is still owned + * by the packet. Clone it if needed. + * + * @param this calling object + * @return chunk containing the data + */ + chunk_t (*get_data) (packet_t *packet); + + /** + * @brief Set the data in the packet. + * + * Supplied chunk data is now owned by the + * packet. It will free it. + * + * @param this calling object + * @param data chunk with data to set + */ + void (*set_data) (packet_t *packet, chunk_t data); + + /** + * @brief Clones a packet_t object. + * + * @param packet calling object + * @param clone pointer to a packet_t object pointer where the new object is stored + */ + packet_t* (*clone) (packet_t *packet); + + /** + * @brief Destroy the packet, freeing contained data. + * + * @param packet packet to destroy + */ + void (*destroy) (packet_t *packet); +}; + +/** + * @brief create an empty packet + * + * @return packet_t object + * + * @ingroup network + */ +packet_t *packet_create(void); + + +#endif /*PACKET_H_*/ diff --git a/src/charon/network/socket.c b/src/charon/network/socket.c new file mode 100644 index 000000000..00ba22d5a --- /dev/null +++ b/src/charon/network/socket.c @@ -0,0 +1,755 @@ +/** + * @file socket.c + * + * @brief Implementation of socket_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket.h" + +#include + +/* constants for packet handling */ +#define IP_LEN sizeof(struct iphdr) +#define IP6_LEN sizeof(struct ip6_hdr) +#define UDP_LEN sizeof(struct udphdr) +#define MARKER_LEN sizeof(u_int32_t) + +/* offsets for packet handling */ +#define IP_PROTO_OFFSET 9 +#define IP6_PROTO_OFFSET 6 +#define IKE_VERSION_OFFSET 17 +#define IKE_LENGTH_OFFSET 24 + +/* from linux/in.h */ +#ifndef IP_IPSEC_POLICY +#define IP_IPSEC_POLICY 16 +#endif /*IP_IPSEC_POLICY*/ + +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif /*UDP_ENCAP*/ + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif /*UDP_ENCAP_ESPINUDP*/ + +/* needed for older kernel headers */ +#ifndef IPV6_2292PKTINFO +#define IPV6_2292PKTINFO 2 +#endif /*IPV6_2292PKTINFO*/ + +/* missing on uclibc */ +#ifndef IPV6_IPSEC_POLICY +#define IPV6_IPSEC_POLICY 34 +#endif /*IPV6_IPSEC_POLICY*/ + +typedef struct private_socket_t private_socket_t; + +/** + * Private data of an socket_t object + */ +struct private_socket_t{ + /** + * public functions + */ + socket_t public; + + /** + * regular port + */ + int port; + + /** + * port used for nat-t + */ + int natt_port; + + /** + * raw receiver socket for IPv4 + */ + int recv4; + + /** + * raw receiver socket for IPv6 + */ + int recv6; + + /** + * send socket on regular port for IPv4 + */ + int send4; + + /** + * send socket on regular port for IPv6 + */ + int send6; + + /** + * send socket on nat-t port for IPv4 + */ + int send4_natt; + + /** + * send socket on nat-t port for IPv6 + */ + int send6_natt; +}; + +/** + * implementation of socket_t.receive + */ +static status_t receiver(private_socket_t *this, packet_t **packet) +{ + char buffer[MAX_PACKET]; + chunk_t data; + packet_t *pkt; + struct udphdr *udp; + host_t *source = NULL, *dest = NULL; + int bytes_read = 0; + int data_offset, oldstate; + fd_set rfds; + + FD_ZERO(&rfds); + + if (this->recv4) + { + FD_SET(this->recv4, &rfds); + } + if (this->recv6) + { + FD_SET(this->recv6, &rfds); + } + + DBG2(DBG_NET, "waiting for data on raw sockets"); + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + if (select(max(this->recv4, this->recv6) + 1, &rfds, NULL, NULL, NULL) <= 0) + { + pthread_setcancelstate(oldstate, NULL); + return FAILED; + } + pthread_setcancelstate(oldstate, NULL); + + if (this->recv4 && FD_ISSET(this->recv4, &rfds)) + { + /* IPv4 raw sockets return the IP header. We read src/dest + * information directly from the raw header */ + struct iphdr *ip; + struct sockaddr_in src, dst; + + bytes_read = recv(this->recv4, buffer, MAX_PACKET, 0); + if (bytes_read < 0) + { + DBG1(DBG_NET, "error reading from IPv4 socket: %m"); + return FAILED; + } + DBG3(DBG_NET, "received IPv4 packet %b", buffer, bytes_read); + + /* read source/dest from raw IP/UDP header */ + if (bytes_read < IP_LEN + UDP_LEN + MARKER_LEN) + { + DBG1(DBG_NET, "received IPv4 packet too short (%d bytes)", + bytes_read); + return FAILED; + } + ip = (struct iphdr*) buffer; + udp = (struct udphdr*) (buffer + IP_LEN); + src.sin_family = AF_INET; + src.sin_addr.s_addr = ip->saddr; + src.sin_port = udp->source; + dst.sin_family = AF_INET; + dst.sin_addr.s_addr = ip->daddr; + dst.sin_port = udp->dest; + source = host_create_from_sockaddr((sockaddr_t*)&src); + dest = host_create_from_sockaddr((sockaddr_t*)&dst); + + pkt = packet_create(); + pkt->set_source(pkt, source); + pkt->set_destination(pkt, dest); + DBG2(DBG_NET, "received packet: from %#H to %#H", source, dest); + data_offset = IP_LEN + UDP_LEN; + /* remove non esp marker */ + if (dest->get_port(dest) == this->natt_port) + { + data_offset += MARKER_LEN; + } + /* fill in packet */ + data.len = bytes_read - data_offset; + data.ptr = malloc(data.len); + memcpy(data.ptr, buffer + data_offset, data.len); + pkt->set_data(pkt, data); + } + else if (this->recv6 && FD_ISSET(this->recv6, &rfds)) + { + /* IPv6 raw sockets return no IP header. We must query + * src/dest via socket options/ancillary data */ + struct msghdr msg; + struct cmsghdr *cmsgptr; + struct sockaddr_in6 src, dst; + struct iovec iov; + char ancillary[64]; + + msg.msg_name = &src; + msg.msg_namelen = sizeof(src); + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + bytes_read = recvmsg(this->recv6, &msg, 0); + if (bytes_read < 0) + { + DBG1(DBG_NET, "error reading from IPv6 socket: %m"); + return FAILED; + } + DBG3(DBG_NET, "received IPv6 packet %b", buffer, bytes_read); + + if (bytes_read < IP_LEN + UDP_LEN + MARKER_LEN) + { + DBG3(DBG_NET, "received IPv6 packet too short (%d bytes)", + bytes_read); + return FAILED; + } + + /* read ancillary data to get destination address */ + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) + { + if (cmsgptr->cmsg_len == 0) + { + DBG1(DBG_NET, "error reading IPv6 ancillary data"); + return FAILED; + } + if (cmsgptr->cmsg_level == SOL_IPV6 && + cmsgptr->cmsg_type == IPV6_2292PKTINFO) + { + struct in6_pktinfo *pktinfo; + pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsgptr); + + memset(&dst, 0, sizeof(dst)); + memcpy(&dst.sin6_addr, &pktinfo->ipi6_addr, sizeof(dst.sin6_addr)); + dst.sin6_family = AF_INET6; + udp = (struct udphdr*) (buffer); + dst.sin6_port = udp->dest; + src.sin6_port = udp->source; + dest = host_create_from_sockaddr((sockaddr_t*)&dst); + } + } + /* ancillary data missing? */ + if (dest == NULL) + { + DBG1(DBG_NET, "error reading IPv6 packet header"); + return FAILED; + } + + source = host_create_from_sockaddr((sockaddr_t*)&src); + + pkt = packet_create(); + pkt->set_source(pkt, source); + pkt->set_destination(pkt, dest); + DBG2(DBG_NET, "received packet: from %#H to %#H", source, dest); + data_offset = UDP_LEN; + /* remove non esp marker */ + if (dest->get_port(dest) == this->natt_port) + { + data_offset += MARKER_LEN; + } + /* fill in packet */ + data.len = bytes_read - data_offset; + data.ptr = malloc(data.len); + memcpy(data.ptr, buffer + data_offset, data.len); + pkt->set_data(pkt, data); + } + else + { + /* oops, shouldn't happen */ + return FAILED; + } + + /* return packet */ + *packet = pkt; + return SUCCESS; +} + +/** + * implementation of socket_t.send + */ +status_t sender(private_socket_t *this, packet_t *packet) +{ + int sport, skt, family; + ssize_t bytes_sent; + chunk_t data, marked; + host_t *src, *dst; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + data = packet->get_data(packet); + + DBG2(DBG_NET, "sending packet: from %#H to %#H", src, dst); + + /* send data */ + sport = src->get_port(src); + family = dst->get_family(dst); + if (sport == this->port) + { + if (family == AF_INET) + { + skt = this->send4; + } + else + { + skt = this->send6; + } + } + else if (sport == this->natt_port) + { + if (family == AF_INET) + { + skt = this->send4_natt; + } + else + { + skt = this->send6_natt; + } + /* NAT keepalives without marker */ + if (data.len != 1 || data.ptr[0] != 0xFF) + { + /* add non esp marker to packet */ + if (data.len > MAX_PACKET - MARKER_LEN) + { + DBG1(DBG_NET, "unable to send packet: it's too big (%d bytes)", + data.len); + return FAILED; + } + marked = chunk_alloc(data.len + MARKER_LEN); + memset(marked.ptr, 0, MARKER_LEN); + memcpy(marked.ptr + MARKER_LEN, data.ptr, data.len); + /* let the packet do the clean up for us */ + packet->set_data(packet, marked); + data = marked; + } + } + else + { + DBG1(DBG_NET, "unable to locate a send socket for port %d", sport); + return FAILED; + } + + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_name = dst->get_sockaddr(dst);; + msg.msg_namelen = *dst->get_sockaddr_len(dst); + iov.iov_base = data.ptr; + iov.iov_len = data.len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + if (!dst->is_anyaddr(dst)) + { + if (family == AF_INET) + { + char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + struct in_pktinfo *pktinfo; + struct sockaddr_in *sin; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + memset(pktinfo, 0, sizeof(struct in_pktinfo)); + sin = (struct sockaddr_in*)src->get_sockaddr(src); + memcpy(&pktinfo->ipi_spec_dst, &sin->sin_addr, sizeof(struct in_addr)); + } + else + { + char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 *sin; + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_IPV6; + cmsg->cmsg_type = IPV6_2292PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg); + memset(pktinfo, 0, sizeof(struct in6_pktinfo)); + sin = (struct sockaddr_in6*)src->get_sockaddr(src); + memcpy(&pktinfo->ipi6_addr, &sin->sin6_addr, sizeof(struct in6_addr)); + } + } + + bytes_sent = sendmsg(skt, &msg, 0); + + if (bytes_sent != data.len) + { + DBG1(DBG_NET, "error writing to socket: %m"); + return FAILED; + } + return SUCCESS; +} + +/** + * open a socket to send packets + */ +static int open_send_socket(private_socket_t *this, int family, u_int16_t port) +{ + int on = TRUE; + int type = UDP_ENCAP_ESPINUDP; + struct sockaddr_storage addr; + u_int sol, ipsec_policy; + struct sadb_x_policy policy; + int skt; + + memset(&addr, 0, sizeof(addr)); + /* precalculate constants depending on address family */ + switch (family) + { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_port = htons(port); + sol = SOL_IP; + ipsec_policy = IP_IPSEC_POLICY; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any)); + sin6->sin6_port = htons(port); + sol = SOL_IPV6; + ipsec_policy = IPV6_IPSEC_POLICY; + break; + } + default: + return 0; + } + + skt = socket(family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 0) + { + DBG1(DBG_NET, "could not open send socket: %m"); + return 0; + } + + if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + { + DBG1(DBG_NET, "unable to set SO_REUSEADDR on send socket: %m"); + close(skt); + return 0; + } + + /* bypass outgoung IKE traffic on send socket */ + memset(&policy, 0, sizeof(policy)); + policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t); + policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; + policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; + + if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_NET, "unable to set IPSEC_POLICY on send socket: %m"); + close(skt); + return 0; + } + + /* We don't receive packets on the send socket, but we need a INBOUND policy. + * Otherwise, UDP decapsulation does not work!!! */ + policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; + if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_NET, "unable to set IPSEC_POLICY on send socket: %m"); + close(skt); + return 0; + } + + /* bind the send socket */ + if (bind(skt, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + DBG1(DBG_NET, "unable to bind send socket: %m"); + close(skt); + return 0; + } + + if (family == AF_INET) + { + /* enable UDP decapsulation globally, only for one socket needed */ + if (setsockopt(skt, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_NET, "unable to set UDP_ENCAP: %m; NAT-T may fail"); + } + } + + return skt; +} + +/** + * open a socket to receive packets + */ +static int open_recv_socket(private_socket_t *this, int family) +{ + int skt; + int on = TRUE; + u_int proto_offset, ip_len, sol, ipsec_policy, udp_header, ike_header; + struct sadb_x_policy policy; + + /* precalculate constants depending on address family */ + switch (family) + { + case AF_INET: + proto_offset = IP_PROTO_OFFSET; + ip_len = IP_LEN; + sol = SOL_IP; + ipsec_policy = IP_IPSEC_POLICY; + break; + case AF_INET6: + proto_offset = IP6_PROTO_OFFSET; + ip_len = 0; /* IPv6 raw sockets contain no IP header */ + sol = SOL_IPV6; + ipsec_policy = IPV6_IPSEC_POLICY; + break; + default: + return 0; + } + udp_header = ip_len; + ike_header = ip_len + UDP_LEN; + + /* This filter code filters out all non-IKEv2 traffic on + * a SOCK_RAW IP_PROTP_UDP socket. Handling of other + * IKE versions is done in pluto. + */ + struct sock_filter ikev2_filter_code[] = + { + /* Destination Port must be either port or natt_port */ + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, udp_header + 2), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, this->port, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, this->natt_port, 5, 12), + /* port */ + /* IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ike_header + IKE_VERSION_OFFSET), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 10), + /* packet length is length in IKEv2 header + ip header + udp header */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ike_header + IKE_LENGTH_OFFSET), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, ip_len + UDP_LEN), + BPF_STMT(BPF_RET+BPF_A, 0), + /* natt_port */ + /* nat-t: check for marker */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ike_header), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 5), + /* nat-t: IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, ike_header + MARKER_LEN + IKE_VERSION_OFFSET), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 3), + /* nat-t: packet length is length in IKEv2 header + ip header + udp header + non esp marker */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ike_header + MARKER_LEN + IKE_LENGTH_OFFSET), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, ip_len + UDP_LEN + MARKER_LEN), + BPF_STMT(BPF_RET+BPF_A, 0), + /* packet doesn't match, ignore */ + BPF_STMT(BPF_RET+BPF_K, 0), + }; + + /* Filter struct to use with setsockopt */ + struct sock_fprog ikev2_filter = { + sizeof(ikev2_filter_code) / sizeof(struct sock_filter), + ikev2_filter_code + }; + + /* set up a raw socket */ + skt = socket(family, SOCK_RAW, IPPROTO_UDP); + if (skt < 0) + { + DBG1(DBG_NET, "unable to create raw socket: %m"); + return 0; + } + + if (setsockopt(skt, SOL_SOCKET, SO_ATTACH_FILTER, + &ikev2_filter, sizeof(ikev2_filter)) < 0) + { + DBG1(DBG_NET, "unable to attach IKEv2 filter to raw socket: %m"); + close(skt); + return 0; + } + + if (family == AF_INET6 && + /* we use IPV6_2292PKTINFO, as IPV6_PKTINFO is defined as + * 2 or 50 depending on kernel header version */ + setsockopt(skt, sol, IPV6_2292PKTINFO, &on, sizeof(on)) < 0) + { + DBG1(DBG_NET, "unable to set IPV6_PKTINFO on raw socket: %m"); + close(skt); + return 0; + } + + /* bypass incomining IKE traffic on this socket */ + memset(&policy, 0, sizeof(policy)); + policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t); + policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; + policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; + + if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0) + { + DBG1(DBG_NET, "unable to set IPSEC_POLICY on raw socket: %m"); + close(skt); + return 0; + } + + return skt; +} + +/** + * implementation of socket_t.destroy + */ +static void destroy(private_socket_t *this) +{ + if (this->recv4) + { + close(this->recv4); + } + if (this->recv6) + { + close(this->recv6); + } + if (this->send4) + { + close(this->send4); + } + if (this->send6) + { + close(this->send6); + } + if (this->send4_natt) + { + close(this->send4_natt); + } + if (this->send6_natt) + { + close(this->send6_natt); + } + free(this); +} + +/* + * See header for description + */ +socket_t *socket_create(u_int16_t port, u_int16_t natt_port) +{ + private_socket_t *this = malloc_thing(private_socket_t); + + /* public functions */ + this->public.send = (status_t(*)(socket_t*, packet_t*))sender; + this->public.receive = (status_t(*)(socket_t*, packet_t**))receiver; + this->public.destroy = (void(*)(socket_t*)) destroy; + + this->port = port; + this->natt_port = natt_port; + this->recv4 = 0; + this->recv6 = 0; + this->send4 = 0; + this->send6 = 0; + this->send4_natt = 0; + this->send6_natt = 0; + + this->recv4 = open_recv_socket(this, AF_INET); + if (this->recv4 == 0) + { + DBG1(DBG_NET, "could not open IPv4 receive socket, IPv4 disabled"); + } + else + { + this->send4 = open_send_socket(this, AF_INET, this->port); + if (this->send4 == 0) + { + DBG1(DBG_NET, "could not open IPv4 send socket, IPv4 disabled"); + close(this->recv4); + } + else + { + this->send4_natt = open_send_socket(this, AF_INET, this->natt_port); + if (this->send4_natt == 0) + { + DBG1(DBG_NET, "could not open IPv4 NAT-T send socket"); + } + } + } + + this->recv6 = open_recv_socket(this, AF_INET6); + if (this->recv6 == 0) + { + DBG1(DBG_NET, "could not open IPv6 receive socket, IPv6 disabled"); + } + else + { + this->send6 = open_send_socket(this, AF_INET6, this->port); + if (this->send6 == 0) + { + DBG1(DBG_NET, "could not open IPv6 send socket, IPv6 disabled"); + close(this->recv6); + } + else + { + this->send6_natt = open_send_socket(this, AF_INET6, this->natt_port); + if (this->send6_natt == 0) + { + DBG1(DBG_NET, "could not open IPv6 NAT-T send socket"); + } + } + } + + if (!(this->send4 || this->send6) || !(this->recv4 || this->recv6)) + { + DBG1(DBG_NET, "could not create any sockets"); + destroy(this); + charon->kill(charon, "socket initialization failed"); + } + + return (socket_t*)this; +} diff --git a/src/charon/network/socket.h b/src/charon/network/socket.h new file mode 100644 index 000000000..ef60fa7b6 --- /dev/null +++ b/src/charon/network/socket.h @@ -0,0 +1,112 @@ +/** + * @file socket.h + * + * @brief Interface for socket_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SOCKET_H_ +#define SOCKET_H_ + +typedef struct socket_t socket_t; + +#include +#include +#include +#include + +/** + * @brief Maximum size of a packet. + * + * 3000 Bytes should be sufficient, see IKEv2 RFC. + * + * @ingroup network + */ +#define MAX_PACKET 3000 + +/** + * @brief Abstraction of all sockets (IPv6/IPv6 send/receive). + * + * All available sockets are bound and the receive function + * reads from them. To allow binding of other daemons (pluto) to + * UDP/500, this implementation uses RAW sockets. An installed + * "Linux socket filter" filters out all non-IKEv2 traffic and handles + * just IKEv2 messages. An other daemon (pluto) must handle all traffic + * seperatly, e.g. ignore IKEv2 traffic, since charon handles that. + * + * @b Constructors: + * - socket_create() + * + * @ingroup network + */ +struct socket_t { + + /** + * @brief Receive a packet. + * + * Reads a packet from the socket and sets source/dest + * appropriately. + * + * @param this socket_t object to work on + * @param packet pinter gets address from allocated packet_t + * @return + * - SUCCESS when packet successfully received + * - FAILED when unable to receive + */ + status_t (*receive) (socket_t *this, packet_t **packet); + + /** + * @brief Send a packet. + * + * Sends a packet to the net using destination from the packet. + * Packet is sent using default routing mechanisms, thus the + * source address in packet is ignored. + * + * @param this socket_t object to work on + * @param packet[out] packet_t to send + * @return + * - SUCCESS when packet successfully sent + * - FAILED when unable to send + */ + status_t (*send) (socket_t *this, packet_t *packet); + + /** + * @brief Destroy sockets. + * + * close sockets and destroy socket_t object + * + * @param this socket_t to destroy + */ + void (*destroy) (socket_t *this); +}; + +/** + * @brief Create a socket_t, wich binds multiple sockets. + * + * @param port port to bind socket to + * @param natt_port port to float to in NAT-T + * @return socket_t object + * + * @ingroup network + */ +socket_t *socket_create(u_int16_t port, u_int16_t natt_port); + + +#endif /*SOCKET_H_*/ diff --git a/src/charon/queues/event_queue.c b/src/charon/queues/event_queue.c new file mode 100644 index 000000000..40bcb1ed8 --- /dev/null +++ b/src/charon/queues/event_queue.c @@ -0,0 +1,290 @@ +/** + * @file event_queue.c + * + * @brief Implementation of event_queue_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "event_queue.h" + +#include +#include + + + +typedef struct event_t event_t; + +/** + * Event containing a job and a schedule time + */ +struct event_t { + /** + * Time to fire the event. + */ + timeval_t time; + + /** + * Every event has its assigned job. + */ + job_t * job; +}; + +/** + * destroy an event and its job + */ +static void event_destroy(event_t *event) +{ + event->job->destroy(event->job); + free(event); +} + +typedef struct private_event_queue_t private_event_queue_t; + +/** + * Private Variables and Functions of event_queue_t class. + */ +struct private_event_queue_t { + /** + * Public part. + */ + event_queue_t public; + + /** + * The events are stored in a linked list of type linked_list_t. + */ + linked_list_t *list; + + /** + * Access to linked_list is locked through this mutex. + */ + pthread_mutex_t mutex; + + /** + * If the queue is empty or an event has not to be fired + * a thread has to wait. + * + * This condvar is used to wake up such a thread. + */ + pthread_cond_t condvar; +}; + +/** + * Returns the difference of to timeval structs in milliseconds + */ +static long time_difference(struct timeval *end_time, struct timeval *start_time) +{ + time_t s; + suseconds_t us; + + s = (end_time->tv_sec - start_time->tv_sec); + us = (end_time->tv_usec - start_time->tv_usec); + return ((s * 1000) + us/1000); +} + +/** + * Implements event_queue_t.get_count + */ +static int get_count(private_event_queue_t *this) +{ + int count; + pthread_mutex_lock(&(this->mutex)); + count = this->list->get_count(this->list); + pthread_mutex_unlock(&(this->mutex)); + return count; +} + +/** + * Implements event_queue_t.get + */ +static job_t *get(private_event_queue_t *this) +{ + timespec_t timeout; + timeval_t current_time; + event_t * next_event; + job_t *job; + int oldstate; + + pthread_mutex_lock(&(this->mutex)); + + while (TRUE) + { + while(this->list->get_count(this->list) == 0) + { + /* add mutex unlock handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + pthread_cond_wait( &(this->condvar), &(this->mutex)); + + /* reset cancellation, remove mutex-unlock handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + + this->list->get_first(this->list, (void **)&next_event); + + gettimeofday(¤t_time, NULL); + long difference = time_difference(¤t_time,&(next_event->time)); + if (difference <= 0) + { + timeout.tv_sec = next_event->time.tv_sec; + timeout.tv_nsec = next_event->time.tv_usec * 1000; + + /* add mutex unlock handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + pthread_cond_timedwait(&(this->condvar), &(this->mutex), &timeout); + + /* reset cancellation, remove mutex-unlock handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + else + { + /* event available */ + this->list->remove_first(this->list, (void **)&next_event); + job = next_event->job; + free(next_event); + break; + } + } + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); + + return job; +} + +/** + * Implements function add_absolute of event_queue_t. + * See #event_queue_s.add_absolute for description. + */ +static void add_absolute(private_event_queue_t *this, job_t *job, timeval_t time) +{ + event_t *event; + event_t *current_event; + iterator_t *iterator; + + /* create event */ + event = malloc_thing(event_t); + event->time = time; + event->job = job; + + pthread_mutex_lock(&(this->mutex)); + + /* while just used to break out */ + while(TRUE) + { + if (this->list->get_count(this->list) == 0) + { + this->list->insert_first(this->list,event); + break; + } + + /* check last entry */ + this->list->get_last(this->list,(void **) ¤t_event); + + if (time_difference(&(event->time), &(current_event->time)) >= 0) + { + /* my event has to be fired after the last event in list */ + this->list->insert_last(this->list,event); + break; + } + + /* check first entry */ + this->list->get_first(this->list,(void **) ¤t_event); + + if (time_difference(&(event->time), &(current_event->time)) < 0) + { + /* my event has to be fired before the first event in list */ + this->list->insert_first(this->list,event); + break; + } + + iterator = this->list->create_iterator(this->list,TRUE); + iterator->iterate(iterator, (void**)¤t_event); + /* first element has not to be checked (already done) */ + while(iterator->iterate(iterator, (void**)¤t_event)) + { + if (time_difference(&(event->time), &(current_event->time)) <= 0) + { + /* my event has to be fired before the current event in list */ + iterator->insert_before(iterator,event); + break; + } + } + iterator->destroy(iterator); + break; + } + + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implements event_queue_t.add_relative. + */ +static void add_relative(event_queue_t *this, job_t *job, u_int32_t ms) +{ + timeval_t current_time; + timeval_t time; + + time_t s = ms / 1000; + suseconds_t us = (ms - s * 1000) * 1000; + + gettimeofday(¤t_time, NULL); + + time.tv_usec = (current_time.tv_usec + us) % 1000000; + time.tv_sec = current_time.tv_sec + (current_time.tv_usec + us)/1000000 + s; + + this->add_absolute(this, job, time); +} + + +/** + * Implements event_queue_t.destroy. + */ +static void event_queue_destroy(private_event_queue_t *this) +{ + this->list->destroy_function(this->list, (void*)event_destroy); + free(this); +} + +/* + * Documented in header + */ +event_queue_t *event_queue_create() +{ + private_event_queue_t *this = malloc_thing(private_event_queue_t); + + this->public.get_count = (int (*) (event_queue_t *event_queue)) get_count; + this->public.get = (job_t *(*) (event_queue_t *event_queue)) get; + this->public.add_absolute = (void (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add_absolute; + this->public.add_relative = (void (*) (event_queue_t *event_queue, job_t *job, u_int32_t ms)) add_relative; + this->public.destroy = (void (*) (event_queue_t *event_queue)) event_queue_destroy; + + this->list = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + pthread_cond_init(&(this->condvar), NULL); + + return (&this->public); +} diff --git a/src/charon/queues/event_queue.h b/src/charon/queues/event_queue.h new file mode 100644 index 000000000..cd275123b --- /dev/null +++ b/src/charon/queues/event_queue.h @@ -0,0 +1,118 @@ +/** + * @file event_queue.h + * + * @brief Interface of job_queue_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef EVENT_QUEUE_H_ +#define EVENT_QUEUE_H_ + +typedef struct event_queue_t event_queue_t; + +#include + +#include +#include + +/** + * @brief Event-Queue used to store timed events. + * + * Added events are sorted. The get method blocks until + * the time is elapsed to process the next event. The get + * method is called from the scheduler_t thread, which + * will add the jobs to to job_queue_t for further processing. + * + * Although the event-queue is based on a linked_list_t + * all access functions are thread-save implemented. + * + * @b Constructors: + * - event_queue_create() + * + * @ingroup queues + */ +struct event_queue_t { + + /** + * @brief Returns number of events in queue. + * + * @param event_queue calling object + * @return number of events in queue + */ + int (*get_count) (event_queue_t *event_queue); + + /** + * @brief Get the next job from the event-queue. + * + * If no event is pending, this function blocks until a job can be returned. + * + * @param event_queue calling object + * @param[out] job pointer to a job pointer where to job is returned to + * @return next job + */ + job_t *(*get) (event_queue_t *event_queue); + + /** + * @brief Adds a event to the queue, using a relative time. + * + * This function is non blocking and adds a job_t at a specific time to the list. + * The specific job object has to get destroyed by the thread which + * removes the job. + * + * @param event_queue calling object + * @param[in] job job to add to the queue (job is not copied) + * @param[in] time relative time, when the event has to get fired + */ + void (*add_relative) (event_queue_t *event_queue, job_t *job, u_int32_t ms); + + /** + * @brief Adds a event to the queue, using an absolute time. + * + * This function is non blocking and adds a job_t at a specific time to the list. + * The specific job object has to get destroyed by the thread which + * removes the job. + * + * @param event_queue calling object + * @param[in] job job to add to the queue (job is not copied) + * @param[in] time absolute time, when the event has to get fired + */ + void (*add_absolute) (event_queue_t *event_queue, job_t *job, timeval_t time); + + /** + * @brief Destroys a event_queue object. + * + * @warning The caller of this function has to make sure + * that no thread is going to add or get an event from the event_queue + * after calling this function. + * + * @param event_queue calling object + */ + void (*destroy) (event_queue_t *event_queue); +}; + +/** + * @brief Creates an empty event_queue. + * + * @returns event_queue_t object + * + * @ingroup queues + */ +event_queue_t *event_queue_create(void); + +#endif /*EVENT_QUEUE_H_*/ diff --git a/src/charon/queues/job_queue.c b/src/charon/queues/job_queue.c new file mode 100644 index 000000000..2310ca6ff --- /dev/null +++ b/src/charon/queues/job_queue.c @@ -0,0 +1,139 @@ +/** + * @file job_queue.c + * + * @brief Implementation of job_queue_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "job_queue.h" + +#include + + +typedef struct private_job_queue_t private_job_queue_t; + +/** + * @brief Private Variables and Functions of job_queue class + * + */ +struct private_job_queue_t { + + /** + * public members + */ + job_queue_t public; + + /** + * The jobs are stored in a linked list + */ + linked_list_t *list; + + /** + * access to linked_list is locked through this mutex + */ + pthread_mutex_t mutex; + + /** + * If the queue is empty a thread has to wait + * This condvar is used to wake up such a thread + */ + pthread_cond_t condvar; +}; + + +/** + * implements job_queue_t.get_count + */ +static int get_count(private_job_queue_t *this) +{ + int count; + pthread_mutex_lock(&(this->mutex)); + count = this->list->get_count(this->list); + pthread_mutex_unlock(&(this->mutex)); + return count; +} + +/** + * implements job_queue_t.get + */ +static job_t *get(private_job_queue_t *this) +{ + int oldstate; + job_t *job; + pthread_mutex_lock(&(this->mutex)); + /* go to wait while no jobs available */ + while(this->list->get_count(this->list) == 0) + { + /* add mutex unlock handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex)); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + pthread_cond_wait( &(this->condvar), &(this->mutex)); + + /* reset cancellation, remove mutex-unlock handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + this->list->remove_first(this->list, (void **)&job); + pthread_mutex_unlock(&(this->mutex)); + return job; +} + +/** + * implements function job_queue_t.add + */ +static void add(private_job_queue_t *this, job_t *job) +{ + pthread_mutex_lock(&(this->mutex)); + this->list->insert_last(this->list,job); + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * implements job_queue_t.destroy + */ +static void job_queue_destroy (private_job_queue_t *this) +{ + this->list->destroy_offset(this->list, offsetof(job_t, destroy)); + free(this); +} + +/* + * + * Documented in header + */ +job_queue_t *job_queue_create(void) +{ + private_job_queue_t *this = malloc_thing(private_job_queue_t); + + this->public.get_count = (int(*)(job_queue_t*))get_count; + this->public.get = (job_t*(*)(job_queue_t*))get; + this->public.add = (void(*)(job_queue_t*, job_t*))add; + this->public.destroy = (void(*)(job_queue_t*))job_queue_destroy; + + this->list = linked_list_create(); + pthread_mutex_init(&(this->mutex), NULL); + pthread_cond_init(&(this->condvar), NULL); + + return (&this->public); +} diff --git a/src/charon/queues/job_queue.h b/src/charon/queues/job_queue.h new file mode 100644 index 000000000..c971ba514 --- /dev/null +++ b/src/charon/queues/job_queue.h @@ -0,0 +1,100 @@ +/** + * @file job_queue.h + * + * @brief Interface of job_queue_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef JOB_QUEUE_H_ +#define JOB_QUEUE_H_ + +typedef struct job_queue_t job_queue_t; + +#include +#include + +/** + * @brief The job queue stores jobs, which will be processed by the thread_pool_t. + * + * Jobs are added from various sources, from the threads and + * from the event_queue_t. + * Although the job-queue is based on a linked_list_t + * all access functions are thread-save implemented. + * + * @b Constructors: + * - job_queue_create() + * + * @ingroup queues + */ +struct job_queue_t { + + /** + * @brief Returns number of jobs in queue. + * + * @param job_queue_t calling object + * @returns number of items in queue + */ + int (*get_count) (job_queue_t *job_queue); + + /** + * @brief Get the next job from the queue. + * + * If the queue is empty, this function blocks until a job can be returned. + * After using, the returned job has to get destroyed by the caller. + * + * @param job_queue_t calling object + * @param[out] job pointer to a job pointer where to job is returned to + * @return next job + */ + job_t *(*get) (job_queue_t *job_queue); + + /** + * @brief Adds a job to the queue. + * + * This function is non blocking and adds a job_t to the list. + * The specific job object has to get destroyed by the thread which + * removes the job. + * + * @param job_queue_t calling object + * @param job job to add to the queue (job is not copied) + */ + void (*add) (job_queue_t *job_queue, job_t *job); + + /** + * @brief Destroys a job_queue object. + * + * @warning The caller of this function has to make sure + * that no thread is going to add or get a job from the job_queue + * after calling this function. + * + * @param job_queue_t calling object + */ + void (*destroy) (job_queue_t *job_queue); +}; + +/** + * @brief Creates an empty job_queue. + * + * @return job_queue_t object + * + * @ingroup queues + */ +job_queue_t *job_queue_create(void); + +#endif /*JOB_QUEUE_H_*/ diff --git a/src/charon/queues/jobs/acquire_job.c b/src/charon/queues/jobs/acquire_job.c new file mode 100644 index 000000000..b4ffb258d --- /dev/null +++ b/src/charon/queues/jobs/acquire_job.c @@ -0,0 +1,98 @@ +/** + * @file acquire_job.c + * + * @brief Implementation of acquire_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "acquire_job.h" + +#include + + +typedef struct private_acquire_job_t private_acquire_job_t; + +/** + * Private data of an acquire_job_t object. + */ +struct private_acquire_job_t { + /** + * Public acquire_job_t interface. + */ + acquire_job_t public; + + /** + * reqid of the child to rekey + */ + u_int32_t reqid; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_acquire_job_t *this) +{ + return ACQUIRE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_acquire_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + if (ike_sa == NULL) + { + DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for acquiring", + this->reqid); + return DESTROY_ME; + } + ike_sa->acquire(ike_sa, this->reqid); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_acquire_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +acquire_job_t *acquire_job_create(u_int32_t reqid) +{ + private_acquire_job_t *this = malloc_thing(private_acquire_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/acquire_job.h b/src/charon/queues/jobs/acquire_job.h new file mode 100644 index 000000000..54f1b9b5b --- /dev/null +++ b/src/charon/queues/jobs/acquire_job.h @@ -0,0 +1,60 @@ +/** + * @file acquire_job.h + * + * @brief Interface of acquire_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef ACQUIRE_JOB_H_ +#define ACQUIRE_JOB_H_ + +typedef struct acquire_job_t acquire_job_t; + +#include +#include + +/** + * @brief Class representing an ACQUIRE Job. + * + * This job initiates a CHILD SA on kernel request. + * + * @b Constructors: + * - acquire_job_create() + * + * @ingroup jobs + */ +struct acquire_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type ACQUIRE. + * + * We use the reqid to find the routed CHILD_SA. + * + * @param reqid reqid of the CHILD_SA to acquire + * @return acquire_job_t object + * + * @ingroup jobs + */ +acquire_job_t *acquire_job_create(u_int32_t reqid); + +#endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c new file mode 100644 index 000000000..f694696b0 --- /dev/null +++ b/src/charon/queues/jobs/delete_child_sa_job.c @@ -0,0 +1,113 @@ +/** + * @file delete_child_sa_job.c + * + * @brief Implementation of delete_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "delete_child_sa_job.h" + +#include + + +typedef struct private_delete_child_sa_job_t private_delete_child_sa_job_t; + +/** + * Private data of an delete_child_sa_job_t object. + */ +struct private_delete_child_sa_job_t { + /** + + * Public delete_child_sa_job_t interface. + */ + delete_child_sa_job_t public; + + /** + * reqid of the CHILD_SA + */ + u_int32_t reqid; + + /** + * protocol of the CHILD_SA (ESP/AH) + */ + protocol_id_t protocol; + + /** + * inbound SPI of the CHILD_SA + */ + u_int32_t spi; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_delete_child_sa_job_t *this) +{ + return DELETE_CHILD_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_delete_child_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + if (ike_sa == NULL) + { + DBG1(DBG_JOB, "CHILD_SA with reqid %d not found for delete", + this->reqid); + return DESTROY_ME; + } + ike_sa->delete_child_sa(ike_sa, this->protocol, this->spi); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_delete_child_sa_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi) +{ + private_delete_child_sa_job_t *this = malloc_thing(private_delete_child_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + this->protocol = protocol; + this->spi = spi; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/delete_child_sa_job.h b/src/charon/queues/jobs/delete_child_sa_job.h new file mode 100644 index 000000000..9c2e4fa4d --- /dev/null +++ b/src/charon/queues/jobs/delete_child_sa_job.h @@ -0,0 +1,68 @@ +/** + * @file delete_child_sa_job.h + * + * @brief Interface of delete_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DELETE_CHILD_SA_JOB_H_ +#define DELETE_CHILD_SA_JOB_H_ + +typedef struct delete_child_sa_job_t delete_child_sa_job_t; + +#include +#include +#include +#include + + +/** + * @brief Class representing an DELETE_CHILD_SA Job. + * + * This job initiates the delete of a CHILD SA. + * + * @b Constructors: + * - delete_child_sa_job_create() + * + * @ingroup jobs + */ +struct delete_child_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type DELETE_CHILD_SA. + * + * The CHILD_SA is identified by its reqid, protocol (AH/ESP) and its + * inbound SPI. + * + * @param reqid reqid of the CHILD_SA, as used in kernel + * @param protocol protocol of the CHILD_SA + * @param spi security parameter index of the CHILD_SA + * @return delete_child_sa_job_t object + * + * @ingroup jobs + */ +delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi); + +#endif /* DELETE_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/delete_ike_sa_job.c b/src/charon/queues/jobs/delete_ike_sa_job.c new file mode 100644 index 000000000..706155aa6 --- /dev/null +++ b/src/charon/queues/jobs/delete_ike_sa_job.c @@ -0,0 +1,126 @@ +/** + * @file delete_ike_sa_job.c + * + * @brief Implementation of delete_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "delete_ike_sa_job.h" + +#include + +typedef struct private_delete_ike_sa_job_t private_delete_ike_sa_job_t; + +/** + * Private data of an delete_ike_sa_job_t Object + */ +struct private_delete_ike_sa_job_t { + /** + * public delete_ike_sa_job_t interface + */ + delete_ike_sa_job_t public; + + /** + * ID of the ike_sa to delete + */ + ike_sa_id_t *ike_sa_id; + + /** + * Should the IKE_SA be deleted if it is in ESTABLISHED state? + */ + bool delete_if_established; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_delete_ike_sa_job_t *this) +{ + return DELETE_IKE_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_delete_ike_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) + { + if (this->delete_if_established) + { + if (ike_sa->delete(ike_sa) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + else + { + /* destroy only if not ESTABLISHED */ + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + else + { + DBG1(DBG_JOB, "deleting half open IKE_SA after timeout"); + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + } + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_delete_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +delete_ike_sa_job_t *delete_ike_sa_job_create(ike_sa_id_t *ike_sa_id, + bool delete_if_established) +{ + private_delete_ike_sa_job_t *this = malloc_thing(private_delete_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t *)) destroy;; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->delete_if_established = delete_if_established; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/delete_ike_sa_job.h b/src/charon/queues/jobs/delete_ike_sa_job.h new file mode 100644 index 000000000..43701a354 --- /dev/null +++ b/src/charon/queues/jobs/delete_ike_sa_job.h @@ -0,0 +1,66 @@ +/** + * @file delete_ike_sa_job.h + * + * @brief Interface of delete_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DELETE_IKE_SA_JOB_H_ +#define DELETE_IKE_SA_JOB_H_ + +typedef struct delete_ike_sa_job_t delete_ike_sa_job_t; + +#include +#include +#include + + +/** + * @brief Class representing an DELETE_IKE_SA Job. + * + * This job is responsible for deleting established or half open IKE_SAs. + * A half open IKE_SA is every IKE_SA which hasn't reache the SA_ESTABLISHED + * state. + * + * @b Constructors: + * - delete_ike_sa_job_create() + * + * @ingroup jobs + */ +struct delete_ike_sa_job_t { + + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type DELETE_IKE_SA. + * + * @param ike_sa_id id of the IKE_SA to delete + * @param delete_if_established should the IKE_SA be deleted if it is established? + * @return created delete_ike_sa_job_t object + * + * @ingroup jobs + */ +delete_ike_sa_job_t *delete_ike_sa_job_create(ike_sa_id_t *ike_sa_id, + bool delete_if_established); + +#endif /* DELETE_IKE_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/initiate_job.c b/src/charon/queues/jobs/initiate_job.c new file mode 100644 index 000000000..af50663d6 --- /dev/null +++ b/src/charon/queues/jobs/initiate_job.c @@ -0,0 +1,112 @@ +/** + * @file initiate_job.c + * + * @brief Implementation of initiate_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include + +#include "initiate_job.h" + +#include + +typedef struct private_initiate_job_t private_initiate_job_t; + +/** + * Private data of an initiate_job_t Object + */ +struct private_initiate_job_t { + /** + * public initiate_job_t interface + */ + initiate_job_t public; + + /** + * associated connection to initiate + */ + connection_t *connection; + + /** + * associated policy to initiate + */ + policy_t *policy; +}; + +/** + * Implements initiate_job_t.get_type. + */ +static job_type_t get_type(private_initiate_job_t *this) +{ + return INITIATE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_initiate_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); + + if (ike_sa->initiate(ike_sa, this->connection, this->policy) != SUCCESS) + { + DBG1(DBG_JOB, "initiation failed, going to delete IKE_SA"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_initiate_job_t *this) +{ + this->connection->destroy(this->connection); + this->policy->destroy(this->policy); + free(this); +} + +/* + * Described in header + */ +initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy) +{ + private_initiate_job_t *this = malloc_thing(private_initiate_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->connection = connection; + this->policy = policy; + + return &this->public; +} diff --git a/src/charon/queues/jobs/initiate_job.h b/src/charon/queues/jobs/initiate_job.h new file mode 100644 index 000000000..af1dd9ece --- /dev/null +++ b/src/charon/queues/jobs/initiate_job.h @@ -0,0 +1,61 @@ +/** + * @file initiate_job.h + * + * @brief Interface of initiate_job_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef INITIATE_IKE_SA_JOB_H_ +#define INITIATE_IKE_SA_JOB_H_ + +typedef struct initiate_job_t initiate_job_t; + +#include +#include +#include +#include + +/** + * @brief Class representing an INITIATE_IKE_SA Job. + * + * This job is created if an IKE_SA should be iniated. + * + * @b Constructors: + * - initiate_job_create() + * + * @ingroup jobs + */ +struct initiate_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type INITIATE_IKE_SA. + * + * @param connection connection_t to initialize + * @param policy policy to set up + * @return initiate_job_t object + * + * @ingroup jobs + */ +initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy); + +#endif /*INITIATE_IKE_SA_JOB_H_*/ diff --git a/src/charon/queues/jobs/job.c b/src/charon/queues/jobs/job.c new file mode 100644 index 000000000..d32d1bc61 --- /dev/null +++ b/src/charon/queues/jobs/job.c @@ -0,0 +1,39 @@ +/** + * @file job.c + * + * @brief Interface additions to job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "job.h" + +ENUM(job_type_names, PROCESS_MESSAGE, SEND_DPD, + "PROCESS_MESSAGE", + "RETRANSMIT", + "INITIATE", + "ROUTE", + "ACQUIRE", + "DELETE_IKE_SA", + "DELETE_CHILD_SA", + "REKEY_CHILD_SA", + "REKEY_IKE_SA", + "SEND_KEEPALIVE", + "SEND_DPD", +); diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h new file mode 100644 index 000000000..28632672d --- /dev/null +++ b/src/charon/queues/jobs/job.h @@ -0,0 +1,165 @@ +/** + * @file job.h + * + * @brief Interface job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef JOB_H_ +#define JOB_H_ + +typedef enum job_type_t job_type_t; +typedef struct job_t job_t; + +#include + +/** + * @brief Definition of the various job types. + * + * @ingroup jobs + */ +enum job_type_t { + /** + * Process an incoming IKEv2-Message. + * + * Job is implemented in class process_message_job_t + */ + PROCESS_MESSAGE, + + /** + * Retransmit an IKEv2-Message. + * + * Job is implemented in class retransmit_job_t + */ + RETRANSMIT, + + /** + * Set up a CHILD_SA, optional with an IKE_SA. + * + * Job is implemented in class initiate_job_t + */ + INITIATE, + + /** + * Install SPD entries. + * + * Job is implemented in class route_job_t + */ + ROUTE, + + /** + * React on a acquire message from the kernel (e.g. setup CHILD_SA) + * + * Job is implemented in class acquire_job_t + */ + ACQUIRE, + + /** + * Delete an IKE_SA. + * + * Job is implemented in class delete_ike_sa_job_t + */ + DELETE_IKE_SA, + + /** + * Delete a CHILD_SA. + * + * Job is implemented in class delete_child_sa_job_t + */ + DELETE_CHILD_SA, + + /** + * Rekey a CHILD_SA. + * + * Job is implemented in class rekey_child_sa_job_t + */ + REKEY_CHILD_SA, + + /** + * Rekey an IKE_SA. + * + * Job is implemented in class rekey_ike_sa_job_t + */ + REKEY_IKE_SA, + + /** + * Send a keepalive packet. + * + * Job is implemented in class type send_keepalive_job_t + */ + SEND_KEEPALIVE, + + /** + * Send a DPD packet. + * + * Job is implemented in class type send_dpd_job_t + */ + SEND_DPD +}; + +/** + * enum name for job_type_t + * + * @ingroup jobs + */ +extern enum_name_t *job_type_names; + + +/** + * @brief Job-Interface as it is stored in the job queue. + * + * A job consists of a job-type and one or more assigned values. + * + * @b Constructors: + * - None, use specific implementation of the interface. + * + * @ingroup jobs + */ +struct job_t { + + /** + * @brief get type of job. + * + * @param this calling object + * @return type of this job + */ + job_type_t (*get_type) (job_t *this); + + /** + * @brief Execute a job. + * + * Call the internall job routine to process the + * job. If this method returns DESTROY_ME, the job + * must be destroyed by the caller. + * + * @param this calling object + * @return status of job execution + */ + status_t (*execute) (job_t *this); + + /** + * @brief Destroys a job_t object + * + * @param job_t calling object + */ + void (*destroy) (job_t *job); +}; + + +#endif /* JOB_H_ */ diff --git a/src/charon/queues/jobs/process_message_job.c b/src/charon/queues/jobs/process_message_job.c new file mode 100644 index 000000000..ee7484bbd --- /dev/null +++ b/src/charon/queues/jobs/process_message_job.c @@ -0,0 +1,106 @@ +/** + * @file process_message_job.h + * + * @brief Implementation of process_message_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "process_message_job.h" + +#include + +typedef struct private_process_message_job_t private_process_message_job_t; + +/** + * Private data of an process_message_job_t Object + */ +struct private_process_message_job_t { + /** + * public process_message_job_t interface + */ + process_message_job_t public; + + /** + * Message associated with this job + */ + message_t *message; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_process_message_job_t *this) +{ + return PROCESS_MESSAGE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_process_message_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_message(charon->ike_sa_manager, + this->message); + if (ike_sa) + { + DBG1(DBG_NET, "received packet: from %#H to %#H", + this->message->get_source(this->message), + this->message->get_destination(this->message)); + if (ike_sa->process_message(ike_sa, this->message) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_process_message_job_t *this) +{ + this->message->destroy(this->message); + free(this); +} + +/* + * Described in header + */ +process_message_job_t *process_message_job_create(message_t *message) +{ + private_process_message_job_t *this = malloc_thing(private_process_message_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void(*)(job_t*))destroy; + + /* private variables */ + this->message = message; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/process_message_job.h b/src/charon/queues/jobs/process_message_job.h new file mode 100644 index 000000000..2e60a298c --- /dev/null +++ b/src/charon/queues/jobs/process_message_job.h @@ -0,0 +1,58 @@ +/** + * @file process_message_job.h + * + * @brief Interface of process_message_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PROCESS_MESSAGE_JOB_H_ +#define PROCESS_MESSAGE_JOB_H_ + +typedef struct process_message_job_t process_message_job_t; + +#include +#include +#include + +/** + * @brief Class representing an PROCESS_MESSAGE job. + * + * @b Constructors: + * - process_message_job_create() + * + * @ingroup jobs + */ +struct process_message_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type PROCESS_MESSAGE. + * + * @param message message to process + * @return created process_message_job_t object + * + * @ingroup jobs + */ +process_message_job_t *process_message_job_create(message_t *message); + +#endif /*PROCESS_MESSAGE_JOB_H_*/ diff --git a/src/charon/queues/jobs/rekey_child_sa_job.c b/src/charon/queues/jobs/rekey_child_sa_job.c new file mode 100644 index 000000000..3422b614d --- /dev/null +++ b/src/charon/queues/jobs/rekey_child_sa_job.c @@ -0,0 +1,112 @@ +/** + * @file rekey_child_sa_job.c + * + * @brief Implementation of rekey_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "rekey_child_sa_job.h" + +#include + + +typedef struct private_rekey_child_sa_job_t private_rekey_child_sa_job_t; + +/** + * Private data of an rekey_child_sa_job_t object. + */ +struct private_rekey_child_sa_job_t { + /** + * Public rekey_child_sa_job_t interface. + */ + rekey_child_sa_job_t public; + + /** + * reqid of the child to rekey + */ + u_int32_t reqid; + + /** + * protocol of the CHILD_SA (ESP/AH) + */ + protocol_id_t protocol; + + /** + * inbound SPI of the CHILD_SA + */ + u_int32_t spi; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_rekey_child_sa_job_t *this) +{ + return REKEY_CHILD_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_rekey_child_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + if (ike_sa == NULL) + { + DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for rekeying", + this->reqid); + return DESTROY_ME; + } + ike_sa->rekey_child_sa(ike_sa, this->protocol, this->spi); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_rekey_child_sa_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi) +{ + private_rekey_child_sa_job_t *this = malloc_thing(private_rekey_child_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + this->protocol = protocol; + this->spi = spi; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/rekey_child_sa_job.h b/src/charon/queues/jobs/rekey_child_sa_job.h new file mode 100644 index 000000000..19e1b5d32 --- /dev/null +++ b/src/charon/queues/jobs/rekey_child_sa_job.h @@ -0,0 +1,65 @@ +/** + * @file rekey_child_sa_job.h + * + * @brief Interface of rekey_child_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef REKEY_CHILD_SA_JOB_H_ +#define REKEY_CHILD_SA_JOB_H_ + +typedef struct rekey_child_sa_job_t rekey_child_sa_job_t; + +#include +#include +#include +#include + +/** + * @brief Class representing an REKEY_CHILD_SA Job. + * + * This job initiates the rekeying of a CHILD SA. + * + * @b Constructors: + * - rekey_child_sa_job_create() + * + * @ingroup jobs + */ +struct rekey_child_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type REKEY_CHILD_SA. + * + * The CHILD_SA is identified by its protocol (AH/ESP) and its + * inbound SPI. + * + * @param reqid reqid of the CHILD_SA to rekey + * @param protocol protocol of the CHILD_SA + * @param spi security parameter index of the CHILD_SA + * @return rekey_child_sa_job_t object + * + * @ingroup jobs + */ +rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, protocol_id_t protocol, u_int32_t spi); + +#endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/rekey_ike_sa_job.c b/src/charon/queues/jobs/rekey_ike_sa_job.c new file mode 100644 index 000000000..2539d997e --- /dev/null +++ b/src/charon/queues/jobs/rekey_ike_sa_job.c @@ -0,0 +1,120 @@ +/** + * @file rekey_ike_sa_job.c + * + * @brief Implementation of rekey_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "rekey_ike_sa_job.h" + +#include + + +typedef struct private_rekey_ike_sa_job_t private_rekey_ike_sa_job_t; + +/** + * Private data of an rekey_ike_sa_job_t object. + */ +struct private_rekey_ike_sa_job_t { + /** + * Public rekey_ike_sa_job_t interface. + */ + rekey_ike_sa_job_t public; + + /** + * ID of the IKE_SA to rekey + */ + ike_sa_id_t *ike_sa_id; + + /** + * force reauthentication of the peer (full IKE_SA setup) + */ + bool reauth; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_rekey_ike_sa_job_t *this) +{ + return REKEY_IKE_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_rekey_ike_sa_job_t *this) +{ + ike_sa_t *ike_sa; + status_t status = SUCCESS; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa == NULL) + { + DBG2(DBG_JOB, "IKE_SA %J to rekey not found", this->ike_sa_id); + return DESTROY_ME; + } + + if (this->reauth) + { + ike_sa->reestablish(ike_sa); + } + else + { + status = ike_sa->rekey(ike_sa); + } + + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_rekey_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id, bool reauth) +{ + private_rekey_ike_sa_job_t *this = malloc_thing(private_rekey_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->reauth = reauth; + + return &(this->public); +} diff --git a/src/charon/queues/jobs/rekey_ike_sa_job.h b/src/charon/queues/jobs/rekey_ike_sa_job.h new file mode 100644 index 000000000..f3e336fb3 --- /dev/null +++ b/src/charon/queues/jobs/rekey_ike_sa_job.h @@ -0,0 +1,60 @@ +/** + * @file rekey_ike_sa_job.h + * + * @brief Interface of rekey_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef REKEY_IKE_SA_JOB_H_ +#define REKEY_IKE_SA_JOB_H_ + +typedef struct rekey_ike_sa_job_t rekey_ike_sa_job_t; + +#include +#include +#include + +/** + * @brief Class representing an REKEY_IKE_SA Job. + * + * This job initiates the rekeying of an IKE_SA. + * + * @b Constructors: + * - rekey_ike_sa_job_create() + * + * @ingroup jobs + */ +struct rekey_ike_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type REKEY_IKE_SA. + * + * @param ike_sa_id ID of the IKE_SA to rekey + * @param reauth TRUE to reauthenticate peer, FALSE for rekeying only + * @return rekey_ike_sa_job_t object + * + * @ingroup jobs + */ +rekey_ike_sa_job_t *rekey_ike_sa_job_create(ike_sa_id_t *ike_sa_id, bool reauth); + +#endif /* REKEY_IKE_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/retransmit_job.c b/src/charon/queues/jobs/retransmit_job.c new file mode 100644 index 000000000..5bfa20dfd --- /dev/null +++ b/src/charon/queues/jobs/retransmit_job.c @@ -0,0 +1,109 @@ +/** + * @file retransmit_job.c + * + * @brief Implementation of retransmit_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "retransmit_job.h" + +#include + +typedef struct private_retransmit_job_t private_retransmit_job_t; + +/** + * Private data of an retransmit_job_t Object. + */ +struct private_retransmit_job_t { + /** + * Public retransmit_job_t interface. + */ + retransmit_job_t public; + + /** + * Message ID of the request to resend. + */ + u_int32_t message_id; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_retransmit_job_t *this) +{ + return RETRANSMIT; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_retransmit_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) + { + if (ike_sa->retransmit(ike_sa, this->message_id) == DESTROY_ME) + { + /* retransmitted to many times, giving up */ + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_retransmit_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header. + */ +retransmit_job_t *retransmit_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id) +{ + private_retransmit_job_t *this = malloc_thing(private_retransmit_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->message_id = message_id; + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &this->public; +} diff --git a/src/charon/queues/jobs/retransmit_job.h b/src/charon/queues/jobs/retransmit_job.h new file mode 100644 index 000000000..19e29b909 --- /dev/null +++ b/src/charon/queues/jobs/retransmit_job.h @@ -0,0 +1,64 @@ +/** + * @file retransmit_job.h + * + * @brief Interface of retransmit_job_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef RETRANSMIT_JOB_H_ +#define RETRANSMIT_JOB_H_ + +typedef struct retransmit_job_t retransmit_job_t; + +#include +#include +#include + +/** + * @brief Class representing an retransmit Job. + * + * This job is scheduled every time a request is sent over the + * wire. If the response to the request is not received at schedule + * time, the retransmission will be initiated. + * + * @b Constructors: + * - retransmit_job_create() + * + * @ingroup jobs + */ +struct retransmit_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type retransmit. + * + * @param message_id message_id of the request to resend + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t + * @return retransmit_job_t object + * + * @ingroup jobs + */ +retransmit_job_t *retransmit_job_create(u_int32_t message_id, + ike_sa_id_t *ike_sa_id); + +#endif /* RETRANSMIT_JOB_H_ */ diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c new file mode 100644 index 000000000..bb6281dcc --- /dev/null +++ b/src/charon/queues/jobs/route_job.c @@ -0,0 +1,125 @@ +/** + * @file route_job.c + * + * @brief Implementation of route_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include + +#include "route_job.h" + +#include + +typedef struct private_route_job_t private_route_job_t; + +/** + * Private data of an route_job_t Object + */ +struct private_route_job_t { + /** + * public route_job_t interface + */ + route_job_t public; + + /** + * associated connection to route + */ + connection_t *connection; + + /** + * associated policy to route + */ + policy_t *policy; + + /** + * route or unroute? + */ + bool route; +}; + +/** + * Implements route_job_t.get_type. + */ +static job_type_t get_type(private_route_job_t *this) +{ + return ROUTE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_route_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); + if (this->route) + { + if (ike_sa->route(ike_sa, this->connection, this->policy) != SUCCESS) + { + DBG1(DBG_JOB, "routing failed"); + } + } + else + { + if (ike_sa->unroute(ike_sa, this->policy) == DESTROY_ME) + { + DBG1(DBG_JOB, "removing IKE_SA, as last routed CHILD_SA unrouted"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_route_job_t *this) +{ + this->connection->destroy(this->connection); + this->policy->destroy(this->policy); + free(this); +} + +/* + * Described in header + */ +route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route) +{ + private_route_job_t *this = malloc_thing(private_route_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->connection = connection; + this->policy = policy; + this->route = route; + + return &this->public; +} diff --git a/src/charon/queues/jobs/route_job.h b/src/charon/queues/jobs/route_job.h new file mode 100644 index 000000000..2743a70ab --- /dev/null +++ b/src/charon/queues/jobs/route_job.h @@ -0,0 +1,59 @@ +/** + * @file route_job.h + * + * @brief Interface of route_job_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef ROUTE_JOB_H_ +#define ROUTE_JOB_H_ + +typedef struct route_job_t route_job_t; + +#include +#include +#include +#include + +/** + * @brief Class representing an ROUTE Job. + * + * @b Constructors: + * - route_job_create() + * + * @ingroup jobs + */ +struct route_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type ROUTE. + * + * @param connection connection used for routing + * @param policy policy to set up + * @param route TRUE to route, FALSE to unroute + * @return route_job_t object + * + * @ingroup jobs + */ +route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route); + +#endif /*ROUTE_JOB_H_*/ diff --git a/src/charon/queues/jobs/send_dpd_job.c b/src/charon/queues/jobs/send_dpd_job.c new file mode 100644 index 000000000..7294d78d5 --- /dev/null +++ b/src/charon/queues/jobs/send_dpd_job.c @@ -0,0 +1,110 @@ +/** + * @file send_dpd_job.c + * + * @brief Implementation of send_dpd_job_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include + +#include "send_dpd_job.h" + +#include +#include + + +typedef struct private_send_dpd_job_t private_send_dpd_job_t; + +/** + * Private data of an send_dpd_job_t Object + */ +struct private_send_dpd_job_t { + /** + * public send_dpd_job_t interface + */ + send_dpd_job_t public; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements send_dpd_job_t.get_type. + */ +static job_type_t get_type(private_send_dpd_job_t *this) +{ + return SEND_DPD; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_send_dpd_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa == NULL) + { + return DESTROY_ME; + } + + if (ike_sa->send_dpd(ike_sa) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_send_dpd_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +send_dpd_job_t *send_dpd_job_create(ike_sa_id_t *ike_sa_id) +{ + private_send_dpd_job_t *this = malloc_thing(private_send_dpd_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + + /* public functions */ + this->public.destroy = (void (*)(send_dpd_job_t *)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/queues/jobs/send_dpd_job.h b/src/charon/queues/jobs/send_dpd_job.h new file mode 100644 index 000000000..f3900f9a2 --- /dev/null +++ b/src/charon/queues/jobs/send_dpd_job.h @@ -0,0 +1,68 @@ +/** + * @file send_dpd_job.h + * + * @brief Interface of send_dpd_job_t. + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SEND_DPD_JOB_H_ +#define SEND_DPD_JOB_H_ + +typedef struct send_dpd_job_t send_dpd_job_t; + +#include +#include +#include +#include + +/** + * @brief Class representing a SEND_DPD Job. + * + * Job to periodically send a Dead Peer Detection (DPD) request, + * ie. an IKE request with no payloads other than the encrypted payload + * required by the syntax. + * + * @b Constructors: + * - send_dpd_job_create() + * + * @ingroup jobs + */ +struct send_dpd_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Destroys an send_dpd_job_t object. + * + * @param this send_dpd_job_t object to destroy + */ + void (*destroy) (send_dpd_job_t *this); +}; + +/** + * @brief Creates a job of type SEND_DPD. + * + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +send_dpd_job_t *send_dpd_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*SEND_DPD_JOB_H_*/ diff --git a/src/charon/queues/jobs/send_keepalive_job.c b/src/charon/queues/jobs/send_keepalive_job.c new file mode 100644 index 000000000..1c1cb288e --- /dev/null +++ b/src/charon/queues/jobs/send_keepalive_job.c @@ -0,0 +1,103 @@ +/** + * @file send_keepalive_job.c + * + * @brief Implementation of send_keepalive_job_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include + +#include "send_keepalive_job.h" + +#include +#include + + +typedef struct private_send_keepalive_job_t private_send_keepalive_job_t; + +/** + * Private data of an send_keepalive_job_t Object + */ +struct private_send_keepalive_job_t { + /** + * public send_keepalive_job_t interface + */ + send_keepalive_job_t public; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements send_keepalive_job_t.get_type. + */ +static job_type_t get_type(private_send_keepalive_job_t *this) +{ + return SEND_KEEPALIVE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_send_keepalive_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa == NULL) + { + return DESTROY_ME; + } + ike_sa->send_keepalive(ike_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_send_keepalive_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +send_keepalive_job_t *send_keepalive_job_create(ike_sa_id_t *ike_sa_id) +{ + private_send_keepalive_job_t *this = malloc_thing(private_send_keepalive_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + + /* public functions */ + this->public.destroy = (void (*)(send_keepalive_job_t *)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/queues/jobs/send_keepalive_job.h b/src/charon/queues/jobs/send_keepalive_job.h new file mode 100644 index 000000000..c7d05be65 --- /dev/null +++ b/src/charon/queues/jobs/send_keepalive_job.h @@ -0,0 +1,67 @@ +/** + * @file send_keepalive_job.h + * + * @brief Interface of send_keepalive_job_t. + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SEND_KEEPALIVE_JOB_H_ +#define SEND_KEEPALIVE_JOB_H_ + +typedef struct send_keepalive_job_t send_keepalive_job_t; + +#include +#include +#include +#include + +/** + * @brief Class representing a SEND_KEEPALIVE Job. + * + * This job will send a NAT keepalive packet if the IKE SA is still alive, + * and reinsert itself into the event queue. + * + * @b Constructors: + * - send_keepalive_job_create() + * + * @ingroup jobs + */ +struct send_keepalive_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Destroys an send_keepalive_job_t object. + * + * @param this send_keepalive_job_t object to destroy + */ + void (*destroy) (send_keepalive_job_t *this); +}; + +/** + * @brief Creates a job of type SEND_KEEPALIVE. + * + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +send_keepalive_job_t *send_keepalive_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*SEND_KEEPALIVE_JOB_H_*/ diff --git a/src/charon/sa/authenticators/authenticator.c b/src/charon/sa/authenticators/authenticator.c new file mode 100644 index 000000000..707aae9ad --- /dev/null +++ b/src/charon/sa/authenticators/authenticator.c @@ -0,0 +1,56 @@ +/** + * @file authenticator.c + * + * @brief Generic constructor for authenticators. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "authenticator.h" + +#include +#include +#include + + +ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS, + "RSA signature", + "pre-shared key", + "DSS signature"); +ENUM_NEXT(auth_method_names, AUTH_EAP, AUTH_EAP, AUTH_DSS, + "EAP"); +ENUM_END(auth_method_names, AUTH_EAP); + +/* + * Described in header. + */ +authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method) +{ + switch (auth_method) + { + case AUTH_RSA: + return (authenticator_t*)rsa_authenticator_create(ike_sa); + case AUTH_PSK: + return (authenticator_t*)psk_authenticator_create(ike_sa); + case AUTH_EAP: + return (authenticator_t*)eap_authenticator_create(ike_sa); + default: + return NULL; + } +} diff --git a/src/charon/sa/authenticators/authenticator.h b/src/charon/sa/authenticators/authenticator.h new file mode 100644 index 000000000..c7b0fc81a --- /dev/null +++ b/src/charon/sa/authenticators/authenticator.h @@ -0,0 +1,139 @@ +/** + * @file authenticator.h + * + * @brief Interface of authenticator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef AUTHENTICATOR_H_ +#define AUTHENTICATOR_H_ + +typedef enum auth_method_t auth_method_t; +typedef struct authenticator_t authenticator_t; + +#include +#include +#include + +/** + * Method to use for authentication. + * + * @ingroup authenticators + */ +enum auth_method_t { + /** + * Computed as specified in section 2.15 of RFC using + * an RSA private key over a PKCS#1 padded hash. + */ + AUTH_RSA = 1, + + /** + * Computed as specified in section 2.15 of RFC using the + * shared key associated with the identity in the ID payload + * and the negotiated prf function + */ + AUTH_PSK = 2, + + /** + * Computed as specified in section 2.15 of RFC using a + * DSS private key over a SHA-1 hash. + */ + AUTH_DSS = 3, + + /** + * EAP authentication. This value is never negotiated and therefore + * a value from private use. + */ + AUTH_EAP = 201, +}; + +/** + * enum names for auth_method_t. + * + * @ingroup authenticators + */ +extern enum_name_t *auth_method_names; + +/** + * @brief Authenticator interface implemented by the various authenticators. + * + * Currently the following two AUTH methods are supported: + * - shared key message integrity code (AUTH_PSK) + * - RSA digital signature (AUTH_RSA) + * + * @b Constructors: + * - authenticator_create() + * + * @ingroup authenticators + */ +struct authenticator_t { + + /** + * @brief Verify a received authentication payload. + * + * @param this calling object + * @param ike_sa_init binary representation of received ike_sa_init + * @param my_nonce the sent nonce + * @param auth_payload authentication payload to verify + * + * @return + * - SUCCESS, + * - FAILED if verification failed + * - INVALID_ARG if auth_method does not match + * - NOT_FOUND if credentials not found + */ + status_t (*verify) (authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload); + + /** + * @brief Build an authentication payload to send to the other peer. + * + * @param this calling object + * @param ike_sa_init binary representation of sent ike_sa_init + * @param other_nonce the received nonce + * @param[out] auth_payload the resulting authentication payload + * + * @return + * - SUCCESS, + * - NOT_FOUND if the data for AUTH method could not be found + */ + status_t (*build) (authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload); + + /** + * @brief Destroys a authenticator_t object. + * + * @param this calling object + */ + void (*destroy) (authenticator_t *this); +}; + +/** + * @brief Creates an authenticator for the specified auth method. + * + * @param ike_sa associated ike_sa + * @param auth_method authentication method to use for build()/verify() + * + * @return authenticator_t object + * + * @ingroup authenticators + */ +authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method); + +#endif /* AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_identity.c b/src/charon/sa/authenticators/eap/eap_identity.c new file mode 100644 index 000000000..12a8bf7cc --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_identity.c @@ -0,0 +1,135 @@ +/** + * @file eap_identity.c + * + * @brief Implementation of eap_identity_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "eap_identity.h" + +#include +#include + +typedef struct private_eap_identity_t private_eap_identity_t; + +/** + * Private data of an eap_identity_t object. + */ +struct private_eap_identity_t { + + /** + * Public authenticator_t interface. + */ + eap_identity_t public; + + /** + * ID of the peer + */ + identification_t *peer; +}; + +/** + * Implementation of eap_method_t.process for the peer + */ +static status_t process(private_eap_identity_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t id, hdr; + + hdr = chunk_alloca(5); + id = this->peer->get_encoding(this->peer); + + *(hdr.ptr + 0) = EAP_RESPONSE; + *(hdr.ptr + 1) = in->get_identifier(in); + *(u_int16_t*)(hdr.ptr + 2) = htons(hdr.len + id.len); + *(hdr.ptr + 4) = EAP_IDENTITY; + + *out = eap_payload_create_data(chunk_cata("cc", hdr, id)); + return SUCCESS; + +} + +/** + * Implementation of eap_method_t.initiate for the peer + */ +static status_t initiate(private_eap_identity_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_identity_t *this) +{ + return EAP_IDENTITY; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_identity_t *this, chunk_t *msk) +{ + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_identity_t *this) +{ + return FALSE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_identity_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +eap_identity_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer) +{ + private_eap_identity_t *this; + + if (role != EAP_PEER) + { + return NULL; + } + + this = malloc_thing(private_eap_identity_t); + + /* public functions */ + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type; + this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; + + /* private data */ + this->peer = peer; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap/eap_identity.h b/src/charon/sa/authenticators/eap/eap_identity.h new file mode 100644 index 000000000..20f0f0b67 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_identity.h @@ -0,0 +1,59 @@ +/** + * @file eap_identity.h + * + * @brief Interface of eap_identity_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef EAP_IDENTITY_H_ +#define EAP_IDENTITY_H_ + +typedef struct eap_identity_t eap_identity_t; + +#include + +/** + * @brief Implementation of the eap_method_t interface using EAP Identity. + * + * @b Constructors: + * - eap_identity_create() + * - eap_client_create() using eap_method EAP_IDENTITY + * + * @ingroup eap + */ +struct eap_identity_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t eap_method_interface; +}; + +/** + * @brief Creates the EAP method EAP Identity. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_identity_t object + * + * @ingroup eap + */ +eap_identity_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer); + +#endif /* EAP_IDENTITY_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c new file mode 100644 index 000000000..a4d8abb58 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -0,0 +1,245 @@ +/** + * @file eap_method.c + * + * @brief Generic constructor for eap_methods. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "eap_method.h" + +#include +#include +#include +#include + + +ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_TOKEN_CARD, + "EAP_IDENTITY", + "EAP_NOTIFICATION", + "EAP_NAK", + "EAP_MD5", + "EAP_ONE_TIME_PASSWORD", + "EAP_TOKEN_CARD"); +ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_TOKEN_CARD, + "EAP_SIM"); +ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM, + "EAP_AKA"); +ENUM_END(eap_type_names, EAP_AKA); + +ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE, + "EAP_REQUEST", + "EAP_RESPONSE", + "EAP_SUCCESS", + "EAP_FAILURE", +); + +ENUM(eap_role_names, EAP_SERVER, EAP_PEER, + "EAP_SERVER", + "EAP_PEER", +); + + +typedef struct module_entry_t module_entry_t; + +/** + * Representation of a loaded module: EAP type, library handle, constructor + */ +struct module_entry_t { + eap_type_t type; + void *handle; + eap_constructor_t constructor; +}; + +/** List of module_entry_t's */ +static linked_list_t *modules = NULL; + +/** + * unload modules at daemon shutdown + */ +void eap_method_unload() +{ + if (modules) + { + module_entry_t *entry; + + while (modules->remove_last(modules, (void**)&entry) == SUCCESS) + { + DBG2(DBG_CFG, "unloaded module for %s", eap_type_names, entry->type); + dlclose(entry->handle); + free(entry); + } + modules->destroy(modules); + modules = NULL; + } +} + +/** + * Load EAP modules at daemon startup + */ +void eap_method_load(char *directory) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + + eap_method_unload(); + modules = linked_list_create(); + + if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR)) + { + DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); + return; + } + if (stb.st_uid != 0) + { + DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory); + return; + } + if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) + { + DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory); + return; + } + + dir = opendir(directory); + if (dir == NULL) + { + DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); + return; + } + + DBG1(DBG_CFG, "loading EAP modules from '%s'", directory); + + while ((entry = readdir(dir)) != NULL) + { + char file[256]; + module_entry_t module, *loaded_module; + eap_method_t *method; + identification_t *id; + char *ending; + + snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name); + + if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG)) + { + DBG2(DBG_CFG, " skipping %s, doesn't look like a file", + entry->d_name); + continue; + } + ending = entry->d_name + strlen(entry->d_name) - 3; + if (ending <= entry->d_name || !streq(ending, ".so")) + { + /* skip anything which does not look like a library */ + DBG2(DBG_CFG, " skipping %s, doesn't look like a library", + entry->d_name); + continue; + } + if (stb.st_uid != 0) + { + DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name); + return; + } + if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) + { + DBG1(DBG_CFG, " skipping %s, file is writeable by others", entry->d_name); + continue; + } + + /* try to load the library */ + module.handle = dlopen(file, RTLD_LAZY); + if (module.handle == NULL) + { + DBG1(DBG_CFG, " opening EAP module %s failed: %s", entry->d_name, + dlerror()); + continue; + } + module.constructor = dlsym(module.handle, "eap_create"); + if (module.constructor == NULL) + { + DBG1(DBG_CFG, " EAP module %s has no eap_create() function, skipped", + entry->d_name); + dlclose(module.handle); + continue; + } + + /* get the type implemented in the method, create an instance for it */ + id = identification_create_from_string("john@doe.xyz"); + method = module.constructor(EAP_SERVER, id, id); + if (method == NULL) + { + method = module.constructor(EAP_PEER, id, id); + } + id->destroy(id); + if (method == NULL) + { + DBG1(DBG_CFG, " unable to create instance of EAP method %s, skipped", + entry->d_name); + dlclose(module.handle); + continue; + } + module.type = method->get_type(method); + method->destroy(method); + + DBG1(DBG_CFG, " loaded EAP method %N successfully from %s", + eap_type_names, module.type, entry->d_name); + + loaded_module = malloc_thing(module_entry_t); + memcpy(loaded_module, &module, sizeof(module)); + modules->insert_last(modules, loaded_module); + } + closedir(dir); +} + +/* + * Described in header. + */ +eap_method_t *eap_method_create(eap_type_t type, eap_role_t role, + identification_t *server, + identification_t *peer) +{ + eap_method_t *method = NULL; + iterator_t *iterator; + module_entry_t *entry; + + iterator = modules->create_iterator(modules, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (entry->type == type) + { + method = entry->constructor(role, server, peer); + if (method) + { + break; + } + } + } + iterator->destroy(iterator); + + if (method == NULL) + { + DBG1(DBG_CFG, "no EAP module found for %N %N", + eap_type_names, type, eap_role_names, role); + } + return method; +} diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h new file mode 100644 index 000000000..d43dc001f --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -0,0 +1,242 @@ +/** + * @file eap_method.h + * + * @brief Interface eap_method_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef EAP_METHOD_H_ +#define EAP_METHOD_H_ + +typedef struct eap_method_t eap_method_t; +typedef enum eap_role_t eap_role_t; +typedef enum eap_type_t eap_type_t; +typedef enum eap_code_t eap_code_t; + +#include +#include +#include + +/** + * Role of an eap_method, SERVER or PEER (client) + * + * @ingroup eap + */ +enum eap_role_t { + EAP_SERVER, + EAP_PEER, +}; +/** + * enum names for eap_role_t. + * + * @ingroup eap + */ +extern enum_name_t *eap_role_names; + +/** + * EAP types, defines the EAP method implementation + * + * @ingroup eap + */ +enum eap_type_t { + EAP_IDENTITY = 1, + EAP_NOTIFICATION = 2, + EAP_NAK = 3, + EAP_MD5 = 4, + EAP_ONE_TIME_PASSWORD = 5, + EAP_TOKEN_CARD = 6, + EAP_SIM = 18, + EAP_AKA = 23, +}; + +/** + * enum names for eap_type_t. + * + * @ingroup eap + */ +extern enum_name_t *eap_type_names; + +/** + * EAP code, type of an EAP message + * + * @ingroup eap + */ +enum eap_code_t { + EAP_REQUEST = 1, + EAP_RESPONSE = 2, + EAP_SUCCESS = 3, + EAP_FAILURE = 4, +}; + +/** + * enum names for eap_code_t. + * + * @ingroup eap + */ +extern enum_name_t *eap_code_names; + + +/** + * @brief Interface of an EAP method for server and client side. + * + * An EAP method initiates an EAP exchange and processes requests and + * responses. An EAP method may need multiple exchanges before succeeding, and + * the eap_authentication may use multiple EAP methods to authenticate a peer. + * To accomplish these requirements, all EAP methods have their own + * implementation while the eap_authenticatior uses one or more of these + * EAP methods. Sending of EAP(SUCCESS/FAILURE) message is not the job + * of the method, the eap_authenticator does this. + * An EAP method may establish a MSK, this is used the complete the + * authentication. Even if a mutual EAP method is used, the traditional + * AUTH payloads are required. Only these include the nonces and messages from + * ike_sa_init and therefore prevent man in the middle attacks. + * + * @b Constructors: + * - eap_method_create() + * + * @ingroup eap + */ +struct eap_method_t { + + /** + * @brief Initiate the EAP exchange. + * + * initiate() is only useable for server implementations, as clients only + * reply to server requests. + * A eap_payload is created in "out" if result is NEED_MORE. + * + * @param this calling object + * @param out eap_payload to send to the client + * @return + * - NEED_MORE, if an other exchange is required + * - FAILED, if unable to create eap request payload + */ + status_t (*initiate) (eap_method_t *this, eap_payload_t **out); + + /** + * @brief Process a received EAP message. + * + * A eap_payload is created in "out" if result is NEED_MORE. + * + * @param this calling object + * @param in eap_payload response received + * @param out created eap_payload to send + * @return + * - NEED_MORE, if an other exchange is required + * - FAILED, if EAP method failed + * - SUCCESS, if EAP method succeeded + */ + status_t (*process) (eap_method_t *this, eap_payload_t *in, + eap_payload_t **out); + + /** + * @brief Get the EAP type implemented in this method. + * + * @param this calling object + * @return type of the EAP method + */ + eap_type_t (*get_type) (eap_method_t *this); + + /** + * @brief Check if this EAP method authenticates the server. + * + * Some EAP methods provide mutual authentication and + * allow authentication using only EAP, if the peer supports it. + * + * @param this calling object + * @return TRUE if methods provides mutual authentication + */ + bool (*is_mutual) (eap_method_t *this); + + /** + * @brief Get the MSK established by this EAP method. + * + * Not all EAP methods establish a shared secret. + * + * @param this calling object + * @param msk chunk receiving internal stored MSK + * @return + * - SUCCESS, or + * - FAILED, if MSK not established (yet) + */ + status_t (*get_msk) (eap_method_t *this, chunk_t *msk); + + /** + * @brief Destroys a eap_method_t object. + * + * @param this calling object + */ + void (*destroy) (eap_method_t *this); +}; + +/** + * @brief Creates an EAP method for a specific type and role. + * + * @param eap_type EAP type to use + * @param role role of the eap_method, server or peer + * @param server ID of acting server + * @param peer ID of involved peer (client) + * @return eap_method_t object + * + * @ingroup eap + */ +eap_method_t *eap_method_create(eap_type_t eap_type, eap_role_t role, + identification_t *server, identification_t *peer); + +/** + * @brief (Re-)Load all EAP modules in the EAP modules directory. + * + * For security reasons, the directory and all it's modules must be owned + * by root and must not be writeable by someone else. + * + * @param dir directory of the EAP modules + * + * @ingroup eap + */ +void eap_method_load(char *directory); + +/** + * @brief Unload all loaded EAP modules + * + * @ingroup eap + */ +void eap_method_unload(); + +/** + * @brief Constructor definition for a pluggable EAP module. + * + * Each EAP module must define a constructor function which will return + * an initialized object with the methods defined in eap_method_t. The + * constructor must be named eap_create() and it's signature must be equal + * to that of eap_constructor_t. + * A module may implement only a single role. If it does not support the role + * requested, NULL should be returned. Multiple modules are allowed of the + * same EAP type to support seperate implementations of peer/server. + * + * @param role role the module will play, peer or server + * @param server ID of the server to use for credential lookup + * @param peer ID of the peer to use for credential lookup + * @return implementation of the eap_method_t interface + * + * @ingroup eap + */ +typedef eap_method_t *(*eap_constructor_t)(eap_role_t role, + identification_t *server, + identification_t *peer); + +#endif /* EAP_METHOD_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_sim.c b/src/charon/sa/authenticators/eap/eap_sim.c new file mode 100644 index 000000000..3dc59fb6b --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_sim.c @@ -0,0 +1,703 @@ +/** + * @file eap_sim.c + * + * @brief Implementation of eap_sim_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "eap_sim.h" + +#include + +#include +#include + +#define MAX_TRIES 3 + +ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR, + "SIM_START", + "SIM_CHALLENGE", + "SIM_NOTIFICATION", + "SIM_13", + "SIM_CLIENT_ERROR", +); + +ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE, + "AT_END", + "AT_0", + "AT_RAND", + "AT_AUTN", + "AT_RES", + "AT_AUTS", + "AT_5", + "AT_PADDING", + "AT_NONCE_MT", + "AT_8", + "AT_9", + "AT_PERMANENT_ID_REQ", + "AT_MAC", + "AT_NOTIFICATION", + "AT_ANY_ID_REQ", + "AT_IDENTITY", + "AT_VERSION_LIST", + "AT_SELECTED_VERSION", + "AT_FULLAUTH_ID_REQ", + "AT_18", + "AT_COUNTER", + "AT_COUNTER_TOO_SMALL", + "AT_NONCE_S", + "AT_CLIENT_ERROR_CODE"); +ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE, + "AT_IV", + "AT_ENCR_DATA", + "AT_131", + "AT_NEXT_PSEUDONYM", + "AT_NEXT_REAUTH_ID", + "AT_CHECKCODE", + "AT_RESULT_IND"); +ENUM_END(sim_attribute_names, AT_RESULT_IND); + + +typedef struct private_eap_sim_t private_eap_sim_t; + +/** + * Private data of an eap_sim_t object. + */ +struct private_eap_sim_t { + + /** + * Public authenticator_t interface. + */ + eap_sim_t public; + + /** + * ID of ourself + */ + identification_t *peer; + + /** + * SIM cardreader function loaded from library + */ + sim_algo_t alg; + + /** + * handle of the loaded library + */ + void *handle; + + /** + * how many times we try to authenticate + */ + int tries; + + /** + * version this implementation uses + */ + chunk_t version; + + /** + * version list received from server + */ + chunk_t version_list; + + /** + * Nonce value used in AT_NONCE_MT + */ + chunk_t nonce; + + /** + * k_encr key derived from MK + */ + chunk_t k_encr; + + /** + * k_auth key derived from MK, used for AT_MAC verification + */ + chunk_t k_auth; + + /** + * MSK, used for EAP-SIM based IKEv2 authentication + */ + chunk_t msk; + + /** + * EMSK, extendes MSK for further uses + */ + chunk_t emsk; +}; + +/** length of the AT_NONCE_MT nonce value */ +#define NONCE_LEN 16 +/** length of the AT_MAC value */ +#define MAC_LEN 16 +/** length of the AT_RAND value */ +#define RAND_LEN 16 +/** length of the k_encr key */ +#define KENCR_LEN 16 +/** length of the k_auth key */ +#define KAUTH_LEN 16 +/** length of the MSK */ +#define MSK_LEN 64 +/** length of the EMSK */ +#define EMSK_LEN 64 + +/* client error codes used in AT_CLIENT_ERROR_CODE */ +char client_error_general_buf[] = {0x00, 0x01}; +char client_error_unsupported_buf[] = {0x00, 0x02}; +char client_error_insufficient_buf[] = {0x00, 0x03}; +char client_error_notfresh_buf[] = {0x00, 0x04}; +chunk_t client_error_general = chunk_from_buf(client_error_general_buf); +chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf); +chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf); +chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf); + +/** + * Read EAP and EAP-SIM header, return SIM type + */ +static sim_subtype_t read_header(chunk_t *message) +{ + sim_subtype_t type; + + if (message->len < 8) + { + *message = chunk_empty; + return 0; + } + type = *(message->ptr + 5); + *message = chunk_skip(*message, 8); + return type; +} + +/** + * read the next attribute from the chunk data + */ +static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data) +{ + sim_attribute_t attribute; + size_t length; + + DBG3(DBG_IKE, "reading attribute from %B", message); + + if (message->len < 2) + { + return AT_END; + } + attribute = *message->ptr++; + length = *message->ptr++ * 4 - 2; + message->len -= 2; + DBG3(DBG_IKE, "found attribute %N with length %d", + sim_attribute_names, attribute, length); + + if (length > message->len) + { + return AT_END; + } + data->len = length; + data->ptr = message->ptr; + *message = chunk_skip(*message, length); + return attribute; +} + +/** + * Build an EAP-SIM payload using a variable length attribute list. + * The variable argument takes a sim_attribute_t followed by its data in a chunk. + */ +static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier, + sim_subtype_t type, ...) +{ + chunk_t message = chunk_alloca(512); + chunk_t pos = message; + eap_payload_t *payload; + va_list args; + sim_attribute_t attr; + u_int8_t *mac_pos = NULL; + chunk_t mac_data = chunk_empty; + + /* write EAP header, skip length bytes */ + *pos.ptr++ = EAP_RESPONSE; + *pos.ptr++ = identifier; + pos.ptr += 2; + pos.len -= 4; + /* write SIM header with type and subtype, zero reserved bytes */ + *pos.ptr++ = EAP_SIM; + *pos.ptr++ = type; + *pos.ptr++ = 0; + *pos.ptr++ = 0; + pos.len -= 4; + + va_start(args, type); + while ((attr = va_arg(args, sim_attribute_t)) != AT_END) + { + chunk_t data = va_arg(args, chunk_t); + + DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data); + + /* write attribute header */ + *pos.ptr++ = attr; + pos.len--; + + switch (attr) + { + case AT_CLIENT_ERROR_CODE: + case AT_SELECTED_VERSION: + { + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_IDENTITY: + { + /* align up to four byte */ + if (data.len % 4) + { + chunk_t tmp = chunk_alloca((data.len/4)*4 + 4); + memset(tmp.ptr, 0, tmp.len); + memcpy(tmp.ptr, data.ptr, data.len); + data = tmp; + } + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + /* actual length in bytes */ + *(u_int16_t*)pos.ptr = htons(data.len); + pos = chunk_skip(pos, sizeof(u_int16_t)); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_NONCE_MT: + { + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + memset(pos.ptr, 0, 2); + pos = chunk_skip(pos, 2); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_MAC: + { + *pos.ptr++ = 5; pos.len--; + *pos.ptr++ = 0; pos.len--; + *pos.ptr++ = 0; pos.len--; + mac_pos = pos.ptr; + memset(mac_pos, 0, MAC_LEN); + pos = chunk_skip(pos, MAC_LEN); + mac_data = data; + break; + } + case AT_RAND: + { + *pos.ptr++ = data.len/4 + 1; pos.len--; + *pos.ptr++ = 0; pos.len--; + *pos.ptr++ = 0; pos.len--; + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + default: + DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped", + sim_attribute_names, attr); + break; + } + } + va_end(args); + + /* calculate message length, write into header */ + message.len = pos.ptr - message.ptr; + *(u_int16_t*)(message.ptr + 2) = htons(message.len); + + /* create MAC if AT_MAC attribte was included. Append supplied va_arg + * chunk mac_data to "to-sign" chunk */ + if (mac_pos) + { + signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + mac_data = chunk_cata("cc", message, mac_data); + signer->get_signature(signer, mac_data, mac_pos); + DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b", + &mac_data, mac_pos, MAC_LEN); + signer->destroy(signer); + } + + payload = eap_payload_create_data(message); + + DBG3(DBG_IKE, "created EAP message %B", &message); + return payload; +} + +/** + * process an EAP-SIM/Request/Start message + */ +static status_t process_start(private_eap_sim_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + chunk_t message, data; + sim_attribute_t attribute, include_id = AT_END; + u_int8_t identifier; + + identifier = in->get_identifier(in); + message = in->get_data(in); + read_header(&message); + + while ((attribute = read_attribute(&message, &data)) != AT_END) + { + switch (attribute) + { + case AT_VERSION_LIST: + { + /* check if server supports our implementation */ + bool found = FALSE; + if (data.len > 2) + { + /* read actual length first */ + data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2); + data = chunk_skip(data, 2); + chunk_free(&this->version_list); + this->version_list = chunk_clone(data); + while (data.len >= this->version.len) + { + if (memeq(data.ptr, this->version.ptr, this->version.len)) + { + found = TRUE; + break; + } + data = chunk_skip(data, this->version.len); + } + } + if (!found) + { + DBG1(DBG_IKE, "server does not support EAP_SIM " + "version number %#B", &this->version); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_unsupported, + AT_END); + return NEED_MORE; + } + break; + } + case AT_PERMANENT_ID_REQ: + case AT_FULLAUTH_ID_REQ: + case AT_ANY_ID_REQ: + /* only include AT_IDENTITY if requested */ + include_id = AT_IDENTITY; + break; + default: + DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", + sim_attribute_names, attribute); + break; + } + } + + /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */ + *out = build_payload(this, identifier, SIM_START, + AT_SELECTED_VERSION, this->version, + AT_NONCE_MT, this->nonce, + include_id, this->peer->get_encoding(this->peer), + AT_END); + return NEED_MORE; +} + +/** + * process an EAP-SIM/Request/Challenge message + */ +static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + chunk_t message, data, tmp, kcs, kc, sreses, sres, mk; + sim_attribute_t attribute; + u_int8_t identifier, i; + chunk_t mac = chunk_empty, rands = chunk_empty; + signer_t *signer; + hasher_t *hasher; + prf_t *prf; + + if (this->tries-- <= 0) + { + /* give up without notification. This hack is required as some buggy + * server implementations won't respect our client-error. */ + return FAILED; + } + + identifier = in->get_identifier(in); + message = in->get_data(in); + read_header(&message); + + while ((attribute = read_attribute(&message, &data)) != AT_END) + { + switch (attribute) + { + case AT_RAND: + { + rands = chunk_skip(data, 2); + break; + } + case AT_MAC: + { + /* backup MAC, zero it inline for later verification */ + data = chunk_skip(data, 2); + mac = chunk_clonea(data); + memset(data.ptr, 0, data.len); + break; + } + default: + DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", + sim_attribute_names, attribute); + break; + } + } + + /* excepting two or three RAND, each 16 bytes. We require two valid + * and different RANDs */ + if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) || + memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN)) + { + DBG1(DBG_IKE, "no valid AT_RAND received"); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_insufficient, + AT_END); + return FAILED; + } + if (mac.len != MAC_LEN) + { + DBG1(DBG_IKE, "no valid AT_MAC received"); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, + AT_END); + return NEED_MORE; + } + + /* get two or three KCs/SRESes from SIM using RANDs */ + kcs = kc = chunk_alloca(rands.len / 2); + sreses = sres = chunk_alloca(rands.len / 4); + while (rands.len > 0) + { + int kc_len = kc.len, sres_len = sres.len; + + if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len)) + { + DBG1(DBG_IKE, "unable to get triplets from SIM"); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, + AT_END); + return NEED_MORE; + } + DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b", + rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len); + kc = chunk_skip(kc, kc_len); + sres = chunk_skip(sres, sres_len); + rands = chunk_skip(rands, RAND_LEN); + } + + /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs, + this->nonce, this->version_list, this->version); + hasher = hasher_create(HASH_SHA1); + mk = chunk_alloca(hasher->get_hash_size(hasher)); + hasher->get_hash(hasher, tmp, mk.ptr); + hasher->destroy(hasher); + DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk); + + /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf() + * FIPS PRF has 320 bit block size, we need 160 byte for keys + * => run prf four times */ + prf = prf_create(PRF_FIPS_SHA1_160); + prf->set_key(prf, mk); + tmp = chunk_alloca(prf->get_block_size(prf) * 4); + for (i = 0; i < 4; i++) + { + prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i); + } + prf->destroy(prf); + chunk_free(&this->k_encr); + chunk_free(&this->k_auth); + chunk_free(&this->msk); + chunk_free(&this->emsk); + chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth, + MSK_LEN, &this->msk, EMSK_LEN, &this->emsk); + DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B", + &this->k_encr, &this->k_auth, &this->msk, &this->emsk); + + /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */ + signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + tmp = chunk_cata("cc", in->get_data(in), this->nonce); + if (!signer->verify_signature(signer, tmp, mac)) + { + DBG1(DBG_IKE, "AT_MAC verification failed"); + signer->destroy(signer); + *out = build_payload(this, identifier, SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, + AT_END); + return NEED_MORE; + } + signer->destroy(signer); + + /* build response, AT_MAC is built over "EAP packet | n*SRES" */ + *out = build_payload(this, identifier, SIM_CHALLENGE, + AT_MAC, sreses, + AT_END); + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.process for the peer + */ +static status_t process(private_eap_sim_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + sim_subtype_t type; + chunk_t message; + + message = in->get_data(in); + type = read_header(&message); + + switch (type) + { + case SIM_START: + return process_start(this, in, out); + case SIM_CHALLENGE: + return process_challenge(this, in, out); + default: + DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N", + sim_subtype_names, type); + *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_general, AT_END); + return NEED_MORE; + } +} + +/** + * Implementation of eap_method_t.initiate for the peer + */ +static status_t initiate(private_eap_sim_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_sim_t *this) +{ + return EAP_SIM; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_sim_t *this, chunk_t *msk) +{ + if (this->msk.ptr) + { + *msk = this->msk; + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_sim_t *this) +{ + return TRUE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_sim_t *this) +{ + dlclose(this->handle); + chunk_free(&this->nonce); + chunk_free(&this->version_list); + chunk_free(&this->k_auth); + chunk_free(&this->k_encr); + chunk_free(&this->msk); + chunk_free(&this->emsk); + free(this); +} + +/* + * Described in header. + */ +eap_sim_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer) +{ + private_eap_sim_t *this; + randomizer_t *randomizer; + static char version[] = {0x00,0x01}; + + if (role != EAP_PEER) + { + return NULL; + } + this = malloc_thing(private_eap_sim_t); + + this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY); + if (this->handle == NULL) + { + DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB); + free(this); + return NULL; + } + this->alg = dlsym(this->handle, SIM_READER_ALG); + if (this->alg == NULL) + { + DBG1(DBG_IKE, "unable to open SIM reader function '%s' in '%s'", + SIM_READER_ALG, SIM_READER_LIB); + dlclose(this->handle); + free(this); + return NULL; + } + + randomizer = randomizer_create(); + if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN, + &this->nonce)) + { + DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM"); + randomizer->destroy(randomizer); + free(this); + return NULL; + } + randomizer->destroy(randomizer); + + /* public functions */ + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type; + this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; + + /* private data */ + this->peer = peer; + this->tries = MAX_TRIES; + this->version.ptr = version; + this->version.len = sizeof(version); + this->version_list = chunk_empty; + this->k_auth = chunk_empty; + this->k_encr = chunk_empty; + this->msk = chunk_empty; + this->emsk = chunk_empty; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap/eap_sim.h b/src/charon/sa/authenticators/eap/eap_sim.h new file mode 100644 index 000000000..10640babe --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_sim.h @@ -0,0 +1,141 @@ +/** + * @file eap_sim.h + * + * @brief Interface of eap_sim_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef EAP_SIM_H_ +#define EAP_SIM_H_ + +typedef struct eap_sim_t eap_sim_t; +typedef enum sim_subtype_t sim_subtype_t; +typedef enum sim_attribute_t sim_attribute_t; + +#include + +/** + * Subtypes of SIM messages + */ +enum sim_subtype_t { + SIM_START = 10, + SIM_CHALLENGE = 11, + SIM_NOTIFICATION = 12, + SIM_CLIENT_ERROR = 14, +}; + +/** + * enum names for sim_subtype_t + */ +extern enum_name_t *sim_subtype_names; + +enum sim_attribute_t { + /** defines the end of attribute list */ + AT_END = -1, + AT_RAND = 1, + AT_AUTN = 2, + AT_RES = 3, + AT_AUTS = 4, + AT_PADDING = 6, + AT_NONCE_MT = 7, + AT_PERMANENT_ID_REQ = 10, + AT_MAC = 11, + AT_NOTIFICATION = 12, + AT_ANY_ID_REQ = 13, + AT_IDENTITY = 14, + AT_VERSION_LIST = 15, + AT_SELECTED_VERSION = 16, + AT_FULLAUTH_ID_REQ = 17, + AT_COUNTER = 19, + AT_COUNTER_TOO_SMALL = 20, + AT_NONCE_S = 21, + AT_CLIENT_ERROR_CODE = 22, + AT_IV = 129, + AT_ENCR_DATA = 130, + AT_NEXT_PSEUDONYM = 132, + AT_NEXT_REAUTH_ID = 133, + AT_CHECKCODE = 134, + AT_RESULT_IND = 135, +}; + +/** + * enum names for sim_subtype_t + */ +extern enum_name_t *sim_attribute_names; + +/** + * @brief Cardreaders SIM function. + * + * @param rand RAND to run algo with + * @param rand_length length of value in rand + * @param sres buffer to get SRES + * @param sres_length size of buffer in sres, returns bytes written to SRES + * @param kc buffer to get Kc + * @param kc_length size of buffer in Kc, returns bytes written to Kc + * @return zero on success + */ +typedef int (*sim_algo_t)(const unsigned char *rand, int rand_length, + unsigned char *sres, int *sres_length, + unsigned char *kc, int *kc_length); + +#ifndef SIM_READER_LIB +/** the library containing the cardreader with the SIM function */ +#error SIM_READER_LIB not specified, use --with-sim-reader option +#endif /* SIM_READER_LIB */ + +#ifndef SIM_READER_ALG +/** the SIM_READER_LIB's algorithm, uses sim_algo_t signature */ +#define SIM_READER_ALG "sim_run_alg" +#endif /* SIM_READER_ALG */ + + +/** + * @brief Implementation of the eap_method_t interface using EAP-SIM. + * + * This EAP-SIM client implementation uses another pluggable library to + * access the SIM card. This module is specified using the SIM_READER_LIB + * definition. The function to run the algorithm has the sim_algo_t type and + * is named as SIM_READER_ALG is defined. + * + * @b Constructors: + * - eap_create() of this module + * - eap_client_create() using eap_method EAP_SIM + * + * @ingroup eap + */ +struct eap_sim_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t eap_method_interface; +}; + +/** + * @brief Creates the EAP method EAP-SIM. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_sim_t object + * + * @ingroup eap + */ +eap_sim_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer); + +#endif /* EAP_SIM_H_ */ diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c new file mode 100644 index 000000000..6c8ca8d8f --- /dev/null +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -0,0 +1,360 @@ +/** + * @file eap_authenticator.c + * + * @brief Implementation of eap_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "eap_authenticator.h" + +#include +#include +#include + +typedef struct private_eap_authenticator_t private_eap_authenticator_t; + +/** + * Private data of an eap_authenticator_t object. + */ +struct private_eap_authenticator_t { + + /** + * Public authenticator_t interface. + */ + eap_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * Role of this authenticator, PEER or SERVER + */ + eap_role_t role; + + /** + * Current EAP method processing + */ + eap_method_t *method; + + /** + * MSK used to build and verify auth payload + */ + chunk_t msk; +}; + +extern chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, + prf_t *prf_skp, prf_t *prf); + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + chunk_t auth_data, recv_auth_data; + identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa); + + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, this->msk, + other_id, this->ike_sa->get_auth_verify(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + + recv_auth_data = auth_payload->get_data(auth_payload); + if (!chunk_equals(auth_data, recv_auth_data)) + { + DBG1(DBG_IKE, "verification of AUTH payload created from EAP MSK failed"); + chunk_free(&auth_data); + return FAILED; + } + chunk_free(&auth_data); + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_EAP); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t auth_data; + identification_t *my_id = this->ike_sa->get_my_id(this->ike_sa); + + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_EAP); + + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, this->msk, + my_id, this->ike_sa->get_auth_build(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); + (*auth_payload)->set_data(*auth_payload, auth_data); + chunk_free(&auth_data); + + return SUCCESS; +} + +/** + * Implementation of eap_authenticator_t.initiate + */ +static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, + eap_payload_t **out) +{ + /* if initiate() is called, role is always server */ + this->role = EAP_SERVER; + + if (type == 0) + { + DBG1(DBG_IKE, + "client requested EAP authentication, but configuration forbids it"); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + + DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type); + this->method = eap_method_create(type, this->role, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + + if (this->method == NULL) + { + DBG1(DBG_IKE, "configured EAP server method %N not supported, sending %N", + eap_type_names, type, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + if (this->method->initiate(this->method, out) != NEED_MORE) + { + DBG1(DBG_IKE, "failed to initiate %N, sending %N", + eap_type_names, type, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + return NEED_MORE; +} + +/** + * Processing method for a peer + */ +static status_t process_peer(private_eap_authenticator_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + eap_type_t type = in->get_type(in); + + if (type == EAP_IDENTITY) + { + eap_method_t *method = eap_method_create(type, EAP_PEER, + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa)); + + if (method == NULL || method->process(method, in, out) != SUCCESS) + { + DBG1(DBG_IKE, "EAP server requested %N, but unable to process", + eap_type_names, type); + DESTROY_IF(method); + return FAILED; + } + + DBG1(DBG_IKE, "EAP server requested %N, sending IKE identity", + eap_type_names, type); + + method->destroy(method); + return NEED_MORE; + } + + /* create an eap_method for the first call */ + if (this->method == NULL) + { + DBG1(DBG_IKE, "EAP server requested %N authentication", + eap_type_names, type); + this->method = eap_method_create(type, EAP_PEER, + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa)); + if (this->method == NULL) + { + DBG1(DBG_IKE, "EAP server requested unsupported " + "EAP method %N, sending EAP_NAK", eap_type_names, type); + *out = eap_payload_create_nak(); + return NEED_MORE; + } + } + + switch (this->method->process(this->method, in, out)) + { + case NEED_MORE: + return NEED_MORE; + case SUCCESS: + DBG1(DBG_IKE, "EAP method %N succeded", + eap_type_names, this->method->get_type(this->method)); + return SUCCESS; + case FAILED: + default: + DBG1(DBG_IKE, "EAP method %N failed", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + } +} + +/** + * Processing method for a server + */ +static status_t process_server(private_eap_authenticator_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + switch (this->method->process(this->method, in, out)) + { + case NEED_MORE: + return NEED_MORE; + case SUCCESS: + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + DBG1(DBG_IKE, "EAP method %N succeded, MSK established", + eap_type_names, this->method->get_type(this->method)); + this->msk = chunk_clone(this->msk); + *out = eap_payload_create_code(EAP_SUCCESS); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established", + eap_type_names, this->method->get_type(this->method)); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + case FAILED: + default: + DBG1(DBG_IKE, "EAP method %N failed for peer %D", + eap_type_names, this->method->get_type(this->method), + this->ike_sa->get_other_id(this->ike_sa)); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } +} + +/** + * Implementation of eap_authenticator_t.process + */ +static status_t process(private_eap_authenticator_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + eap_code_t code = in->get_code(in); + + switch (this->role) + { + case EAP_SERVER: + { + switch (code) + { + case EAP_RESPONSE: + { + return process_server(this, in, out); + } + default: + { + DBG1(DBG_IKE, "received %N, sending %N", + eap_code_names, code, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + } + } + case EAP_PEER: + { + switch (code) + { + case EAP_REQUEST: + { + return process_peer(this, in, out); + } + case EAP_SUCCESS: + { + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + this->msk = chunk_clone(this->msk); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N has no MSK established", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + } + case EAP_FAILURE: + default: + { + DBG1(DBG_IKE, "received %N, EAP authentication failed", + eap_code_names, code); + return FAILED; + } + } + } + default: + { + return FAILED; + } + } +} + +/** + * Implementation of authenticator_t.is_mutual. + */ +static bool is_mutual(private_eap_authenticator_t *this) +{ + if (this->method) + { + return this->method->is_mutual(this->method); + } + return FALSE; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_eap_authenticator_t *this) +{ + DESTROY_IF(this->method); + chunk_free(&this->msk); + free(this); +} + +/* + * Described in header. + */ +eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa) +{ + private_eap_authenticator_t *this = malloc_thing(private_eap_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + this->public.is_mutual = (bool(*)(eap_authenticator_t*))is_mutual; + this->public.initiate = (status_t(*)(eap_authenticator_t*,eap_type_t,eap_payload_t**))initiate; + this->public.process = (status_t(*)(eap_authenticator_t*,eap_payload_t*,eap_payload_t**))process; + + /* private data */ + this->ike_sa = ike_sa; + this->role = EAP_PEER; + this->method = NULL; + this->msk = chunk_empty; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h new file mode 100644 index 000000000..ffa162343 --- /dev/null +++ b/src/charon/sa/authenticators/eap_authenticator.h @@ -0,0 +1,156 @@ +/** + * @file eap_authenticator.h + * + * @brief Interface of eap_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef EAP_AUTHENTICATOR_H_ +#define EAP_AUTHENTICATOR_H_ + +typedef struct eap_authenticator_t eap_authenticator_t; + +#include +#include + +/** + * @brief Implementation of the authenticator_t interface using AUTH_EAP. + * + * Authentication using EAP involves the most complex authenticator. It stays + * alive over multiple ike_auth transactions and handles multiple EAP + * messages. + * EAP authentication must be clearly distinguished between using + * mutual EAP methods and using methods not providing server authentication. + * If no mutual authentication is used, the server must prove it's identity + * by traditional AUTH methods (RSA, psk). Only when the EAP method is mutual, + * the client should accept an EAP-only authentication. + * RFC4306 does always use traditional authentiction, EAP only authentication + * is described in the internet draft draft-eronen-ipsec-ikev2-eap-auth-05.txt. + * + * @verbatim + ike_sa_init + -------------------------> + <------------------------- + followed by multiple ike_auth: + + +--------+ +--------+ + | EAP | ID, SA, TS, N(EAP_ONLY) | EAP | + | client | ---------------------------> | server | + | | ID, [AUTH,] EAP | | AUTH payload is + | | <--------------------------- | | only included if + | | EAP | | authentication + | | ---------------------------> | | is not mutual. + | | EAP | | + | | <--------------------------- | | + | | EAP | | + | | ---------------------------> | | + | | EAP(SUCCESS) | | + | | <--------------------------- | | + | | AUTH | | If EAP establishes + | | ---------------------------> | | a session key, AUTH + | | AUTH, SA, TS | | payloads use this + | | <--------------------------- | | key, not SK_pi/pr + +--------+ +--------+ + + @endverbatim + * @b Constructors: + * - eap_authenticator_create() + * - authenticator_create() using auth_method AUTH_EAP + * + * @ingroup authenticators + */ +struct eap_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; + + /** + * @brief Check if the EAP method was/is mutual and secure. + * + * RFC4306 proposes to authenticate the EAP responder (server) by standard + * IKEv2 methods (RSA, psk). Not all, but some EAP methods + * provide mutual authentication, which would result in a redundant + * authentication. If the client supports EAP_ONLY_AUTHENTICATION, and + * the the server provides mutual authentication, authentication using + * RSA/PSK may be omitted. If the server did not include a traditional + * AUTH payload, the client must verify that the server initiated mutual + * EAP authentication before it can trust the server. + * + * @param this calling object + * @return TRUE, if no AUTH payload required, FALSE otherwise + */ + bool (*is_mutual) (eap_authenticator_t* this); + + /** + * @brief Initiate the EAP exchange. + * + * The server initiates EAP exchanges, so the client never calls + * this method. If initiate() returns NEED_MORE, the EAP authentication + * process started. In any case, a payload is created in "out". + * + * @param this calling object + * @param type EAP method to use to authenticate client + * @param out created initiaal EAP message to send + * @return + * - FAILED, if initiation failed + * - NEED_MORE, if more EAP exchanges reqired + */ + status_t (*initiate) (eap_authenticator_t* this, eap_type_t type, + eap_payload_t **out); + + /** + * @brief Process an EAP message. + * + * After receiving an EAP message "in", the peer/server processes + * the payload and creates a reply/subsequent request. + * The server side always returns NEED_MORE if another EAP message + * is excepted from the client, SUCCESS if EAP exchange completed and + * "out" is EAP_SUCCES, or FAILED if the EAP exchange failed with + * a EAP_FAILURE payload in "out". Anyway, a payload in "out" is always + * created. + * The peer (client) side only creates a "out" payload if result is + * NEED_MORE, a SUCCESS/FAILED is returned whenever a + * EAP_SUCCESS/EAP_FAILURE message is received in "in". + * If a SUCCESS is returned (on any side), the EAP authentication was + * successful and the AUTH payload can be exchanged. + * + * @param this calling object + * @param in received EAP message + * @param out created EAP message to send + * @return + * - FAILED, if authentication/EAP exchange failed + * - SUCCESS, if authentication completed + * - NEED_MORE, if more EAP exchanges reqired + */ + status_t (*process) (eap_authenticator_t* this, + eap_payload_t *in, eap_payload_t **out); +}; + +/** + * @brief Creates an authenticator for AUTH_EAP. + * + * @param ike_sa associated ike_sa + * @return eap_authenticator_t object + * + * @ingroup authenticators + */ +eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa); + +#endif /* EAP_AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c new file mode 100644 index 000000000..43aec0971 --- /dev/null +++ b/src/charon/sa/authenticators/psk_authenticator.c @@ -0,0 +1,204 @@ +/** + * @file psk_authenticator.c + * + * @brief Implementation of psk_authenticator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "psk_authenticator.h" + +#include +#include + +/** + * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +#define IKEV2_KEY_PAD "Key Pad for IKEv2" +#define IKEV2_KEY_PAD_LENGTH 17 + + +typedef struct private_psk_authenticator_t private_psk_authenticator_t; + +/** + * Private data of an psk_authenticator_t object. + */ +struct private_psk_authenticator_t { + + /** + * Public authenticator_t interface. + */ + psk_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; +}; + +/** + * Builds the octets to be signed as described in section 2.15 of RFC 4306 + */ +chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, + identification_t *id, prf_t *prf) +{ + u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00}; + chunk_t id_header = chunk_from_buf(id_header_buf); + chunk_t id_with_header, id_prfd, id_encoding; + + id_header_buf[0] = id->get_type(id); + id_encoding = id->get_encoding(id); + + id_with_header = chunk_cat("cc", id_header, id_encoding); + prf->allocate_bytes(prf, id_with_header, &id_prfd); + chunk_free(&id_with_header); + + return chunk_cat("ccm", ike_sa_init, nonce, id_prfd); +} + +/** + * Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, + prf_t *prf_skp, prf_t *prf) +{ + chunk_t key_pad, key, auth_data, octets; + + octets = build_tbs_octets(ike_sa_init, nonce, id, prf_skp); + /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), ) */ + key_pad.ptr = IKEV2_KEY_PAD; + key_pad.len = IKEV2_KEY_PAD_LENGTH; + prf->set_key(prf, secret); + prf->allocate_bytes(prf, key_pad, &key); + prf->set_key(prf, key); + prf->allocate_bytes(prf, octets, &auth_data); + DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets); + DBG3(DBG_IKE, "secret %B", &secret); + DBG3(DBG_IKE, "keypad %B", &key_pad); + DBG3(DBG_IKE, "prf(secret, keypad) %B", &key); + DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &auth_data); + chunk_free(&octets); + chunk_free(&key); + + return auth_data; +} + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + status_t status; + chunk_t auth_data, recv_auth_data, shared_key; + identification_t *my_id, *other_id; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); + status = charon->credentials->get_shared_key(charon->credentials, my_id, + other_id, &shared_key); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); + return status; + } + + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, shared_key, + other_id, this->ike_sa->get_auth_verify(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + chunk_free(&shared_key); + + recv_auth_data = auth_payload->get_data(auth_payload); + if (auth_data.len != recv_auth_data.len || + !memeq(auth_data.ptr, recv_auth_data.ptr, auth_data.len)) + { + DBG1(DBG_IKE, "PSK MAC verification failed"); + chunk_free(&auth_data); + return FAILED; + } + chunk_free(&auth_data); + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_PSK); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t shared_key; + chunk_t auth_data; + status_t status; + identification_t *my_id, *other_id; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_PSK); + status = charon->credentials->get_shared_key(charon->credentials, my_id, + other_id, &shared_key); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); + return status; + } + + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, shared_key, + my_id, this->ike_sa->get_auth_build(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + DBG2(DBG_IKE, "successfully created shared key MAC"); + chunk_free(&shared_key); + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); + (*auth_payload)->set_data(*auth_payload, auth_data); + + chunk_free(&auth_data); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_psk_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa) +{ + private_psk_authenticator_t *this = malloc_thing(private_psk_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + /* private data */ + this->ike_sa = ike_sa; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/psk_authenticator.h b/src/charon/sa/authenticators/psk_authenticator.h new file mode 100644 index 000000000..c1c5bcaac --- /dev/null +++ b/src/charon/sa/authenticators/psk_authenticator.h @@ -0,0 +1,57 @@ +/** + * @file psk_authenticator.h + * + * @brief Interface of psk_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PSK_AUTHENTICATOR_H_ +#define PSK_AUTHENTICATOR_H_ + +typedef struct psk_authenticator_t psk_authenticator_t; + +#include + +/** + * @brief Implementation of the authenticator_t interface using AUTH_PSK. + * + * @b Constructors: + * - psk_authenticator_create() + * - authenticator_create() using auth_method AUTH_PSK + * + * @ingroup authenticators + */ +struct psk_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; +}; + +/** + * @brief Creates an authenticator for AUTH_PSK. + * + * @param ike_sa associated ike_sa + * @return psk_authenticator_t object + * + * @ingroup authenticators + */ +psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa); + +#endif /* PSK_AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/authenticators/rsa_authenticator.c b/src/charon/sa/authenticators/rsa_authenticator.c new file mode 100644 index 000000000..dfa01e332 --- /dev/null +++ b/src/charon/sa/authenticators/rsa_authenticator.c @@ -0,0 +1,180 @@ +/** + * @file rsa_authenticator.c + * + * @brief Implementation of rsa_authenticator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "rsa_authenticator.h" + +#include +#include + + +typedef struct private_rsa_authenticator_t private_rsa_authenticator_t; + +/** + * Private data of an rsa_authenticator_t object. + */ +struct private_rsa_authenticator_t { + + /** + * Public authenticator_t interface. + */ + rsa_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; +}; + +/** + * Function implemented in psk_authenticator.c + */ +extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, + identification_t *id, prf_t *prf); + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_rsa_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + status_t status; + chunk_t auth_data, octets; + rsa_public_key_t *public_key; + identification_t *other_id; + + other_id = this->ike_sa->get_other_id(this->ike_sa); + + if (auth_payload->get_auth_method(auth_payload) != AUTH_RSA) + { + return INVALID_ARG; + } + auth_data = auth_payload->get_data(auth_payload); + public_key = charon->credentials->get_trusted_public_key(charon->credentials, + other_id); + if (public_key == NULL) + { + DBG1(DBG_IKE, "no RSA public key found for '%D'", other_id); + return NOT_FOUND; + } + octets = build_tbs_octets(ike_sa_init, my_nonce, other_id, + this->ike_sa->get_auth_verify(this->ike_sa)); + status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data); + chunk_free(&octets); + + if (status != SUCCESS) + { + DBG1(DBG_IKE, "RSA signature verification failed"); + return status; + } + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_RSA); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_rsa_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t chunk; + chunk_t octets; + chunk_t auth_data; + status_t status; + rsa_public_key_t *my_pubkey; + rsa_private_key_t *my_key; + identification_t *my_id; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_RSA); + DBG2(DBG_IKE, "looking for RSA public key belonging to '%D'", my_id); + + my_pubkey = charon->credentials->get_rsa_public_key(charon->credentials, my_id); + if (my_pubkey == NULL) + { + DBG1(DBG_IKE, "no RSA public key found for '%D'", my_id); + return NOT_FOUND; + } + DBG2(DBG_IKE, "matching RSA public key found"); + chunk = my_pubkey->get_keyid(my_pubkey); + DBG2(DBG_IKE, "looking for RSA private key with keyid %#B", &chunk); + my_key = charon->credentials->get_rsa_private_key(charon->credentials, my_pubkey); + if (my_key == NULL) + { + DBG1(DBG_IKE, "no RSA private key found with for %D with keyid %#B", + my_id, &chunk); + return NOT_FOUND; + } + DBG2(DBG_IKE, "matching RSA private key found"); + + octets = build_tbs_octets(ike_sa_init, other_nonce, my_id, + this->ike_sa->get_auth_build(this->ike_sa)); + status = my_key->build_emsa_pkcs1_signature(my_key, HASH_SHA1, octets, &auth_data); + chunk_free(&octets); + + if (status != SUCCESS) + { + my_key->destroy(my_key); + DBG1(DBG_IKE, "build signature of SHA1 hash failed"); + return status; + } + DBG2(DBG_IKE, "successfully signed with RSA private key"); + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_RSA); + (*auth_payload)->set_data(*auth_payload, auth_data); + + my_key->destroy(my_key); + chunk_free(&auth_data); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_rsa_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa) +{ + private_rsa_authenticator_t *this = malloc_thing(private_rsa_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + /* private data */ + this->ike_sa = ike_sa; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/rsa_authenticator.h b/src/charon/sa/authenticators/rsa_authenticator.h new file mode 100644 index 000000000..cc5cc0150 --- /dev/null +++ b/src/charon/sa/authenticators/rsa_authenticator.h @@ -0,0 +1,57 @@ +/** + * @file rsa_authenticator.h + * + * @brief Interface of rsa_authenticator_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef RSA_AUTHENTICATOR_H_ +#define RSA_AUTHENTICATOR_H_ + +typedef struct rsa_authenticator_t rsa_authenticator_t; + +#include + +/** + * @brief Implementation of the authenticator_t interface using AUTH_RSA. + * + * @b Constructors: + * - rsa_authenticator_create() + * - authenticator_create() using auth_method AUTH_RSA + * + * @ingroup authenticators + */ +struct rsa_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; +}; + +/** + * @brief Creates an authenticator for AUTH_RSA. + * + * @param ike_sa associated ike_sa + * @return rsa_authenticator_t object + * + * @ingroup authenticators + */ +rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa); + +#endif /* RSA_AUTHENTICATOR_H_ */ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c new file mode 100644 index 000000000..19131389d --- /dev/null +++ b/src/charon/sa/child_sa.c @@ -0,0 +1,1130 @@ +/** + * @file child_sa.c + * + * @brief Implementation of child_sa_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include "child_sa.h" + +#include +#include +#include + +#include + +ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DELETING, + "CREATED", + "ROUTED", + "INSTALLED", + "REKEYING", + "DELETING", +); + +typedef struct sa_policy_t sa_policy_t; + +/** + * Struct used to store information for a policy. This + * is needed since we must provide all this information + * for deleting a policy... + */ +struct sa_policy_t { + /** + * Traffic selector for us + */ + traffic_selector_t *my_ts; + + /** + * Traffic selector for other + */ + traffic_selector_t *other_ts; +}; + +typedef struct private_child_sa_t private_child_sa_t; + +/** + * Private data of a child_sa_t bject. + */ +struct private_child_sa_t { + /** + * Public interface of child_sa_t. + */ + child_sa_t public; + + struct { + /** address of peer */ + host_t *addr; + /** id of peer */ + identification_t *id; + /** actual used SPI, 0 if unused */ + u_int32_t spi; + } me, other; + + /** + * Allocated SPI for a ESP proposal candidates + */ + u_int32_t alloc_esp_spi; + + /** + * Allocated SPI for a AH proposal candidates + */ + u_int32_t alloc_ah_spi; + + /** + * Protocol used to protect this SA, ESP|AH + */ + protocol_id_t protocol; + + /** + * List containing sa_policy_t objects + */ + linked_list_t *policies; + + /** + * Seperate list for local traffic selectors + */ + linked_list_t *my_ts; + + /** + * Seperate list for remote traffic selectors + */ + linked_list_t *other_ts; + + /** + * reqid used for this child_sa + */ + u_int32_t reqid; + + /** + * encryption algorithm used for this SA + */ + algorithm_t encryption; + + /** + * integrity protection algorithm used for this SA + */ + algorithm_t integrity; + + /** + * time, on which SA was installed + */ + time_t install_time; + + /** + * absolute time when rekeying is sceduled + */ + time_t rekey_time; + + /** + * state of the CHILD_SA + */ + child_sa_state_t state; + + /** + * Specifies if NAT traversal is used + */ + bool use_natt; + + /** + * mode this SA uses, tunnel/transport + */ + mode_t mode; + + /** + * virtual IP assinged to local host + */ + host_t *virtual_ip; + + /** + * policy used to create this child + */ + policy_t *policy; +}; + +/** + * Implementation of child_sa_t.get_name. + */ +static char *get_name(private_child_sa_t *this) +{ + return this->policy->get_name(this->policy);; +} + +/** + * Implements child_sa_t.get_reqid + */ +static u_int32_t get_reqid(private_child_sa_t *this) +{ + return this->reqid; +} + +/** + * Implements child_sa_t.get_spi + */ +u_int32_t get_spi(private_child_sa_t *this, bool inbound) +{ + if (inbound) + { + return this->me.spi; + } + return this->other.spi; +} + +/** + * Implements child_sa_t.get_protocol + */ +protocol_id_t get_protocol(private_child_sa_t *this) +{ + return this->protocol; +} + +/** + * Implements child_sa_t.get_state + */ +static child_sa_state_t get_state(private_child_sa_t *this) +{ + return this->state; +} + +/** + * Implements child_sa_t.get_policy + */ +static policy_t* get_policy(private_child_sa_t *this) +{ + return this->policy; +} + +/** + * Run the up/down script + */ +static void updown(private_child_sa_t *this, bool up) +{ + sa_policy_t *policy; + iterator_t *iterator; + char *script; + + script = this->policy->get_updown(this->policy); + + if (script == NULL) + { + return; + } + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + char command[1024]; + char *ifname = NULL; + char *my_client, *other_client, *my_client_mask, *other_client_mask; + char *pos, *virtual_ip; + FILE *shell; + + /* get subnet/bits from string */ + asprintf(&my_client, "%R", policy->my_ts); + pos = strchr(my_client, '/'); + *pos = '\0'; + my_client_mask = pos + 1; + pos = strchr(my_client_mask, '['); + if (pos) + { + *pos = '\0'; + } + asprintf(&other_client, "%R", policy->other_ts); + pos = strchr(other_client, '/'); + *pos = '\0'; + other_client_mask = pos + 1; + pos = strchr(other_client_mask, '['); + if (pos) + { + *pos = '\0'; + } + + if (this->virtual_ip) + { + asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", + this->virtual_ip); + } + else + { + asprintf(&virtual_ip, ""); + } + + ifname = charon->kernel_interface->get_interface(charon->kernel_interface, + this->me.addr); + + /* build the command with all env variables. + * TODO: PLUTO_PEER_CA and PLUTO_NEXT_HOP are currently missing + */ + snprintf(command, sizeof(command), + "2>&1 " + "PLUTO_VERSION='1.1' " + "PLUTO_VERB='%s%s%s' " + "PLUTO_CONNECTION='%s' " + "PLUTO_INTERFACE='%s' " + "PLUTO_REQID='%u' " + "PLUTO_ME='%H' " + "PLUTO_MY_ID='%D' " + "PLUTO_MY_CLIENT='%s/%s' " + "PLUTO_MY_CLIENT_NET='%s' " + "PLUTO_MY_CLIENT_MASK='%s' " + "PLUTO_MY_PORT='%u' " + "PLUTO_MY_PROTOCOL='%u' " + "PLUTO_PEER='%H' " + "PLUTO_PEER_ID='%D' " + "PLUTO_PEER_CLIENT='%s/%s' " + "PLUTO_PEER_CLIENT_NET='%s' " + "PLUTO_PEER_CLIENT_MASK='%s' " + "PLUTO_PEER_PORT='%u' " + "PLUTO_PEER_PROTOCOL='%u' " + "%s" + "%s" + "%s", + up ? "up" : "down", + policy->my_ts->is_host(policy->my_ts, + this->me.addr) ? "-host" : "-client", + this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6", + this->policy->get_name(this->policy), + ifname ? ifname : "(unknown)", + this->reqid, + this->me.addr, + this->me.id, + my_client, my_client_mask, + my_client, my_client_mask, + policy->my_ts->get_from_port(policy->my_ts), + policy->my_ts->get_protocol(policy->my_ts), + this->other.addr, + this->other.id, + other_client, other_client_mask, + other_client, other_client_mask, + policy->other_ts->get_from_port(policy->other_ts), + policy->other_ts->get_protocol(policy->other_ts), + virtual_ip, + this->policy->get_hostaccess(this->policy) ? + "PLUTO_HOST_ACCESS='1' " : "", + script); + free(ifname); + free(my_client); + free(other_client); + free(virtual_ip); + + shell = popen(command, "r"); + + if (shell == NULL) + { + DBG1(DBG_CHD, "could not execute updown script '%s'", script); + return; + } + + while (TRUE) + { + char resp[128]; + + if (fgets(resp, sizeof(resp), shell) == NULL) + { + if (ferror(shell)) + { + DBG1(DBG_CHD, "error reading output from updown script"); + return; + } + else + { + break; + } + } + else + { + char *e = resp + strlen(resp); + if (e > resp && e[-1] == '\n') + { /* trim trailing '\n' */ + e[-1] = '\0'; + } + DBG1(DBG_CHD, "updown: %s", resp); + } + } + pclose(shell); + } + iterator->destroy(iterator); +} + +/** + * Implements child_sa_t.set_state + */ +static void set_state(private_child_sa_t *this, child_sa_state_t state) +{ + this->state = state; + if (state == CHILD_INSTALLED) + { + updown(this, TRUE); + } +} + +/** + * Allocate SPI for a single proposal + */ +static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal) +{ + protocol_id_t protocol = proposal->get_protocol(proposal); + + if (protocol == PROTO_AH) + { + /* get a new spi for AH, if not already done */ + if (this->alloc_ah_spi == 0) + { + if (charon->kernel_interface->get_spi( + charon->kernel_interface, + this->other.addr, this->me.addr, + PROTO_AH, this->reqid, + &this->alloc_ah_spi) != SUCCESS) + { + return FAILED; + } + } + proposal->set_spi(proposal, this->alloc_ah_spi); + } + if (protocol == PROTO_ESP) + { + /* get a new spi for ESP, if not already done */ + if (this->alloc_esp_spi == 0) + { + if (charon->kernel_interface->get_spi( + charon->kernel_interface, + this->other.addr, this->me.addr, + PROTO_ESP, this->reqid, + &this->alloc_esp_spi) != SUCCESS) + { + return FAILED; + } + } + proposal->set_spi(proposal, this->alloc_esp_spi); + } + return SUCCESS; +} + + +/** + * Implements child_sa_t.alloc + */ +static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) +{ + iterator_t *iterator; + proposal_t *proposal; + + /* iterator through proposals to update spis */ + iterator = proposals->create_iterator(proposals, TRUE); + while(iterator->iterate(iterator, (void**)&proposal)) + { + if (alloc_proposal(this, proposal) != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +static status_t install(private_child_sa_t *this, proposal_t *proposal, + mode_t mode, prf_plus_t *prf_plus, bool mine) +{ + u_int32_t spi, soft, hard;; + algorithm_t *enc_algo, *int_algo; + algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0}; + algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; + host_t *src; + host_t *dst; + natt_conf_t *natt; + status_t status; + + this->protocol = proposal->get_protocol(proposal); + + /* now we have to decide which spi to use. Use self allocated, if "mine", + * or the one in the proposal, if not "mine" (others). Additionally, + * source and dest host switch depending on the role */ + if (mine) + { + /* if we have allocated SPIs for AH and ESP, we must delete the unused + * one. */ + if (this->protocol == PROTO_ESP) + { + this->me.spi = this->alloc_esp_spi; + if (this->alloc_ah_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, + this->alloc_ah_spi, PROTO_AH); + } + } + else + { + this->me.spi = this->alloc_ah_spi; + if (this->alloc_esp_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, + this->alloc_esp_spi, PROTO_ESP); + } + } + spi = this->me.spi; + dst = this->me.addr; + src = this->other.addr; + } + else + { + this->other.spi = proposal->get_spi(proposal); + spi = this->other.spi; + src = this->me.addr; + dst = this->other.addr; + } + + DBG2(DBG_CHD, "adding %s %N SA", mine ? "inbound" : "outbound", + protocol_id_names, this->protocol); + + /* select encryption algo */ + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo)) + { + DBG2(DBG_CHD, " using %N for encryption", + encryption_algorithm_names, enc_algo->algorithm); + } + else + { + enc_algo = &enc_algo_none; + } + + /* select integrity algo */ + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo)) + { + DBG2(DBG_CHD, " using %N for integrity", + integrity_algorithm_names, int_algo->algorithm); + } + else + { + int_algo = &int_algo_none; + } + + /* setup nat-t */ + if (this->use_natt) + { + natt = alloca(sizeof(natt_conf_t)); + natt->sport = src->get_port(src); + natt->dport = dst->get_port(dst); + } + else + { + natt = NULL; + } + + soft = this->policy->get_soft_lifetime(this->policy); + hard = this->policy->get_hard_lifetime(this->policy); + + /* send SA down to the kernel */ + DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, spi, this->protocol, + this->reqid, mine ? soft : 0, + hard, enc_algo, int_algo, + prf_plus, natt, mode, mine); + + this->encryption = *enc_algo; + this->integrity = *int_algo; + this->install_time = time(NULL); + this->rekey_time = soft; + + return status; +} + +static status_t add(private_child_sa_t *this, proposal_t *proposal, + mode_t mode, prf_plus_t *prf_plus) +{ + u_int32_t outbound_spi, inbound_spi; + + /* backup outbound spi, as alloc overwrites it */ + outbound_spi = proposal->get_spi(proposal); + + /* get SPIs inbound SAs */ + if (alloc_proposal(this, proposal) != SUCCESS) + { + return FAILED; + } + inbound_spi = proposal->get_spi(proposal); + + /* install inbound SAs */ + if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + + /* install outbound SAs, restore spi*/ + proposal->set_spi(proposal, outbound_spi); + if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + proposal->set_spi(proposal, inbound_spi); + + return SUCCESS; +} + +static status_t update(private_child_sa_t *this, proposal_t *proposal, + mode_t mode, prf_plus_t *prf_plus) +{ + u_int32_t inbound_spi; + + /* backup received spi, as install() overwrites it */ + inbound_spi = proposal->get_spi(proposal); + + /* install outbound SAs */ + if (install(this, proposal, mode, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + + /* restore spi */ + proposal->set_spi(proposal, inbound_spi); + /* install inbound SAs */ + if (install(this, proposal, mode, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + + return SUCCESS; +} + +static status_t add_policies(private_child_sa_t *this, + linked_list_t *my_ts_list, + linked_list_t *other_ts_list, mode_t mode) +{ + iterator_t *my_iter, *other_iter; + traffic_selector_t *my_ts, *other_ts; + /* use low prio for ROUTED policies */ + bool high_prio = (this->state != CHILD_CREATED); + + /* iterate over both lists */ + my_iter = my_ts_list->create_iterator(my_ts_list, TRUE); + other_iter = other_ts_list->create_iterator(other_ts_list, TRUE); + while (my_iter->iterate(my_iter, (void**)&my_ts)) + { + other_iter->reset(other_iter); + while (other_iter->iterate(other_iter, (void**)&other_ts)) + { + /* set up policies for every entry in my_ts_list to every entry in other_ts_list */ + status_t status; + sa_policy_t *policy; + + if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts)) + { + DBG2(DBG_CHD, + "CHILD_SA policy uses two different IP families, ignored"); + continue; + } + + /* only set up policies if protocol matches, or if one is zero (any) */ + if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts) && + my_ts->get_protocol(my_ts) && other_ts->get_protocol(other_ts)) + { + DBG2(DBG_CHD, + "CHILD_SA policy uses two different protocols, ignored"); + continue; + } + + /* install 3 policies: out, in and forward */ + status = charon->kernel_interface->add_policy(charon->kernel_interface, + this->me.addr, this->other.addr, my_ts, other_ts, POLICY_OUT, + this->protocol, this->reqid, high_prio, mode, FALSE); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other.addr, this->me.addr, other_ts, my_ts, POLICY_IN, + this->protocol, this->reqid, high_prio, mode, FALSE); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other.addr, this->me.addr, other_ts, my_ts, POLICY_FWD, + this->protocol, this->reqid, high_prio, mode, FALSE); + + if (status != SUCCESS) + { + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + return status; + } + + /* store policy to delete/update them later */ + policy = malloc_thing(sa_policy_t); + policy->my_ts = my_ts->clone(my_ts); + policy->other_ts = other_ts->clone(other_ts); + this->policies->insert_last(this->policies, (void*)policy); + /* add to separate list to query them via get_*_traffic_selectors() */ + this->my_ts->insert_last(this->my_ts, (void*)policy->my_ts); + this->other_ts->insert_last(this->other_ts, (void*)policy->other_ts); + } + } + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + + /* switch to routed state if no SAD entry set up */ + if (this->state == CHILD_CREATED) + { + this->state = CHILD_ROUTED; + } + /* needed to update hosts */ + this->mode = mode; + return SUCCESS; +} + +/** + * Implementation of child_sa_t.get_my_traffic_selectors. + */ +static linked_list_t *get_my_traffic_selectors(private_child_sa_t *this) +{ + return this->my_ts; +} + +/** + * Implementation of child_sa_t.get_my_traffic_selectors. + */ +static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this) +{ + return this->other_ts; +} + +/** + * Implementation of child_sa_t.get_use_time + */ +static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time) +{ + iterator_t *iterator; + sa_policy_t *policy; + status_t status = FAILED; + + *use_time = UNDEFINED_TIME; + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + if (inbound) + { + time_t in = UNDEFINED_TIME, fwd = UNDEFINED_TIME; + + status = charon->kernel_interface->query_policy( + charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_IN, (u_int32_t*)&in); + status |= charon->kernel_interface->query_policy( + charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_FWD, (u_int32_t*)&fwd); + *use_time = max(in, fwd); + } + else + { + status = charon->kernel_interface->query_policy( + charon->kernel_interface, + policy->my_ts, policy->other_ts, + POLICY_OUT, (u_int32_t*)use_time); + } + } + iterator->destroy(iterator); + return status; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_child_sa_t *this = *((private_child_sa_t**)(args[0])); + iterator_t *iterator; + sa_policy_t *policy; + u_int32_t now, rekeying; + u_int32_t use, use_in, use_fwd; + status_t status; + size_t written = 0; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + now = time(NULL); + + written += fprintf(stream, "%12s{%d}: %N, %N", + this->policy->get_name(this->policy), this->reqid, + child_sa_state_names, this->state, + mode_names, this->mode); + + if (this->state == CHILD_INSTALLED) + { + written += fprintf(stream, ", %N SPIs: 0x%0x_i 0x%0x_o", + protocol_id_names, this->protocol, + htonl(this->me.spi), htonl(this->other.spi)); + + if (info->alt) + { + written += fprintf(stream, "\n%12s{%d}: ", + this->policy->get_name(this->policy), + this->reqid); + + if (this->protocol == PROTO_ESP) + { + written += fprintf(stream, "%N", encryption_algorithm_names, + this->encryption.algorithm); + + if (this->encryption.key_size) + { + written += fprintf(stream, "-%d", this->encryption.key_size); + } + written += fprintf(stream, "/"); + } + + written += fprintf(stream, "%N", integrity_algorithm_names, + this->integrity.algorithm); + if (this->integrity.key_size) + { + written += fprintf(stream, "-%d", this->integrity.key_size); + } + written += fprintf(stream, ", rekeying "); + + /* calculate rekey times */ + if (this->rekey_time) + { + rekeying = this->install_time + this->rekey_time - now; + written += fprintf(stream, "in %ds", rekeying); + } + else + { + written += fprintf(stream, "disabled"); + } + } + } + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ", + this->policy->get_name(this->policy), this->reqid, + policy->my_ts, policy->other_ts); + + /* query time of last policy use */ + + /* inbound: POLICY_IN or POLICY_FWD */ + status = charon->kernel_interface->query_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, POLICY_IN, &use_in); + use_in = (status == SUCCESS)? use_in : 0; + status = charon->kernel_interface->query_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, POLICY_FWD, &use_fwd); + use_fwd = (status == SUCCESS)? use_fwd : 0; + use = max(use_in, use_fwd); + if (use) + { + written += fprintf(stream, "%ds_i ", now - use); + } + else + { + written += fprintf(stream, "no_i "); + } + + /* outbound: POLICY_OUT */ + status = charon->kernel_interface->query_policy(charon->kernel_interface, + policy->my_ts, policy->other_ts, POLICY_OUT, &use); + if (status == SUCCESS && use) + { + written += fprintf(stream, "%ds_o ", now - use); + } + else + { + written += fprintf(stream, "no_o "); + } + } + iterator->destroy(iterator); + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_CHILD_SA, print, arginfo_ptr); +} + +/** + * Update the host adress/port of a SA + */ +static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, + int my_changes, int other_changes, bool mine) +{ + host_t *src, *dst, *new_src, *new_dst; + int src_changes, dst_changes; + status_t status; + u_int32_t spi; + + if (mine) + { + src = this->other.addr; + dst = this->me.addr; + new_src = new_other; + new_dst = new_me; + src_changes = other_changes; + dst_changes = my_changes; + spi = this->other.spi; + } + else + { + src = this->me.addr; + dst = this->other.addr; + new_src = new_me; + new_dst = new_other; + src_changes = my_changes; + dst_changes = other_changes; + spi = this->me.spi; + } + + DBG2(DBG_CHD, "updating %N SA 0x%x, from %#H..#H to %#H..%#H", + protocol_id_names, this->protocol, ntohl(spi), src, dst, new_src, new_dst); + + status = charon->kernel_interface->update_sa(charon->kernel_interface, + dst, spi, this->protocol, + new_src, new_dst, + src_changes, dst_changes); + + if (status != SUCCESS) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Update the host adress/port of a policy + */ +static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other) +{ + iterator_t *iterator; + sa_policy_t *policy; + status_t status; + /* we always use high priorities, as hosts getting updated are INSTALLED */ + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + status = charon->kernel_interface->add_policy( + charon->kernel_interface, + new_me, new_other, + policy->my_ts, policy->other_ts, + POLICY_OUT, this->protocol, this->reqid, TRUE, this->mode, TRUE); + + status |= charon->kernel_interface->add_policy( + charon->kernel_interface, + new_other, new_me, + policy->other_ts, policy->my_ts, + POLICY_IN, this->protocol, this->reqid, TRUE, this->mode, TRUE); + + status |= charon->kernel_interface->add_policy( + charon->kernel_interface, + new_other, new_me, + policy->other_ts, policy->my_ts, + POLICY_FWD, this->protocol, this->reqid, TRUE, this->mode, TRUE); + + if (status != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + iterator->destroy(iterator); + + return SUCCESS; +} + +/** + * Implementation of child_sa_t.update_hosts. + */ +static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, + host_diff_t my_changes, host_diff_t other_changes) +{ + if (!my_changes && !other_changes) + { + return SUCCESS; + } + + /* update our (initator) SAs */ + if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS) + { + return FAILED; + } + + /* update his (responder) SAs */ + if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS) + { + return FAILED; + } + + /* update policies */ + if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR) + { + if (update_policy_hosts(this, new_me, new_other) != SUCCESS) + { + return FAILED; + } + } + + /* update hosts */ + if (my_changes) + { + this->me.addr->destroy(this->me.addr); + this->me.addr = new_me->clone(new_me); + } + + if (other_changes) + { + this->other.addr->destroy(this->other.addr); + this->other.addr = new_other->clone(new_other); + } + + return SUCCESS; +} + +/** + * Implementation of child_sa_t.set_virtual_ip. + */ +static void set_virtual_ip(private_child_sa_t *this, host_t *ip) +{ + this->virtual_ip = ip->clone(ip); +} + +/** + * Implementation of child_sa_t.destroy. + */ +static void destroy(private_child_sa_t *this) +{ + sa_policy_t *policy; + + if (this->state == CHILD_DELETING || this->state == CHILD_INSTALLED) + { + updown(this, FALSE); + } + + /* delete SAs in the kernel, if they are set up */ + if (this->me.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me.addr, this->me.spi, this->protocol); + } + if (this->alloc_esp_spi && this->alloc_esp_spi != this->me.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me.addr, this->alloc_esp_spi, PROTO_ESP); + } + if (this->alloc_ah_spi && this->alloc_ah_spi != this->me.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me.addr, this->alloc_ah_spi, PROTO_AH); + } + if (this->other.spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other.addr, this->other.spi, this->protocol); + } + + /* delete all policies in the kernel */ + while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) + { + /* let rekeyed policies, as they are used by another child_sa */ + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->my_ts, policy->other_ts, + POLICY_OUT); + + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_IN); + + charon->kernel_interface->del_policy(charon->kernel_interface, + policy->other_ts, policy->my_ts, + POLICY_FWD); + policy->my_ts->destroy(policy->my_ts); + policy->other_ts->destroy(policy->other_ts); + free(policy); + } + this->policies->destroy(this->policies); + + this->my_ts->destroy(this->my_ts); + this->other_ts->destroy(this->other_ts); + this->me.addr->destroy(this->me.addr); + this->other.addr->destroy(this->other.addr); + this->me.id->destroy(this->me.id); + this->other.id->destroy(this->other.id); + this->policy->destroy(this->policy); + DESTROY_IF(this->virtual_ip); + free(this); +} + +/* + * Described in header. + */ +child_sa_t * child_sa_create(host_t *me, host_t* other, + identification_t *my_id, identification_t *other_id, + policy_t *policy, u_int32_t rekey, bool use_natt) +{ + static u_int32_t reqid = 0; + private_child_sa_t *this = malloc_thing(private_child_sa_t); + + /* public functions */ + this->public.get_name = (char*(*)(child_sa_t*))get_name; + this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid; + this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi; + this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol; + this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; + this->public.add = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))add; + this->public.update = (status_t(*)(child_sa_t*,proposal_t*,mode_t,prf_plus_t*))update; + this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_diff_t,host_diff_t))update_hosts; + this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,mode_t))add_policies; + this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors; + this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors; + this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time; + this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; + this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; + this->public.get_policy = (policy_t*(*)(child_sa_t*))get_policy; + this->public.set_virtual_ip = (void(*)(child_sa_t*,host_t*))set_virtual_ip; + this->public.destroy = (void(*)(child_sa_t*))destroy; + + /* private data */ + this->me.addr = me->clone(me); + this->other.addr = other->clone(other); + this->me.id = my_id->clone(my_id); + this->other.id = other_id->clone(other_id); + this->me.spi = 0; + this->other.spi = 0; + this->alloc_ah_spi = 0; + this->alloc_esp_spi = 0; + this->use_natt = use_natt; + this->state = CHILD_CREATED; + /* reuse old reqid if we are rekeying an existing CHILD_SA */ + this->reqid = rekey ? rekey : ++reqid; + this->encryption.algorithm = ENCR_UNDEFINED; + this->encryption.key_size = 0; + this->integrity.algorithm = AUTH_UNDEFINED; + this->encryption.key_size = 0; + this->policies = linked_list_create(); + this->my_ts = linked_list_create(); + this->other_ts = linked_list_create(); + this->protocol = PROTO_NONE; + this->mode = MODE_TUNNEL; + this->virtual_ip = NULL; + this->policy = policy; + policy->get_ref(policy); + + return &this->public; +} diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h new file mode 100644 index 000000000..216e56659 --- /dev/null +++ b/src/charon/sa/child_sa.h @@ -0,0 +1,298 @@ +/** + * @file child_sa.h + * + * @brief Interface of child_sa_t. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef CHILD_SA_H_ +#define CHILD_SA_H_ + +typedef enum child_sa_state_t child_sa_state_t; +typedef struct child_sa_t child_sa_t; + +#include +#include +#include +#include +#include + +/** + * Where we should start with reqid enumeration + */ +#define REQID_START 2000000000 + +/** + * @brief States of a CHILD_SA + */ +enum child_sa_state_t { + + /** + * Just created, uninstalled CHILD_SA + */ + CHILD_CREATED, + + /** + * Installed SPD, but no SAD entries + */ + CHILD_ROUTED, + + /** + * Installed an in-use CHILD_SA + */ + CHILD_INSTALLED, + + /** + * CHILD_SA which is rekeying + */ + CHILD_REKEYING, + + /** + * CHILD_SA in progress of delete + */ + CHILD_DELETING, +}; + +/** + * enum strings for child_sa_state_t. + */ +extern enum_name_t *child_sa_state_names; + +/** + * @brief Represents an IPsec SAs between two hosts. + * + * A child_sa_t contains two SAs. SAs for both + * directions are managed in one child_sa_t object. Both + * SAs and the policies have the same reqid. + * + * The procedure for child sa setup is as follows: + * - A gets SPIs for a proposal via child_sa_t.alloc + * - A send the updated proposal to B + * - B selects a suitable proposal + * - B calls child_sa_t.add to add and update the selected proposal + * - B sends the updated proposal to A + * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal + * + * Once SAs are set up, policies can be added using add_policies. + * + * + * @b Constructors: + * - child_sa_create() + * + * @ingroup sa + */ +struct child_sa_t { + + /** + * @brief Get the name of the policy this CHILD_SA uses. + * + * @param this calling object + * @return name + */ + char* (*get_name) (child_sa_t *this); + + /** + * @brief Get the reqid of the CHILD SA. + * + * Every CHILD_SA has a reqid. The kernel uses this ID to + * identify it. + * + * @param this calling object + * @return reqid of the CHILD SA + */ + u_int32_t (*get_reqid)(child_sa_t *this); + + /** + * @brief Get the SPI of this CHILD_SA. + * + * Set the boolean parameter inbound to TRUE to + * get the SPI for which we receive packets, use + * FALSE to get those we use for sending packets. + * + * @param this calling object + * @param inbound TRUE to get inbound SPI, FALSE for outbound. + * @return spi of the CHILD SA + */ + u_int32_t (*get_spi) (child_sa_t *this, bool inbound); + + /** + * @brief Get the protocol which this CHILD_SA uses to protect traffic. + * + * @param this calling object + * @return AH | ESP + */ + protocol_id_t (*get_protocol) (child_sa_t *this); + + /** + * @brief Allocate SPIs for given proposals. + * + * Since the kernel manages SPIs for us, we need + * to allocate them. If a proposal contains more + * than one protocol, for each protocol an SPI is + * allocated. SPIs are stored internally and written + * back to the proposal. + * + * @param this calling object + * @param proposals list of proposals for which SPIs are allocated + */ + status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); + + /** + * @brief Install the kernel SAs for a proposal, without previous SPI allocation. + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param mode mode for the CHILD_SA + * @param prf_plus key material to use for key derivation + * @return SUCCESS or FAILED + */ + status_t (*add)(child_sa_t *this, proposal_t *proposal, mode_t mode, + prf_plus_t *prf_plus); + + /** + * @brief Install the kernel SAs for a proposal, after SPIs have been allocated. + * + * Updates an SA, for which SPIs are already allocated via alloc(). + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param mode mode for the CHILD_SA + * @param prf_plus key material to use for key derivation + * @return SUCCESS or FAILED + */ + status_t (*update)(child_sa_t *this, proposal_t *proposal, mode_t mode, + prf_plus_t *prf_plus); + + /** + * @brief Update the hosts in the kernel SAs and policies + * + * @warning only call this after update() has been called. + * + * @param this calling object + * @param new_me the new local host + * @param new_other the new remote host + * @param my_diff differences to apply for me + * @param other_diff differences to apply for other + * @return SUCCESS or FAILED + */ + status_t (*update_hosts)(child_sa_t *this, host_t *new_me, host_t *new_other, + host_diff_t my_diff, host_diff_t other_diff); + + /** + * @brief Install the policies using some traffic selectors. + * + * Supplied lists of traffic_selector_t's specify the policies + * to use for this child sa. + * + * @param this calling object + * @param my_ts traffic selectors for local site + * @param other_ts traffic selectors for remote site + * @param mode mode for the SA: tunnel/transport + * @return SUCCESS or FAILED + */ + status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list, + linked_list_t *other_ts_list, mode_t mode); + + /** + * @brief Get the traffic selectors of added policies of local host. + * + * @param this calling object + * @return list of traffic selectors + */ + linked_list_t* (*get_my_traffic_selectors) (child_sa_t *this); + + /** + * @brief Get the traffic selectors of added policies of remote host. + * + * @param this calling object + * @return list of traffic selectors + */ + linked_list_t* (*get_other_traffic_selectors) (child_sa_t *this); + + /** + * @brief Get the time of this child_sa_t's last use (i.e. last use of any of its policies) + * + * @param this calling object + * @param inbound query for in- or outbound usage + * @param use_time the time + * @return SUCCESS or FAILED + */ + status_t (*get_use_time) (child_sa_t *this, bool inbound, time_t *use_time); + + /** + * @brief Get the state of the CHILD_SA. + * + * @param this calling object + */ + child_sa_state_t (*get_state) (child_sa_t *this); + + /** + * @brief Set the state of the CHILD_SA. + * + * @param this calling object + */ + void (*set_state) (child_sa_t *this, child_sa_state_t state); + + /** + * @brief Get the policy used to set up this child sa. + * + * @param this calling object + * @return policy + */ + policy_t* (*get_policy) (child_sa_t *this); + + /** + * @brief Set the virtual IP used received from IRAS. + * + * To allow proper setup of firewall rules, the virtual IP is required + * for filtering. + * + * @param this calling object + * @param ip own virtual IP + */ + void (*set_virtual_ip) (child_sa_t *this, host_t *ip); + + /** + * @brief Destroys a child_sa. + * + * @param this calling object + */ + void (*destroy) (child_sa_t *this); +}; + +/** + * @brief Constructor to create a new child_sa_t. + * + * @param me own address + * @param other remote address + * @param my_id id of own peer + * @param other_id id of remote peer + * @param policy policy this CHILD_SA instantiates + * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise + * @param use_natt TRUE if NAT traversal is used + * @return child_sa_t object + * + * @ingroup sa + */ +child_sa_t * child_sa_create(host_t *me, host_t *other, + identification_t *my_id, identification_t* other_id, + policy_t *policy, u_int32_t reqid, bool use_natt); + +#endif /*CHILD_SA_H_*/ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c new file mode 100644 index 000000000..68aba3064 --- /dev/null +++ b/src/charon/sa/ike_sa.c @@ -0,0 +1,2032 @@ +/** + * @file ike_sa.c + * + * @brief Implementation of ike_sa_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include + +#include "ike_sa.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef RESOLV_CONF +#define RESOLV_CONF "/etc/resolv.conf" +#endif + +ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING, + "CREATED", + "CONNECTING", + "ESTABLISHED", + "REKEYING", + "DELETING", +); + +typedef struct private_ike_sa_t private_ike_sa_t; + +/** + * Private data of an ike_sa_t object. + */ +struct private_ike_sa_t { + + /** + * Public members + */ + ike_sa_t public; + + /** + * Identifier for the current IKE_SA. + */ + ike_sa_id_t *ike_sa_id; + + /** + * unique numerical ID for this IKE_SA. + */ + u_int32_t unique_id; + + /** + * Current state of the IKE_SA + */ + ike_sa_state_t state; + + /** + * connection used to establish this IKE_SA. + */ + connection_t *connection; + + /** + * Peer and authentication information to establish IKE_SA. + */ + policy_t *policy; + + /** + * Juggles tasks to process messages + */ + task_manager_t *task_manager; + + /** + * Address of local host + */ + host_t *my_host; + + /** + * Address of remote host + */ + host_t *other_host; + + /** + * Identification used for us + */ + identification_t *my_id; + + /** + * Identification used for other + */ + identification_t *other_id; + + /** + * Linked List containing the child sa's of the current IKE_SA. + */ + linked_list_t *child_sas; + + /** + * crypter for inbound traffic + */ + crypter_t *crypter_in; + + /** + * crypter for outbound traffic + */ + crypter_t *crypter_out; + + /** + * Signer for inbound traffic + */ + signer_t *signer_in; + + /** + * Signer for outbound traffic + */ + signer_t *signer_out; + + /** + * Multi purpose prf, set key, use it, forget it + */ + prf_t *prf; + + /** + * Prf function for derivating keymat child SAs + */ + prf_t *child_prf; + + /** + * PRF to build outging authentication data + */ + prf_t *auth_build; + + /** + * PRF to verify incoming authentication data + */ + prf_t *auth_verify; + + /** + * NAT status of local host. + */ + bool nat_here; + + /** + * NAT status of remote host. + */ + bool nat_there; + + /** + * Virtual IP on local host, if any + */ + host_t *my_virtual_ip; + + /** + * Virtual IP on remote host, if any + */ + host_t *other_virtual_ip; + + /** + * List of DNS servers installed by us + */ + linked_list_t *dns_servers; + + /** + * Timestamps for this IKE_SA + */ + struct { + /** last IKE message received */ + u_int32_t inbound; + /** last IKE message sent */ + u_int32_t outbound; + /** when IKE_SA became established */ + u_int32_t established; + /** when IKE_SA gets rekeyed */ + u_int32_t rekey; + /** when IKE_SA gets deleted */ + u_int32_t delete; + } time; + + /** + * how many times we have retried so far (keyingtries) + */ + u_int32_t keyingtry; +}; + +/** + * get the time of the latest traffic processed by the kernel + */ +static time_t get_use_time(private_ike_sa_t* this, bool inbound) +{ + iterator_t *iterator; + child_sa_t *child_sa; + time_t latest = 0, use_time; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS) + { + latest = max(latest, use_time); + } + } + iterator->destroy(iterator); + + if (inbound) + { + return max(this->time.inbound, latest); + } + else + { + return max(this->time.outbound, latest); + } +} + +/** + * Implementation of ike_sa_t.get_unique_id + */ +static u_int32_t get_unique_id(private_ike_sa_t *this) +{ + return this->unique_id; +} + +/** + * Implementation of ike_sa_t.get_name. + */ +static char *get_name(private_ike_sa_t *this) +{ + if (this->connection) + { + return this->connection->get_name(this->connection); + } + return "(unnamed)"; +} + +/** + * Implementation of ike_sa_t.get_connection + */ +static connection_t* get_connection(private_ike_sa_t *this) +{ + return this->connection; +} + +/** + * Implementation of ike_sa_t.set_connection + */ +static void set_connection(private_ike_sa_t *this, connection_t *connection) +{ + this->connection = connection; + connection->get_ref(connection); +} + +/** + * Implementation of ike_sa_t.get_policy + */ +static policy_t *get_policy(private_ike_sa_t *this) +{ + return this->policy; +} + +/** + * Implementation of ike_sa_t.set_policy + */ +static void set_policy(private_ike_sa_t *this, policy_t *policy) +{ + policy->get_ref(policy); + this->policy = policy; +} + +/** + * Implementation of ike_sa_t.get_my_host. + */ +static host_t *get_my_host(private_ike_sa_t *this) +{ + return this->my_host; +} + +/** + * Implementation of ike_sa_t.set_my_host. + */ +static void set_my_host(private_ike_sa_t *this, host_t *me) +{ + DESTROY_IF(this->my_host); + this->my_host = me; +} + +/** + * Implementation of ike_sa_t.get_other_host. + */ +static host_t *get_other_host(private_ike_sa_t *this) +{ + return this->other_host; +} + +/** + * Implementation of ike_sa_t.set_other_host. + */ +static void set_other_host(private_ike_sa_t *this, host_t *other) +{ + DESTROY_IF(this->other_host); + this->other_host = other; +} + +/** + * Implementation of ike_sa_t.send_dpd + */ +static status_t send_dpd(private_ike_sa_t *this) +{ + send_dpd_job_t *job; + time_t diff, delay; + + delay = this->connection->get_dpd_delay(this->connection); + + if (delay == 0) + { + /* DPD disabled */ + return SUCCESS; + } + + if (this->task_manager->busy(this->task_manager)) + { + /* an exchange is in the air, no need to start a DPD check */ + diff = 0; + } + else + { + /* check if there was any inbound traffic */ + time_t last_in, now; + last_in = get_use_time(this, TRUE); + now = time(NULL); + diff = now - last_in; + if (diff >= delay) + { + /* to long ago, initiate dead peer detection */ + task_t *task; + + task = (task_t*)ike_dpd_create(TRUE); + diff = 0; + DBG1(DBG_IKE, "sending DPD request"); + + this->task_manager->queue_task(this->task_manager, task); + this->task_manager->initiate(this->task_manager); + } + } + /* recheck in "interval" seconds */ + job = send_dpd_job_create(this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, + (delay - diff) * 1000); + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.send_keepalive + */ +static void send_keepalive(private_ike_sa_t *this) +{ + send_keepalive_job_t *job; + time_t last_out, now, diff, interval; + + last_out = get_use_time(this, FALSE); + now = time(NULL); + + diff = now - last_out; + interval = charon->configuration->get_keepalive_interval(charon->configuration); + + if (diff >= interval) + { + packet_t *packet; + chunk_t data; + + packet = packet_create(); + packet->set_source(packet, this->my_host->clone(this->my_host)); + packet->set_destination(packet, this->other_host->clone(this->other_host)); + data.ptr = malloc(1); + data.ptr[0] = 0xFF; + data.len = 1; + packet->set_data(packet, data); + charon->sender->send(charon->sender, packet); + DBG1(DBG_IKE, "sending keep alive"); + diff = 0; + } + job = send_keepalive_job_create(this->ike_sa_id); + charon->event_queue->add_relative(charon->event_queue, (job_t*)job, + (interval - diff) * 1000); +} + +/** + * Implementation of ike_sa_t.get_state. + */ +static ike_sa_state_t get_state(private_ike_sa_t *this) +{ + return this->state; +} + +/** + * Implementation of ike_sa_t.set_state. + */ +static void set_state(private_ike_sa_t *this, ike_sa_state_t state) +{ + DBG1(DBG_IKE, "IKE_SA state change: %N => %N", + ike_sa_state_names, this->state, + ike_sa_state_names, state); + + switch (state) + { + case IKE_ESTABLISHED: + { + if (this->state == IKE_CONNECTING) + { + job_t *job; + u_int32_t now = time(NULL); + u_int32_t soft, hard; + bool reauth; + + this->time.established = now; + /* start DPD checks */ + send_dpd(this); + + /* schedule rekeying/reauthentication */ + soft = this->connection->get_soft_lifetime(this->connection); + hard = this->connection->get_hard_lifetime(this->connection); + reauth = this->connection->get_reauth(this->connection); + DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds", + reauth ? "reauthentication": "rekeying", soft, hard); + + if (soft) + { + this->time.rekey = now + soft; + job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth); + charon->event_queue->add_relative(charon->event_queue, job, + soft * 1000); + } + + if (hard) + { + this->time.delete = now + hard; + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->event_queue->add_relative(charon->event_queue, job, + hard * 1000); + } + } + break; + } + case IKE_DELETING: + { + /* delete may fail if a packet gets lost, so set a timeout */ + job_t *job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->event_queue->add_relative(charon->event_queue, job, + charon->configuration->get_half_open_ike_sa_timeout( + charon->configuration)); + break; + } + default: + break; + } + + this->state = state; +} + +/** + * Implementation of ike_sa_t.reset + */ +static void reset(private_ike_sa_t *this) +{ + /* the responder ID is reset, as peer may choose another one */ + if (this->ike_sa_id->is_initiator(this->ike_sa_id)) + { + this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0); + } + + set_state(this, IKE_CREATED); + + this->task_manager->reset(this->task_manager); +} + +/** + * Update connection host, as addresses may change (NAT) + */ +static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) +{ + iterator_t *iterator = NULL; + child_sa_t *child_sa = NULL; + host_diff_t my_diff, other_diff; + + if (this->my_host->is_anyaddr(this->my_host) || + this->other_host->is_anyaddr(this->other_host)) + { + /* on first received message */ + this->my_host->destroy(this->my_host); + this->my_host = me->clone(me); + this->other_host->destroy(this->other_host); + this->other_host = other->clone(other); + return; + } + + my_diff = me->get_differences(me, this->my_host); + other_diff = other->get_differences(other, this->other_host); + + if (!my_diff && !other_diff) + { + return; + } + + if (my_diff) + { + this->my_host->destroy(this->my_host); + this->my_host = me->clone(me); + } + + if (!this->nat_here) + { + /* update without restrictions if we are not NATted */ + if (other_diff) + { + this->other_host->destroy(this->other_host); + this->other_host = other->clone(other); + } + } + else + { + /* if we are natted, only port may change */ + if (other_diff & HOST_DIFF_ADDR) + { + return; + } + else if (other_diff & HOST_DIFF_PORT) + { + this->other_host->set_port(this->other_host, other->get_port(other)); + } + } + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_sa->update_hosts(child_sa, this->my_host, this->other_host, + my_diff, other_diff); + } + iterator->destroy(iterator); +} + +/** + * Implementation of ike_sa_t.generate + */ +static status_t generate_message(private_ike_sa_t *this, message_t *message, + packet_t **packet) +{ + this->time.outbound = time(NULL); + message->set_ike_sa_id(message, this->ike_sa_id); + message->set_destination(message, this->other_host->clone(this->other_host)); + message->set_source(message, this->my_host->clone(this->my_host)); + return message->generate(message, this->crypter_out, this->signer_out, packet); +} + +/** + * send a notify back to the sender + */ +static void send_notify_response(private_ike_sa_t *this, message_t *request, + notify_type_t type) +{ + message_t *response; + packet_t *packet; + + response = message_create(); + response->set_exchange_type(response, request->get_exchange_type(request)); + response->set_request(response, FALSE); + response->set_message_id(response, request->get_message_id(request)); + response->add_notify(response, FALSE, type, chunk_empty); + if (this->my_host->is_anyaddr(this->my_host)) + { + this->my_host->destroy(this->my_host); + this->my_host = request->get_destination(request); + this->my_host = this->my_host->clone(this->my_host); + } + if (this->other_host->is_anyaddr(this->other_host)) + { + this->other_host->destroy(this->other_host); + this->other_host = request->get_source(request); + this->other_host = this->other_host->clone(this->other_host); + } + if (generate_message(this, response, &packet) == SUCCESS) + { + charon->sender->send(charon->sender, packet); + } + response->destroy(response); +} + +/** + * Implementation of ike_sa_t.process_message. + */ +static status_t process_message(private_ike_sa_t *this, message_t *message) +{ + status_t status; + bool is_request; + + is_request = message->get_request(message); + + status = message->parse_body(message, this->crypter_in, this->signer_in); + if (status != SUCCESS) + { + + if (is_request) + { + switch (status) + { + case NOT_SUPPORTED: + DBG1(DBG_IKE, "ciritcal unknown payloads found"); + if (is_request) + { + send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD); + } + break; + case PARSE_ERROR: + DBG1(DBG_IKE, "message parsing failed"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + break; + case VERIFY_ERROR: + DBG1(DBG_IKE, "message verification failed"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + break; + case FAILED: + DBG1(DBG_IKE, "integrity check failed"); + /* ignored */ + break; + case INVALID_STATE: + DBG1(DBG_IKE, "found encrypted message, but no keys available"); + if (is_request) + { + send_notify_response(this, message, INVALID_SYNTAX); + } + default: + break; + } + } + DBG1(DBG_IKE, "%N %s with message ID %d processing failed", + exchange_type_names, message->get_exchange_type(message), + message->get_request(message) ? "request" : "response", + message->get_message_id(message)); + return status; + } + else + { + host_t *me, *other; + + me = message->get_destination(message); + other = message->get_source(message); + + /* if this IKE_SA is virgin, we check for a connection */ + if (this->connection == NULL) + { + job_t *job; + this->connection = charon->connections->get_connection_by_hosts( + charon->connections, me, other); + if (this->connection == NULL) + { + /* no connection found for these hosts, destroy */ + DBG1(DBG_IKE, "no connection found for %H...%H, sending %N", + me, other, notify_type_names, NO_PROPOSAL_CHOSEN); + send_notify_response(this, message, NO_PROPOSAL_CHOSEN); + return DESTROY_ME; + } + /* add a timeout if peer does not establish it completely */ + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, FALSE); + charon->event_queue->add_relative(charon->event_queue, job, + charon->configuration->get_half_open_ike_sa_timeout( + charon->configuration)); + } + + /* check if message is trustworthy, and update connection information */ + if (this->state == IKE_CREATED || + message->get_exchange_type(message) != IKE_SA_INIT) + { + update_hosts(this, me, other); + this->time.inbound = time(NULL); + } + return this->task_manager->process_message(this->task_manager, message); + } +} + +/** + * apply the connection/policy information to this IKE_SA + */ +static void apply_config(private_ike_sa_t *this, + connection_t *connection, policy_t *policy) +{ + host_t *me, *other; + identification_t *my_id, *other_id; + + if (this->connection == NULL && this->policy == NULL) + { + this->connection = connection; + connection->get_ref(connection); + this->policy = policy; + policy->get_ref(policy); + + me = connection->get_my_host(connection); + other = connection->get_other_host(connection); + my_id = policy->get_my_id(policy); + other_id = policy->get_other_id(policy); + set_my_host(this, me->clone(me)); + set_other_host(this, other->clone(other)); + DESTROY_IF(this->my_id); + DESTROY_IF(this->other_id); + this->my_id = my_id->clone(my_id); + this->other_id = other_id->clone(other_id); + } +} + +/** + * Implementation of ike_sa_t.initiate. + */ +static status_t initiate(private_ike_sa_t *this, + connection_t *connection, policy_t *policy) +{ + task_t *task; + + if (this->state == IKE_CREATED) + { + /* if we aren't established/establishing, do so */ + apply_config(this, connection, policy); + + if (this->other_host->is_anyaddr(this->other_host)) + { + SIG(IKE_UP_START, "initiating IKE_SA"); + SIG(IKE_UP_FAILED, "unable to initiate to %%any"); + return DESTROY_ME; + } + + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + } + + task = (task_t*)child_create_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + + return this->task_manager->initiate(this->task_manager); +} + +/** + * Implementation of ike_sa_t.acquire. + */ +static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) +{ + policy_t *policy; + iterator_t *iterator; + child_sa_t *current, *child_sa = NULL; + task_t *task; + child_create_t *child_create; + + if (this->state == IKE_DELETING) + { + SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request"); + SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " + "IKE_SA is deleting", reqid); + return FAILED; + } + + /* find CHILD_SA */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_reqid(current) == reqid) + { + child_sa = current; + break; + } + } + iterator->destroy(iterator); + if (!child_sa) + { + SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request"); + SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " + "CHILD_SA not found", reqid); + return FAILED; + } + + policy = child_sa->get_policy(child_sa); + + if (this->state == IKE_CREATED) + { + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_config_create(&this->public, policy); + this->task_manager->queue_task(this->task_manager, task); + } + + child_create = child_create_create(&this->public, policy); + child_create->use_reqid(child_create, reqid); + this->task_manager->queue_task(this->task_manager, (task_t*)child_create); + + return this->task_manager->initiate(this->task_manager); +} + +/** + * compare two lists of traffic selectors for equality + */ +static bool ts_list_equals(linked_list_t *l1, linked_list_t *l2) +{ + bool equals = TRUE; + iterator_t *i1, *i2; + traffic_selector_t *t1, *t2; + + if (l1->get_count(l1) != l2->get_count(l2)) + { + return FALSE; + } + + i1 = l1->create_iterator(l1, TRUE); + i2 = l2->create_iterator(l2, TRUE); + while (i1->iterate(i1, (void**)&t1) && i2->iterate(i2, (void**)&t2)) + { + if (!t1->equals(t1, t2)) + { + equals = FALSE; + break; + } + } + i1->destroy(i1); + i2->destroy(i2); + return equals; +} + +/** + * Implementation of ike_sa_t.route. + */ +static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t *policy) +{ + child_sa_t *child_sa = NULL; + iterator_t *iterator; + linked_list_t *my_ts, *other_ts; + status_t status; + + SIG(CHILD_ROUTE_START, "routing CHILD_SA"); + + /* check if not already routed*/ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + linked_list_t *my_ts_conf, *other_ts_conf; + + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); + + if (ts_list_equals(my_ts, my_ts_conf) && + ts_list_equals(other_ts, other_ts_conf)) + { + iterator->destroy(iterator); + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + SIG(CHILD_ROUTE_FAILED, "CHILD_SA with such a policy already routed"); + return FAILED; + } + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + } + } + iterator->destroy(iterator); + + switch (this->state) + { + case IKE_DELETING: + case IKE_REKEYING: + SIG(CHILD_ROUTE_FAILED, + "unable to route CHILD_SA, as its IKE_SA gets deleted"); + return FAILED; + case IKE_CREATED: + /* apply connection information, we need it to acquire */ + apply_config(this, connection, policy); + break; + case IKE_CONNECTING: + case IKE_ESTABLISHED: + default: + break; + } + + /* install kernel policies */ + child_sa = child_sa_create(this->my_host, this->other_host, + this->my_id, this->other_id, policy, FALSE, 0); + + my_ts = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts = policy->get_other_traffic_selectors(policy, this->other_host); + status = child_sa->add_policies(child_sa, my_ts, other_ts, + policy->get_mode(policy)); + my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + this->child_sas->insert_last(this->child_sas, child_sa); + SIG(CHILD_ROUTE_SUCCESS, "CHILD_SA routed"); + return status; +} + +/** + * Implementation of ike_sa_t.unroute. + */ +static status_t unroute(private_ike_sa_t *this, policy_t *policy) +{ + iterator_t *iterator; + child_sa_t *child_sa = NULL; + bool found = FALSE; + linked_list_t *my_ts, *other_ts, *my_ts_conf, *other_ts_conf; + + SIG(CHILD_UNROUTE_START, "unrouting CHILD_SA"); + + /* find CHILD_SA in ROUTED state */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); + + if (ts_list_equals(my_ts, my_ts_conf) && + ts_list_equals(other_ts, other_ts_conf)) + { + iterator->remove(iterator); + SIG(CHILD_UNROUTE_SUCCESS, "CHILD_SA unrouted"); + child_sa->destroy(child_sa); + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + found = TRUE; + break; + } + my_ts_conf->destroy_offset(my_ts_conf, offsetof(traffic_selector_t, destroy)); + other_ts_conf->destroy_offset(other_ts_conf, offsetof(traffic_selector_t, destroy)); + } + } + iterator->destroy(iterator); + + if (!found) + { + SIG(CHILD_UNROUTE_FAILED, "CHILD_SA to unroute not found"); + return FAILED; + } + /* if we are not established, and we have no more routed childs, remove whole SA */ + if (this->state == IKE_CREATED && + this->child_sas->get_count(this->child_sas) == 0) + { + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.retransmit. + */ +static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) +{ + this->time.outbound = time(NULL); + if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) + { + policy_t *policy; + child_sa_t* child_sa; + linked_list_t *to_route, *to_restart; + iterator_t *iterator; + + /* send a proper signal to brief interested bus listeners */ + switch (this->state) + { + case IKE_CONNECTING: + { + /* retry IKE_SA_INIT if we have multiple keyingtries */ + u_int32_t tries = this->connection->get_keyingtries(this->connection); + this->keyingtry++; + if (tries == 0 || tries > this->keyingtry) + { + SIG(IKE_UP_FAILED, "peer not responding, trying again " + "(%d/%d) in background ", this->keyingtry + 1, tries); + reset(this); + return this->task_manager->initiate(this->task_manager); + } + SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); + break; + } + case IKE_REKEYING: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); + break; + case IKE_DELETING: + SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); + break; + default: + break; + } + + /* summarize how we have to handle each child */ + to_route = linked_list_create(); + to_restart = linked_list_create(); + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + policy = child_sa->get_policy(child_sa); + + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + /* reroute routed CHILD_SAs */ + to_route->insert_last(to_route, policy); + } + else + { + /* use DPD action for established CHILD_SAs */ + switch (policy->get_dpd_action(policy)) + { + case DPD_ROUTE: + to_route->insert_last(to_route, policy); + break; + case DPD_RESTART: + to_restart->insert_last(to_restart, policy); + break; + default: + break; + } + } + } + iterator->destroy(iterator); + + /* create a new IKE_SA if we have to route or to restart */ + if (to_route->get_count(to_route) || to_restart->get_count(to_restart)) + { + private_ike_sa_t *new; + task_t *task; + + new = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new( + charon->ike_sa_manager, TRUE); + + apply_config(new, this->connection, this->policy); + /* use actual used host, not the wildcarded one in connection */ + new->other_host->destroy(new->other_host); + new->other_host = this->other_host->clone(this->other_host); + + /* install routes */ + while (to_route->remove_last(to_route, (void**)&policy) == SUCCESS) + { + route(new, new->connection, policy); + } + + /* restart children */ + if (to_restart->get_count(to_restart)) + { + task = (task_t*)ike_init_create(&new->public, TRUE, NULL); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_natd_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_cert_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_config_create(&new->public, new->policy); + new->task_manager->queue_task(new->task_manager, task); + task = (task_t*)ike_auth_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); + + while (to_restart->remove_last(to_restart, (void**)&policy) == SUCCESS) + { + task = (task_t*)child_create_create(&new->public, policy); + new->task_manager->queue_task(new->task_manager, task); + } + new->task_manager->initiate(new->task_manager); + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, &new->public); + } + to_route->destroy(to_route); + to_restart->destroy(to_restart); + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.get_prf. + */ +static prf_t *get_prf(private_ike_sa_t *this) +{ + return this->prf; +} + +/** + * Implementation of ike_sa_t.get_prf. + */ +static prf_t *get_child_prf(private_ike_sa_t *this) +{ + return this->child_prf; +} + +/** + * Implementation of ike_sa_t.get_auth_bild + */ +static prf_t *get_auth_build(private_ike_sa_t *this) +{ + return this->auth_build; +} + +/** + * Implementation of ike_sa_t.get_auth_verify + */ +static prf_t *get_auth_verify(private_ike_sa_t *this) +{ + return this->auth_verify; +} + +/** + * Implementation of ike_sa_t.get_id. + */ +static ike_sa_id_t* get_id(private_ike_sa_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implementation of ike_sa_t.get_my_id. + */ +static identification_t* get_my_id(private_ike_sa_t *this) +{ + return this->my_id; +} + +/** + * Implementation of ike_sa_t.set_my_id. + */ +static void set_my_id(private_ike_sa_t *this, identification_t *me) +{ + DESTROY_IF(this->my_id); + this->my_id = me; +} + +/** + * Implementation of ike_sa_t.get_other_id. + */ +static identification_t* get_other_id(private_ike_sa_t *this) +{ + return this->other_id; +} + +/** + * Implementation of ike_sa_t.set_other_id. + */ +static void set_other_id(private_ike_sa_t *this, identification_t *other) +{ + DESTROY_IF(this->other_id); + this->other_id = other; +} + +/** + * Implementation of ike_sa_t.derive_keys. + */ +static status_t derive_keys(private_ike_sa_t *this, + proposal_t *proposal, chunk_t secret, + chunk_t nonce_i, chunk_t nonce_r, + bool initiator, prf_t *child_prf, prf_t *old_prf) +{ + prf_plus_t *prf_plus; + chunk_t skeyseed, key, nonces, prf_plus_seed; + algorithm_t *algo; + size_t key_size; + crypter_t *crypter_i, *crypter_r; + signer_t *signer_i, *signer_r; + prf_t *prf_i, *prf_r; + u_int8_t spi_i_buf[sizeof(u_int64_t)], spi_r_buf[sizeof(u_int64_t)]; + chunk_t spi_i = chunk_from_buf(spi_i_buf); + chunk_t spi_r = chunk_from_buf(spi_r_buf); + + /* Create SAs general purpose PRF first, we may use it here */ + if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo)) + { + DBG1(DBG_IKE, "key derivation failed: no PSEUDO_RANDOM_FUNCTION");; + return FAILED; + } + this->prf = prf_create(algo->algorithm); + if (this->prf == NULL) + { + DBG1(DBG_IKE, "key derivation failed: PSEUDO_RANDOM_FUNCTION " + "%N not supported!", pseudo_random_function_names, algo->algorithm); + return FAILED; + } + + DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); + nonces = chunk_cat("cc", nonce_i, nonce_r); + *((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); + *((u_int64_t*)spi_r.ptr) = this->ike_sa_id->get_responder_spi(this->ike_sa_id); + prf_plus_seed = chunk_cat("ccc", nonces, spi_i, spi_r); + + /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) + * + * if we are rekeying, SKEYSEED is built on another way + */ + if (child_prf == NULL) /* not rekeying */ + { + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + this->prf->set_key(this->prf, nonces); + this->prf->allocate_bytes(this->prf, secret, &skeyseed); + DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); + this->prf->set_key(this->prf, skeyseed); + chunk_free(&skeyseed); + chunk_free(&secret); + prf_plus = prf_plus_create(this->prf, prf_plus_seed); + } + else + { + /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) + * use OLD SAs PRF functions for both prf_plus and prf */ + secret = chunk_cat("mc", secret, nonces); + child_prf->allocate_bytes(child_prf, secret, &skeyseed); + DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); + old_prf->set_key(old_prf, skeyseed); + chunk_free(&skeyseed); + chunk_free(&secret); + prf_plus = prf_plus_create(old_prf, prf_plus_seed); + } + chunk_free(&nonces); + chunk_free(&prf_plus_seed); + + /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */ + + /* SK_d is used for generating CHILD_SA key mat => child_prf */ + proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo); + this->child_prf = prf_create(algo->algorithm); + key_size = this->child_prf->get_key_size(this->child_prf); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_d secret %B", &key); + this->child_prf->set_key(this->child_prf, key); + chunk_free(&key); + + /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ + if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &algo)) + { + DBG1(DBG_IKE, "key derivation failed: no INTEGRITY_ALGORITHM"); + return FAILED; + } + signer_i = signer_create(algo->algorithm); + signer_r = signer_create(algo->algorithm); + if (signer_i == NULL || signer_r == NULL) + { + DBG1(DBG_IKE, "key derivation failed: INTEGRITY_ALGORITHM " + "%N not supported!", integrity_algorithm_names ,algo->algorithm); + return FAILED; + } + key_size = signer_i->get_key_size(signer_i); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ai secret %B", &key); + signer_i->set_key(signer_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ar secret %B", &key); + signer_r->set_key(signer_r, key); + chunk_free(&key); + + if (initiator) + { + this->signer_in = signer_r; + this->signer_out = signer_i; + } + else + { + this->signer_in = signer_i; + this->signer_out = signer_r; + } + + /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ + if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo)) + { + DBG1(DBG_IKE, "key derivation failed: no ENCRYPTION_ALGORITHM"); + return FAILED; + } + crypter_i = crypter_create(algo->algorithm, algo->key_size / 8); + crypter_r = crypter_create(algo->algorithm, algo->key_size / 8); + if (crypter_i == NULL || crypter_r == NULL) + { + DBG1(DBG_IKE, "key derivation failed: ENCRYPTION_ALGORITHM " + "%N (key size %d) not supported!", + encryption_algorithm_names, algo->algorithm, algo->key_size); + return FAILED; + } + key_size = crypter_i->get_key_size(crypter_i); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_ei secret %B", &key); + crypter_i->set_key(crypter_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_er secret %B", &key); + crypter_r->set_key(crypter_r, key); + chunk_free(&key); + + if (initiator) + { + this->crypter_in = crypter_r; + this->crypter_out = crypter_i; + } + else + { + this->crypter_in = crypter_i; + this->crypter_out = crypter_r; + } + + /* SK_pi/SK_pr used for authentication => prf_auth_i, prf_auth_r */ + proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &algo); + prf_i = prf_create(algo->algorithm); + prf_r = prf_create(algo->algorithm); + + key_size = prf_i->get_key_size(prf_i); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_pi secret %B", &key); + prf_i->set_key(prf_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + DBG4(DBG_IKE, "Sk_pr secret %B", &key); + prf_r->set_key(prf_r, key); + chunk_free(&key); + + if (initiator) + { + this->auth_verify = prf_r; + this->auth_build = prf_i; + } + else + { + this->auth_verify = prf_i; + this->auth_build = prf_r; + } + + /* all done, prf_plus not needed anymore */ + prf_plus->destroy(prf_plus); + + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.add_child_sa. + */ +static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) +{ + this->child_sas->insert_last(this->child_sas, child_sa); +} + +/** + * Implementation of ike_sa_t.get_child_sa. + */ +static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi, bool inbound) +{ + iterator_t *iterator; + child_sa_t *current, *found = NULL; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_spi(current, inbound) == spi && + current->get_protocol(current) == protocol) + { + found = current; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of ike_sa_t.create_child_sa_iterator. + */ +static iterator_t* create_child_sa_iterator(private_ike_sa_t *this) +{ + return this->child_sas->create_iterator(this->child_sas, TRUE); +} + +/** + * Implementation of ike_sa_t.rekey_child_sa. + */ +static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +{ + child_sa_t *child_sa; + child_rekey_t *child_rekey; + + child_sa = get_child_sa(this, protocol, spi, TRUE); + if (child_sa) + { + child_rekey = child_rekey_create(&this->public, child_sa); + this->task_manager->queue_task(this->task_manager, &child_rekey->task); + return this->task_manager->initiate(this->task_manager); + } + return FAILED; +} + +/** + * Implementation of ike_sa_t.delete_child_sa. + */ +static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +{ + child_sa_t *child_sa; + child_delete_t *child_delete; + + child_sa = get_child_sa(this, protocol, spi, TRUE); + if (child_sa) + { + child_delete = child_delete_create(&this->public, child_sa); + this->task_manager->queue_task(this->task_manager, &child_delete->task); + return this->task_manager->initiate(this->task_manager); + } + return FAILED; +} + +/** + * Implementation of ike_sa_t.destroy_child_sa. + */ +static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi) +{ + iterator_t *iterator; + child_sa_t *child_sa; + status_t status = NOT_FOUND; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_protocol(child_sa) == protocol && + child_sa->get_spi(child_sa, TRUE) == spi) + { + child_sa->destroy(child_sa); + iterator->remove(iterator); + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of public_ike_sa_t.delete. + */ +static status_t delete_(private_ike_sa_t *this) +{ + ike_delete_t *ike_delete; + + switch (this->state) + { + case IKE_ESTABLISHED: + DBG1(DBG_IKE, "deleting IKE_SA"); + /* do not log when rekeyed */ + case IKE_REKEYING: + ike_delete = ike_delete_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, &ike_delete->task); + return this->task_manager->initiate(this->task_manager); + default: + DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification", + ike_sa_state_names, this->state); + break; + } + return DESTROY_ME; +} + +/** + * Implementation of ike_sa_t.rekey. + */ +static status_t rekey(private_ike_sa_t *this) +{ + ike_rekey_t *ike_rekey; + + ike_rekey = ike_rekey_create(&this->public, TRUE); + + this->task_manager->queue_task(this->task_manager, &ike_rekey->task); + return this->task_manager->initiate(this->task_manager); +} + +/** + * Implementation of ike_sa_t.reestablish + */ +static void reestablish(private_ike_sa_t *this) +{ + private_ike_sa_t *other; + iterator_t *iterator; + child_sa_t *child_sa; + policy_t *policy; + task_t *task; + job_t *job; + + other = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new( + charon->ike_sa_manager, TRUE); + + apply_config(other, this->connection, this->policy); + other->other_host->destroy(other->other_host); + other->other_host = this->other_host->clone(this->other_host); + + if (this->state == IKE_ESTABLISHED) + { + task = (task_t*)ike_init_create(&other->public, TRUE, NULL); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_natd_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_cert_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_config_create(&other->public, other->policy); + other->task_manager->queue_task(other->task_manager, task); + task = (task_t*)ike_auth_create(&other->public, TRUE); + other->task_manager->queue_task(other->task_manager, task); + } + + other->task_manager->adopt_tasks(other->task_manager, this->task_manager); + + /* Create task for established children, adopt routed children directly */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while(iterator->iterate(iterator, (void**)&child_sa)) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_ROUTED: + { + iterator->remove(iterator); + other->child_sas->insert_first(other->child_sas, child_sa); + break; + } + default: + { + policy = child_sa->get_policy(child_sa); + task = (task_t*)child_create_create(&other->public, policy); + other->task_manager->queue_task(other->task_manager, task); + break; + } + } + } + iterator->destroy(iterator); + + other->task_manager->initiate(other->task_manager); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, &other->public); + + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->job_queue->add(charon->job_queue, job); +} + +/** + * Implementation of ike_sa_t.inherit. + */ +static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) +{ + child_sa_t *child_sa; + host_t *ip; + + /* apply hosts and ids */ + this->my_host->destroy(this->my_host); + this->other_host->destroy(this->other_host); + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + this->my_host = other->my_host->clone(other->my_host); + this->other_host = other->other_host->clone(other->other_host); + this->my_id = other->my_id->clone(other->my_id); + this->other_id = other->other_id->clone(other->other_id); + + /* apply virtual assigned IPs... */ + if (other->my_virtual_ip) + { + this->my_virtual_ip = other->my_virtual_ip; + other->my_virtual_ip = NULL; + } + if (other->other_virtual_ip) + { + this->other_virtual_ip = other->other_virtual_ip; + other->other_virtual_ip = NULL; + } + + /* ... and DNS servers */ + while (other->dns_servers->remove_last(other->dns_servers, + (void**)&ip) == SUCCESS) + { + this->dns_servers->insert_first(this->dns_servers, ip); + } + + /* adopt all children */ + while (other->child_sas->remove_last(other->child_sas, + (void**)&child_sa) == SUCCESS) + { + this->child_sas->insert_first(this->child_sas, (void*)child_sa); + } + + /* move pending tasks to the new IKE_SA */ + this->task_manager->adopt_tasks(this->task_manager, other->task_manager); + + /* we have to initate here, there may be new tasks to handle */ + return this->task_manager->initiate(this->task_manager); +} + +/** + * Implementation of ike_sa_t.is_natt_enabled. + */ +static bool is_natt_enabled(private_ike_sa_t *this) +{ + return this->nat_here || this->nat_there; +} + +/** + * Implementation of ike_sa_t.enable_natt. + */ +static void enable_natt(private_ike_sa_t *this, bool local) +{ + if (local) + { + DBG1(DBG_IKE, "local host is behind NAT, scheduling keep alives"); + this->nat_here = TRUE; + send_keepalive(this); + } + else + { + DBG1(DBG_IKE, "remote host is behind NAT"); + this->nat_there = TRUE; + } +} + +/** + * Implementation of ike_sa_t.set_virtual_ip + */ +static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip) +{ + if (local) + { + DBG1(DBG_IKE, "installing new virtual IP %H", ip); + if (this->my_virtual_ip) + { + DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip, + this->my_host); + this->my_virtual_ip->destroy(this->my_virtual_ip); + } + if (charon->kernel_interface->add_ip(charon->kernel_interface, ip, + this->my_host) == SUCCESS) + { + this->my_virtual_ip = ip->clone(ip); + } + else + { + DBG1(DBG_IKE, "installing virtual IP %H failed", ip); + this->my_virtual_ip = NULL; + } + } + else + { + DESTROY_IF(this->other_virtual_ip); + this->other_virtual_ip = ip->clone(ip); + } +} + +/** + * Implementation of ike_sa_t.get_virtual_ip + */ +static host_t* get_virtual_ip(private_ike_sa_t *this, bool local) +{ + if (local) + { + return this->my_virtual_ip; + } + else + { + return this->other_virtual_ip; + } +} + +/** + * Implementation of ike_sa_t.remove_dns_server + */ +static void remove_dns_servers(private_ike_sa_t *this) +{ + FILE *file; + struct stat stats; + chunk_t contents, line, orig_line, token; + char string[INET6_ADDRSTRLEN]; + host_t *ip; + iterator_t *iterator; + + if (this->dns_servers->get_count(this->dns_servers) == 0) + { + /* don't touch anything if we have no nameservers installed */ + return; + } + + file = fopen(RESOLV_CONF, "r"); + if (file == NULL || stat(RESOLV_CONF, &stats) != 0) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + contents = chunk_alloca((size_t)stats.st_size); + + if (fread(contents.ptr, 1, contents.len, file) != contents.len) + { + DBG1(DBG_IKE, "unable to read DNS configuration file: %m"); + fclose(file); + return; + } + + fclose(file); + file = fopen(RESOLV_CONF, "w"); + if (file == NULL) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + iterator = this->dns_servers->create_iterator(this->dns_servers, TRUE); + while (fetchline(&contents, &line)) + { + bool found = FALSE; + orig_line = line; + if (extract_token(&token, ' ', &line) && + strncasecmp(token.ptr, "nameserver", token.len) == 0) + { + if (!extract_token(&token, ' ', &line)) + { + token = line; + } + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&ip)) + { + snprintf(string, sizeof(string), "%H", ip); + if (strlen(string) == token.len && + strncmp(token.ptr, string, token.len) == 0) + { + iterator->remove(iterator); + ip->destroy(ip); + found = TRUE; + break; + } + } + } + + if (!found) + { + /* write line untouched back to file */ + fwrite(orig_line.ptr, orig_line.len, 1, file); + fprintf(file, "\n"); + } + } + iterator->destroy(iterator); + fclose(file); +} + +/** + * Implementation of ike_sa_t.add_dns_server + */ +static void add_dns_server(private_ike_sa_t *this, host_t *dns) +{ + FILE *file; + struct stat stats; + chunk_t contents; + + DBG1(DBG_IKE, "installing DNS server %H", dns); + + file = fopen(RESOLV_CONF, "a+"); + if (file == NULL || stat(RESOLV_CONF, &stats) != 0) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + contents = chunk_alloca(stats.st_size); + + if (fread(contents.ptr, 1, contents.len, file) != contents.len) + { + DBG1(DBG_IKE, "unable to read DNS configuration file: %m"); + fclose(file); + return; + } + + fclose(file); + file = fopen(RESOLV_CONF, "w"); + if (file == NULL) + { + DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF); + return; + } + + if (fprintf(file, "nameserver %H # added by strongSwan, assigned by %D\n", + dns, this->other_id) < 0) + { + DBG1(DBG_IKE, "unable to write DNS configuration: %m"); + } + else + { + this->dns_servers->insert_last(this->dns_servers, dns->clone(dns)); + } + fwrite(contents.ptr, contents.len, 1, file); + + fclose(file); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + int written = 0; + bool reauth = FALSE; + private_ike_sa_t *this = *((private_ike_sa_t**)(args[0])); + + if (this->connection) + { + reauth = this->connection->get_reauth(this->connection); + } + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + written = fprintf(stream, "%12s[%d]: %N, %H[%D]...%H[%D]", get_name(this), + this->unique_id, ike_sa_state_names, this->state, + this->my_host, this->my_id, this->other_host, + this->other_id); + written += fprintf(stream, "\n%12s[%d]: IKE SPIs: %J, %s in %ds", + get_name(this), this->unique_id, this->ike_sa_id, + this->connection && reauth? "reauthentication":"rekeying", + this->time.rekey - time(NULL)); + + if (info->alt) + { + + } + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_IKE_SA, print, arginfo_ptr); +} + +/** + * Implementation of ike_sa_t.destroy. + */ +static void destroy(private_ike_sa_t *this) +{ + this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); + + DESTROY_IF(this->crypter_in); + DESTROY_IF(this->crypter_out); + DESTROY_IF(this->signer_in); + DESTROY_IF(this->signer_out); + DESTROY_IF(this->prf); + DESTROY_IF(this->child_prf); + DESTROY_IF(this->auth_verify); + DESTROY_IF(this->auth_build); + + if (this->my_virtual_ip) + { + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip, this->my_host); + this->my_virtual_ip->destroy(this->my_virtual_ip); + } + DESTROY_IF(this->other_virtual_ip); + + remove_dns_servers(this); + this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy)); + + DESTROY_IF(this->my_host); + DESTROY_IF(this->other_host); + DESTROY_IF(this->my_id); + DESTROY_IF(this->other_id); + + DESTROY_IF(this->connection); + DESTROY_IF(this->policy); + + this->ike_sa_id->destroy(this->ike_sa_id); + this->task_manager->destroy(this->task_manager); + free(this); +} + +/* + * Described in header. + */ +ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) +{ + private_ike_sa_t *this = malloc_thing(private_ike_sa_t); + static u_int32_t unique_id = 0; + + /* Public functions */ + this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state; + this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state; + this->public.get_name = (char*(*)(ike_sa_t*))get_name; + this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; + this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate; + this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route; + this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute; + this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire; + this->public.get_connection = (connection_t*(*)(ike_sa_t*))get_connection; + this->public.set_connection = (void(*)(ike_sa_t*,connection_t*))set_connection; + this->public.get_policy = (policy_t*(*)(ike_sa_t*))get_policy; + this->public.set_policy = (void(*)(ike_sa_t*,policy_t*))set_policy; + this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; + this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host; + this->public.set_my_host = (void(*)(ike_sa_t*,host_t*)) set_my_host; + this->public.get_other_host = (host_t*(*)(ike_sa_t*)) get_other_host; + this->public.set_other_host = (void(*)(ike_sa_t*,host_t*)) set_other_host; + this->public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id; + this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id; + this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id; + this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id; + this->public.retransmit = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit; + this->public.delete = (status_t(*)(ike_sa_t*))delete_; + this->public.destroy = (void(*)(ike_sa_t*))destroy; + this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; + this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive; + this->public.get_prf = (prf_t *(*) (ike_sa_t *)) get_prf; + this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf; + this->public.get_auth_verify = (prf_t *(*) (ike_sa_t *)) get_auth_verify; + this->public.get_auth_build = (prf_t *(*) (ike_sa_t *)) get_auth_build; + this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys; + this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa; + this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; + this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator; + this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa; + this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa; + this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa; + this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt; + this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled; + this->public.rekey = (status_t(*)(ike_sa_t*))rekey; + this->public.reestablish = (void(*)(ike_sa_t*))reestablish; + this->public.inherit = (status_t(*)(ike_sa_t*,ike_sa_t*))inherit; + this->public.generate_message = (status_t(*)(ike_sa_t*,message_t*,packet_t**))generate_message; + this->public.reset = (void(*)(ike_sa_t*))reset; + this->public.get_unique_id = (u_int32_t(*)(ike_sa_t*))get_unique_id; + this->public.set_virtual_ip = (void(*)(ike_sa_t*,bool,host_t*))set_virtual_ip; + this->public.get_virtual_ip = (host_t*(*)(ike_sa_t*,bool))get_virtual_ip; + this->public.add_dns_server = (void(*)(ike_sa_t*,host_t*))add_dns_server; + + /* initialize private fields */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->child_sas = linked_list_create(); + this->my_host = host_create_any(AF_INET); + this->other_host = host_create_any(AF_INET); + this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); + this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); + this->crypter_in = NULL; + this->crypter_out = NULL; + this->signer_in = NULL; + this->signer_out = NULL; + this->prf = NULL; + this->auth_verify = NULL; + this->auth_build = NULL; + this->child_prf = NULL; + this->nat_here = FALSE; + this->nat_there = FALSE; + this->state = IKE_CREATED; + this->time.inbound = this->time.outbound = time(NULL); + this->time.established = 0; + this->time.rekey = 0; + this->time.delete = 0; + this->connection = NULL; + this->policy = NULL; + this->task_manager = task_manager_create(&this->public); + this->unique_id = ++unique_id; + this->my_virtual_ip = NULL; + this->other_virtual_ip = NULL; + this->dns_servers = linked_list_create(); + this->keyingtry = 0; + + return &this->public; +} diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h new file mode 100644 index 000000000..604ec94a9 --- /dev/null +++ b/src/charon/sa/ike_sa.h @@ -0,0 +1,649 @@ +/** + * @file ike_sa.h + * + * @brief Interface of ike_sa_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_SA_H_ +#define IKE_SA_H_ + +typedef enum ike_sa_state_t ike_sa_state_t; +typedef struct ike_sa_t ike_sa_t; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief State of an IKE_SA. + * + * An IKE_SA passes various states in its lifetime. A newly created + * SA is in the state CREATED. + * @verbatim + +----------------+ + ¦ SA_CREATED ¦ + +----------------+ + ¦ + on initiate()---> ¦ <----- on IKE_SA_INIT received + V + +----------------+ + ¦ SA_CONNECTING ¦ + +----------------+ + ¦ + ¦ <----- on IKE_AUTH successfully completed + V + +----------------+ + ¦ SA_ESTABLISHED ¦-------------------------+ <-- on rekeying + +----------------+ ¦ + ¦ V + on delete()---> ¦ <----- on IKE_SA +-------------+ + ¦ delete request ¦ SA_REKEYING ¦ + ¦ received +-------------+ + V ¦ + +----------------+ ¦ + ¦ SA_DELETING ¦<------------------------+ <-- after rekeying + +----------------+ + ¦ + ¦ <----- after delete() acknowledged + ¦ + \V/ + X + / \ + @endverbatim + * + * @ingroup sa + */ +enum ike_sa_state_t { + + /** + * IKE_SA just got created, but is not initiating nor responding yet. + */ + IKE_CREATED, + + /** + * IKE_SA gets initiated actively or passively + */ + IKE_CONNECTING, + + /** + * IKE_SA is fully established + */ + IKE_ESTABLISHED, + + /** + * IKE_SA rekeying in progress + */ + IKE_REKEYING, + + /** + * IKE_SA is in progress of deletion + */ + IKE_DELETING, +}; + +/** + * enum names for ike_sa_state_t. + */ +extern enum_name_t *ike_sa_state_names; + +/** + * @brief Class ike_sa_t representing an IKE_SA. + * + * An IKE_SA contains crypto information related to a connection + * with a peer. It contains multiple IPsec CHILD_SA, for which + * it is responsible. All traffic is handled by an IKE_SA, using + * the task manager and its tasks. + * + * @b Constructors: + * - ike_sa_create() + * + * @ingroup sa + */ +struct ike_sa_t { + + /** + * @brief Get the id of the SA. + * + * Returned ike_sa_id_t object is not getting cloned! + * + * @param this calling object + * @return ike_sa's ike_sa_id_t + */ + ike_sa_id_t* (*get_id) (ike_sa_t *this); + + /** + * @brief Get the numerical ID uniquely defining this IKE_SA. + * + * @param this calling object + * @return unique ID + */ + u_int32_t (*get_unique_id) (ike_sa_t *this); + + /** + * @brief Get the state of the IKE_SA. + * + * @param this calling object + * @return state of the IKE_SA + */ + ike_sa_state_t (*get_state) (ike_sa_t *this); + + /** + * @brief Set the state of the IKE_SA. + * + * @param this calling object + * @param state state to set for the IKE_SA + */ + void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa); + + /** + * @brief Get the name of the connection this IKE_SA uses. + * + * @param this calling object + * @return name + */ + char* (*get_name) (ike_sa_t *this); + + /** + * @brief Get the own host address. + * + * @param this calling object + * @return host address + */ + host_t* (*get_my_host) (ike_sa_t *this); + + /** + * @brief Set the own host address. + * + * @param this calling object + * @param me host address + */ + void (*set_my_host) (ike_sa_t *this, host_t *me); + + /** + * @brief Get the other peers host address. + * + * @param this calling object + * @return host address + */ + host_t* (*get_other_host) (ike_sa_t *this); + + /** + * @brief Set the others host address. + * + * @param this calling object + * @param other host address + */ + void (*set_other_host) (ike_sa_t *this, host_t *other); + + /** + * @brief Get the own identification. + * + * @param this calling object + * @return identification + */ + identification_t* (*get_my_id) (ike_sa_t *this); + + /** + * @brief Set the own identification. + * + * @param this calling object + * @param me identification + */ + void (*set_my_id) (ike_sa_t *this, identification_t *me); + + /** + * @brief Get the other peers identification. + * + * @param this calling object + * @return identification + */ + identification_t* (*get_other_id) (ike_sa_t *this); + + /** + * @brief Set the other peers identification. + * + * @param this calling object + * @param other identification + */ + void (*set_other_id) (ike_sa_t *this, identification_t *other); + + /** + * @brief Get the connection used by this IKE_SA. + * + * @param this calling object + * @return connection + */ + connection_t* (*get_connection) (ike_sa_t *this); + + /** + * @brief Set the connection to use with this IKE_SA. + * + * @param this calling object + * @param connection connection to use + */ + void (*set_connection) (ike_sa_t *this, connection_t* connection); + + /** + * @brief Get the policy used by this IKE_SA. + * + * @param this calling object + * @return policy + */ + policy_t* (*get_policy) (ike_sa_t *this); + + /** + * @brief Set the policy to use with this IKE_SA. + * + * @param this calling object + * @param policy policy to use + */ + void (*set_policy) (ike_sa_t *this, policy_t *policy); + + /** + * @brief Initiate a new connection. + * + * The policy/connection is owned by the IKE_SA after the call, so + * do not modify or destroy it. + * + * @param this calling object + * @param connection connection to initiate + * @param policy policy to set up + * @return + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted + */ + status_t (*initiate) (ike_sa_t *this, connection_t *connection, policy_t *policy); + + /** + * @brief Route a policy in the kernel. + * + * Installs the policies in the kernel. If traffic matches, + * the kernel requests connection setup from the IKE_SA via acquire(). + * + * @param this calling object + * @param connection connection definition used for routing + * @param policy policy to route + * @return + * - SUCCESS if routed successfully + * - FAILED if routing failed + */ + status_t (*route) (ike_sa_t *this, connection_t *connection, policy_t *policy); + + /** + * @brief Unroute a policy in the kernel previously routed. + * + * @param this calling object + * @param policy policy to route + * @return + * - SUCCESS if route removed + * - DESTROY_ME if last route was removed from + * an IKE_SA which was not established + */ + status_t (*unroute) (ike_sa_t *this, policy_t *policy); + + /** + * @brief Acquire connection setup for a policy. + * + * If an installed policy raises an acquire, the kernel calls + * this function to establish the CHILD_SA (and maybe the IKE_SA). + * + * @param this calling object + * @param reqid reqid of the CHILD_SA the policy belongs to. + * @return + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted + */ + status_t (*acquire) (ike_sa_t *this, u_int32_t reqid); + + /** + * @brief Initiates the deletion of an IKE_SA. + * + * Sends a delete message to the remote peer and waits for + * its response. If the response comes in, or a timeout occurs, + * the IKE SA gets deleted. + * + * @param this calling object + * @return + * - SUCCESS if deletion is initialized + * - INVALID_STATE, if the IKE_SA is not in + * an established state and can not be + * delete (but destroyed). + */ + status_t (*delete) (ike_sa_t *this); + + /** + * @brief Processes a incoming IKEv2-Message. + * + * Message processing may fail. If a critical failure occurs, + * process_message() return DESTROY_ME. Then the caller must + * destroy the IKE_SA immediatly, as it is unusable. + * + * @param this calling object + * @param message message to process + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted + */ + status_t (*process_message) (ike_sa_t *this, message_t *message); + + /** + * @brief Generate a IKE message to send it to the peer. + * + * This method generates all payloads in the message and encrypts/signs + * the packet. + * + * @param this calling object + * @param message message to generate + * @param packet generated output packet + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted + */ + status_t (*generate_message) (ike_sa_t *this, message_t *message, + packet_t **packet); + + /** + * @brief Retransmits a request. + * + * @param this calling object + * @param message_id ID of the request to retransmit + * @return + * - SUCCESS + * - NOT_FOUND if request doesn't have to be retransmited + */ + status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id); + + /** + * @brief Sends a DPD request to the peer. + * + * To check if a peer is still alive, periodic + * empty INFORMATIONAL messages are sent if no + * other traffic was received. + * + * @param this calling object + * @return + * - SUCCESS + * - DESTROY_ME, if peer did not respond + */ + status_t (*send_dpd) (ike_sa_t *this); + + /** + * @brief Sends a keep alive packet. + * + * To refresh NAT tables in a NAT router + * between the peers, periodic empty + * UDP packets are sent if no other traffic + * was sent. + * + * @param this calling object + */ + void (*send_keepalive) (ike_sa_t *this); + + /** + * @brief Check if NAT traversal is enabled for this IKE_SA. + * + * @param this calling object + * @return TRUE if NAT traversal enabled + */ + bool (*is_natt_enabled) (ike_sa_t *this); + + /** + * @brief Enable NAT detection for this IKE_SA. + * + * If a Network address translation is detected with + * NAT_DETECTION notifys, a SA must switch to ports + * 4500. To enable this behavior, call enable_natt(). + * It is relevant which peer is NATted, this is specified + * with the "local" parameter. Call it twice when both + * are NATted. + * + * @param this calling object + * @param local TRUE, if we are NATted, FALSE if other + */ + void (*enable_natt) (ike_sa_t *this, bool local); + + /** + * @brief Derive all keys and create the transforms for IKE communication. + * + * Keys are derived using the diffie hellman secret, nonces and internal + * stored SPIs. + * Key derivation differs when an IKE_SA is set up to replace an + * existing IKE_SA (rekeying). The SK_d key from the old IKE_SA + * is included in the derivation process. + * + * @param this calling object + * @param proposal proposal which contains algorithms to use + * @param secret secret derived from DH exchange, gets freed + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + * @param initiator TRUE if initiator, FALSE otherwise + * @param child_prf PRF with SK_d key when rekeying, NULL otherwise + * @param old_prf general purpose PRF of old SA when rekeying + */ + status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret, + chunk_t nonce_i, chunk_t nonce_r, + bool initiator, prf_t *child_prf, prf_t *old_prf); + + /** + * @brief Get the multi purpose prf. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf) (ike_sa_t *this); + + /** + * @brief Get the prf-object, which is used to derive keys for child SAs. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_child_prf) (ike_sa_t *this); + + /** + * @brief Get the prf to build outgoing authentication data. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_auth_build) (ike_sa_t *this); + + /** + * @brief Get the prf to verify incoming authentication data. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_auth_verify) (ike_sa_t *this); + + /** + * @brief Associates a child SA to this IKE SA + * + * @param this calling object + * @param child_sa child_sa to add + */ + void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); + + /** + * @brief Get a CHILD_SA identified by protocol and SPI. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi SPI of the CHILD_SA + * @param inbound TRUE if SPI is inbound, FALSE if outbound + * @return child_sa, or NULL if none found + */ + child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi, bool inbound); + + /** + * @brief Create an iterator over all CHILD_SAs. + * + * @param this calling object + * @return iterator + */ + iterator_t* (*create_child_sa_iterator) (ike_sa_t *this); + + /** + * @brief Rekey the CHILD SA with the specified reqid. + * + * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS, if rekeying initiated + */ + status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + + /** + * @brief Close the CHILD SA with the specified protocol/SPI. + * + * Looks for a CHILD SA owned by this IKE_SA, deletes it and + * notify's the remote peer about the delete. The associated + * states and policies in the kernel get deleted, if they exist. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS, if delete message sent + */ + status_t (*delete_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + + /** + * @brief Destroy a CHILD SA with the specified protocol/SPI. + * + * Looks for a CHILD SA owned by this IKE_SA and destroys it. + * + * @param this calling object + * @param protocol protocol of the SA + * @param spi inbound SPI of the CHILD_SA + * @return + * - NOT_FOUND, if IKE_SA has no such CHILD_SA + * - SUCCESS + */ + status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + + /** + * @brief Rekey the IKE_SA. + * + * Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA. + * + * @param this calling object + * @return - SUCCESS, if IKE_SA rekeying initiated + */ + status_t (*rekey) (ike_sa_t *this); + + /** + * @brief Restablish the IKE_SA. + * + * Create a completely new IKE_SA with authentication, recreates all children + * within the IKE_SA, but lets the old IKE_SA untouched. + * + * @param this calling object + */ + void (*reestablish) (ike_sa_t *this); + + /** + * @brief Set the virtual IP to use for this IKE_SA and its children. + * + * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same + * lifetime as the IKE_SA. + * + * @param this calling object + */ + void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); + + /** + * @brief Get the virtual IP configured. + * + * @param this calling object + * @param local TRUE to get local virtual IP, FALSE for remote + */ + host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); + + /** + * @brief Add a DNS server to the system. + * + * An IRAS may send a DNS server. To use it, it is installed on the + * system. The DNS entry has a lifetime until the IKE_SA gets closed. + * + * @param this calling object + * @param dns DNS server to install on the system + */ + void (*add_dns_server) (ike_sa_t *this, host_t *dns); + + /** + * @brief Inherit all attributes of other to this after rekeying. + * + * When rekeying is completed, all CHILD_SAs, the virtual IP and all + * outstanding tasks are moved from other to this. + * As this call may initiate inherited tasks, a status is returned. + * + * @param this calling object + * @param other other task to inherit from + * @return DESTROY_ME if initiation of inherited task failed + */ + status_t (*inherit) (ike_sa_t *this, ike_sa_t *other); + + /** + * @brief Reset the IKE_SA, useable when initiating fails + * + * @param this calling object + */ + void (*reset) (ike_sa_t *this); + + /** + * @brief Destroys a ike_sa_t object. + * + * @param this calling object + */ + void (*destroy) (ike_sa_t *this); +}; + +/** + * @brief Creates an ike_sa_t object with a specific ID. + * + * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA + * @return ike_sa_t object + * + * @ingroup sa + */ +ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id); + +#endif /* IKE_SA_H_ */ diff --git a/src/charon/sa/ike_sa_id.c b/src/charon/sa/ike_sa_id.c new file mode 100644 index 000000000..c143fc0ba --- /dev/null +++ b/src/charon/sa/ike_sa_id.c @@ -0,0 +1,215 @@ +/** + * @file ike_sa_id.c + * + * @brief Implementation of ike_sa_id_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "ike_sa_id.h" + +#include +#include + + +typedef struct private_ike_sa_id_t private_ike_sa_id_t; + +/** + * Private data of an ike_sa_id_t object. + */ +struct private_ike_sa_id_t { + /** + * Public interface of ike_sa_id_t. + */ + ike_sa_id_t public; + + /** + * SPI of Initiator. + */ + u_int64_t initiator_spi; + + /** + * SPI of Responder. + */ + u_int64_t responder_spi; + + /** + * Role for specific IKE_SA. + */ + bool is_initiator_flag; +}; + +/** + * Implementation of ike_sa_id_t.set_responder_spi. + */ +static void set_responder_spi (private_ike_sa_id_t *this, u_int64_t responder_spi) +{ + this->responder_spi = responder_spi; +} + +/** + * Implementation of ike_sa_id_t.set_initiator_spi. + */ +static void set_initiator_spi(private_ike_sa_id_t *this, u_int64_t initiator_spi) +{ + this->initiator_spi = initiator_spi; +} + +/** + * Implementation of ike_sa_id_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi (private_ike_sa_id_t *this) +{ + return this->initiator_spi; +} + +/** + * Implementation of ike_sa_id_t.get_responder_spi. + */ +static u_int64_t get_responder_spi (private_ike_sa_id_t *this) +{ + return this->responder_spi; +} + +/** + * Implementation of ike_sa_id_t.equals. + */ +static bool equals (private_ike_sa_id_t *this, private_ike_sa_id_t *other) +{ + if (other == NULL) + { + return FALSE; + } + if ((this->is_initiator_flag == other->is_initiator_flag) && + (this->initiator_spi == other->initiator_spi) && + (this->responder_spi == other->responder_spi)) + { + /* private_ike_sa_id's are equal */ + return TRUE; + } + else + { + /* private_ike_sa_id's are not equal */ + return FALSE; + } +} + +/** + * Implementation of ike_sa_id_t.replace_values. + */ +static void replace_values(private_ike_sa_id_t *this, private_ike_sa_id_t *other) +{ + this->initiator_spi = other->initiator_spi; + this->responder_spi = other->responder_spi; + this->is_initiator_flag = other->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.is_initiator. + */ +static bool is_initiator(private_ike_sa_id_t *this) +{ + return this->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.switch_initiator. + */ +static bool switch_initiator(private_ike_sa_id_t *this) +{ + if (this->is_initiator_flag) + { + this->is_initiator_flag = FALSE; + } + else + { + this->is_initiator_flag = TRUE; + } + return this->is_initiator_flag; +} + +/** + * Implementation of ike_sa_id_t.clone. + */ +static ike_sa_id_t* clone_(private_ike_sa_id_t *this) +{ + return ike_sa_id_create(this->initiator_spi, this->responder_spi, this->is_initiator_flag); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_ike_sa_id_t *this = *((private_ike_sa_id_t**)(args[0])); + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + return fprintf(stream, "0x%0llx_i%s 0x%0llx_r%s", + this->initiator_spi, + this->is_initiator_flag ? "*" : "", + this->responder_spi, + this->is_initiator_flag ? "" : "*"); +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_IKE_SA_ID, print, arginfo_ptr); +} + +/** + * Implementation of ike_sa_id_t.destroy. + */ +static void destroy(private_ike_sa_id_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiator_flag) +{ + private_ike_sa_id_t *this = malloc_thing(private_ike_sa_id_t); + + /* public functions */ + this->public.set_responder_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_responder_spi; + this->public.set_initiator_spi = (void(*)(ike_sa_id_t*,u_int64_t)) set_initiator_spi; + this->public.get_responder_spi = (u_int64_t(*)(ike_sa_id_t*)) get_responder_spi; + this->public.get_initiator_spi = (u_int64_t(*)(ike_sa_id_t*)) get_initiator_spi; + this->public.equals = (bool(*)(ike_sa_id_t*,ike_sa_id_t*)) equals; + this->public.replace_values = (void(*)(ike_sa_id_t*,ike_sa_id_t*)) replace_values; + this->public.is_initiator = (bool(*)(ike_sa_id_t*)) is_initiator; + this->public.switch_initiator = (bool(*)(ike_sa_id_t*)) switch_initiator; + this->public.clone = (ike_sa_id_t*(*)(ike_sa_id_t*)) clone_; + this->public.destroy = (void(*)(ike_sa_id_t*))destroy; + + /* private data */ + this->initiator_spi = initiator_spi; + this->responder_spi = responder_spi; + this->is_initiator_flag = is_initiator_flag; + + return &this->public; +} diff --git a/src/charon/sa/ike_sa_id.h b/src/charon/sa/ike_sa_id.h new file mode 100644 index 000000000..0606b7222 --- /dev/null +++ b/src/charon/sa/ike_sa_id.h @@ -0,0 +1,147 @@ +/** + * @file ike_sa_id.h + * + * @brief Interface of ike_sa_id_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef IKE_SA_ID_H_ +#define IKE_SA_ID_H_ + +typedef struct ike_sa_id_t ike_sa_id_t; + +#include + + +/** + * @brief An object of type ike_sa_id_t is used to identify an IKE_SA. + * + * An IKE_SA is identified by its initiator and responder spi's. + * Additionaly it contains the role of the actual running IKEv2-Daemon + * for the specific IKE_SA (original initiator or responder). + * + * @b Constructors: + * - ike_sa_id_create() + * + * @ingroup sa + */ +struct ike_sa_id_t { + + /** + * @brief Set the SPI of the responder. + * + * This function is called when a request or reply of a IKE_SA_INIT is received. + * + * @param this calling object + * @param responder_spi SPI of responder to set + */ + void (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi); + + /** + * @brief Set the SPI of the initiator. + * + * @param this calling object + * @param initiator_spi SPI to set + */ + void (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi); + + /** + * @brief Get the initiator SPI. + * + * @param this calling object + * @return SPI of the initiator + */ + u_int64_t (*get_initiator_spi) (ike_sa_id_t *this); + + /** + * @brief Get the responder SPI. + * + * @param this calling object + * @return SPI of the responder + */ + u_int64_t (*get_responder_spi) (ike_sa_id_t *this); + + /** + * @brief Check if two ike_sa_id_t objects are equal. + * + * Two ike_sa_id_t objects are equal if both SPI values and the role matches. + * + * @param this calling object + * @param other ike_sa_id_t object to check if equal + * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise + */ + bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other); + + /** + * @brief Replace all values of a given ike_sa_id_t object with values. + * from another ike_sa_id_t object. + * + * After calling this function, both objects are equal. + * + * @param this calling object + * @param other ike_sa_id_t object from which values will be taken + */ + void (*replace_values) (ike_sa_id_t *this, ike_sa_id_t *other); + + /** + * @brief Get the initiator flag. + * + * @param this calling object + * @return TRUE if we are the original initator + */ + bool (*is_initiator) (ike_sa_id_t *this); + + /** + * @brief Switche the original initiator flag. + * + * @param this calling object + * @return TRUE if we are the original initator after switch, FALSE otherwise + */ + bool (*switch_initiator) (ike_sa_id_t *this); + + /** + * @brief Clones a given ike_sa_id_t object. + * + * @param this calling object + * @return cloned ike_sa_id_t object + */ + ike_sa_id_t *(*clone) (ike_sa_id_t *this); + + /** + * @brief Destroys an ike_sa_id_t object. + * + * @param this calling object + */ + void (*destroy) (ike_sa_id_t *this); +}; + +/** + * @brief Creates an ike_sa_id_t object with specific SPI's and defined role. + * + * @param initiator_spi initiators SPI + * @param responder_spi responders SPI + * @param is_initiaor TRUE if we are the original initiator + * @return ike_sa_id_t object + * + * @ingroup sa + */ +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiaor); + +#endif /*IKE_SA_ID_H_*/ diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c new file mode 100644 index 000000000..791ef805e --- /dev/null +++ b/src/charon/sa/ike_sa_manager.c @@ -0,0 +1,914 @@ +/** + * @file ike_sa_manager.c + * + * @brief Implementation of ike_sa_mananger_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "ike_sa_manager.h" + +#include +#include +#include +#include + +typedef struct entry_t entry_t; + +/** + * An entry in the linked list, contains IKE_SA, locking and lookup data. + */ +struct entry_t { + + /** + * Number of threads waiting for this ike_sa_t object. + */ + int waiting_threads; + + /** + * Condvar where threads can wait until ike_sa_t object is free for use again. + */ + pthread_cond_t condvar; + + /** + * Is this ike_sa currently checked out? + */ + bool checked_out; + + /** + * Does this SA drives out new threads? + */ + bool driveout_new_threads; + + /** + * Does this SA drives out waiting threads? + */ + bool driveout_waiting_threads; + + /** + * Identifiaction of an IKE_SA (SPIs). + */ + ike_sa_id_t *ike_sa_id; + + /** + * The contained ike_sa_t object. + */ + ike_sa_t *ike_sa; + + /** + * hash of the IKE_SA_INIT message, used to detect retransmissions + */ + chunk_t init_hash; + + /** + * message ID currently processing, if any + */ + u_int32_t message_id; +}; + +/** + * Implementation of entry_t.destroy. + */ +static status_t entry_destroy(entry_t *this) +{ + /* also destroy IKE SA */ + this->ike_sa->destroy(this->ike_sa); + this->ike_sa_id->destroy(this->ike_sa_id); + chunk_free(&this->init_hash); + free(this); + return SUCCESS; +} + +/** + * Creates a new entry for the ike_sa_t list. + */ +static entry_t *entry_create(ike_sa_id_t *ike_sa_id) +{ + entry_t *this = malloc_thing(entry_t); + + this->waiting_threads = 0; + pthread_cond_init(&this->condvar, NULL); + + /* we set checkout flag when we really give it out */ + this->checked_out = FALSE; + this->driveout_new_threads = FALSE; + this->driveout_waiting_threads = FALSE; + this->message_id = -1; + this->init_hash = chunk_empty; + + /* ike_sa_id is always cloned */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + /* create new ike_sa */ + this->ike_sa = ike_sa_create(ike_sa_id); + + return this; +} + + +typedef struct private_ike_sa_manager_t private_ike_sa_manager_t; + +/** + * Additional private members of ike_sa_manager_t. + */ +struct private_ike_sa_manager_t { + /** + * Public interface of ike_sa_manager_t. + */ + ike_sa_manager_t public; + + /** + * Lock for exclusivly accessing the manager. + */ + pthread_mutex_t mutex; + + /** + * Linked list with entries for the ike_sa_t objects. + */ + linked_list_t *ike_sa_list; + + /** + * A randomizer, to get random SPIs for our side + */ + randomizer_t *randomizer; + + /** + * SHA1 hasher for IKE_SA_INIT retransmit detection + */ + hasher_t *hasher; +}; + +/** + * Implementation of private_ike_sa_manager_t.get_entry_by_id. + */ +static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *current; + status_t status; + + /* create iterator over list of ike_sa's */ + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) + { + DBG2(DBG_MGR, "found entry by both SPIs"); + *entry = current; + status = SUCCESS; + break; + } + if (ike_sa_id->get_responder_spi(ike_sa_id) == 0 || + current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0) + { + /* seems to be a half ready ike_sa */ + if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == + ike_sa_id->get_initiator_spi(ike_sa_id)) && + (current->ike_sa_id->is_initiator(ike_sa_id) == + ike_sa_id->is_initiator(current->ike_sa_id))) + { + DBG2(DBG_MGR, "found entry by initiator SPI"); + *entry = current; + status = SUCCESS; + break; + } + } + } + + iterator->destroy(iterator); + return status; +} + +/** + * Implementation of private_ike_sa_manager_t.get_entry_by_sa. + */ +static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *current; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->iterate(iterator, (void**)¤t)) + { + /* only pointers are compared */ + if (current->ike_sa == ike_sa) + { + DBG2(DBG_MGR, "found entry by pointer"); + *entry = current; + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + + return status; +} + +/** + * Implementation of private_ike_sa_manager_s.delete_entry. + */ +static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *current; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + status = NOT_FOUND; + + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current == entry) + { + /* mark it, so now new threads can get this entry */ + entry->driveout_new_threads = TRUE; + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_broadcast(&(entry->condvar)); + /* they will wake us again when their work is done */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + + DBG2(DBG_MGR, "found entry by pointer, deleting it"); + iterator->remove(iterator); + entry_destroy(entry); + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Wait until no other thread is using an IKE_SA, return FALSE if entry not + * acquireable + */ +static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry) +{ + if (entry->driveout_new_threads) + { + /* we are not allowed to get this */ + return FALSE; + } + while (entry->checked_out && !entry->driveout_waiting_threads) + { + /* so wait until we can get it for us. + * we register us as waiting. */ + entry->waiting_threads++; + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + entry->waiting_threads--; + } + /* hm, a deletion request forbids us to get this SA, get next one */ + if (entry->driveout_waiting_threads) + { + /* we must signal here, others may be waiting on it, too */ + pthread_cond_signal(&(entry->condvar)); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of private_ike_sa_manager_t.get_next_spi. + */ +static u_int64_t get_next_spi(private_ike_sa_manager_t *this) +{ + u_int64_t spi; + + this->randomizer->get_pseudo_random_bytes(this->randomizer, sizeof(spi), + (u_int8_t*)&spi); + return spi; +} + +/** + * Implementation of of ike_sa_manager.checkout. + */ +static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + ike_sa_t *ike_sa = NULL; + entry_t *entry; + + DBG2(DBG_MGR, "checkout IKE_SA: %J, %d IKE_SAs in manager", + ike_sa_id, this->ike_sa_list->get_count(this->ike_sa_list)); + + pthread_mutex_lock(&(this->mutex)); + if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + if (wait_for_entry(this, entry)) + { + DBG2(DBG_MGR, "IKE_SA successfully checked out"); + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + } + } + pthread_mutex_unlock(&this->mutex); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_new. + */ +static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) +{ + entry_t *entry; + ike_sa_id_t *id; + + if (initiator) + { + id = ike_sa_id_create(get_next_spi(this), 0, TRUE); + } + else + { + id = ike_sa_id_create(0, get_next_spi(this), FALSE); + } + entry = entry_create(id); + pthread_mutex_lock(&this->mutex); + this->ike_sa_list->insert_last(this->ike_sa_list, entry); + entry->checked_out = TRUE; + pthread_mutex_unlock(&this->mutex); + DBG2(DBG_MGR, "created IKE_SA: %J, %d IKE_SAs in manager", + id, this->ike_sa_list->get_count(this->ike_sa_list)); + return entry->ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_id. + */ +static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, + message_t *message) +{ + entry_t *entry; + ike_sa_t *ike_sa = NULL; + ike_sa_id_t *id = message->get_ike_sa_id(message); + id = id->clone(id); + id->switch_initiator(id); + + DBG2(DBG_MGR, "checkout IKE_SA: %J by message, %d IKE_SAs in manager", + id, this->ike_sa_list->get_count(this->ike_sa_list)); + + if (message->get_request(message) && + message->get_exchange_type(message) == IKE_SA_INIT) + { + /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */ + iterator_t *iterator; + chunk_t data, hash; + + data = message->get_packet_data(message); + this->hasher->allocate_hash(this->hasher, data, &hash); + chunk_free(&data); + + pthread_mutex_lock(&this->mutex); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (chunk_equals(hash, entry->init_hash)) + { + if (entry->message_id == 0) + { + iterator->destroy(iterator); + pthread_mutex_unlock(&this->mutex); + chunk_free(&hash); + id->destroy(id); + DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing"); + return NULL; + } + else if (wait_for_entry(this, entry)) + { + DBG2(DBG_MGR, "IKE_SA checked out by hash"); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + ike_sa = entry->ike_sa; + } + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&this->mutex); + + if (ike_sa == NULL) + { + if (id->get_responder_spi(id) == 0 && + message->get_exchange_type(message) == IKE_SA_INIT) + { + /* no IKE_SA found, create a new one */ + id->set_responder_spi(id, get_next_spi(this)); + entry = entry_create(id); + + pthread_mutex_lock(&this->mutex); + this->ike_sa_list->insert_last(this->ike_sa_list, entry); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + pthread_mutex_unlock(&this->mutex); + entry->init_hash = hash; + ike_sa = entry->ike_sa; + } + else + { + DBG1(DBG_MGR, "ignoring message for %J, no such IKE_SA", id); + } + } + else + { + chunk_free(&hash); + } + id->destroy(id); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; + } + + pthread_mutex_lock(&(this->mutex)); + if (get_entry_by_id(this, id, &entry) == SUCCESS) + { + /* only check out if we are not processing this request */ + if (message->get_request(message) && + message->get_message_id(message) == entry->message_id) + { + DBG1(DBG_MGR, "ignoring request with ID %d, already processing", + entry->message_id); + } + else if (wait_for_entry(this, entry)) + { + ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa); + DBG2(DBG_MGR, "IKE_SA successfully checked out"); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + if (ike_id->get_responder_spi(ike_id) == 0) + { + ike_id->set_responder_spi(ike_id, id->get_responder_spi(id)); + } + ike_sa = entry->ike_sa; + } + } + pthread_mutex_unlock(&this->mutex); + id->destroy(id); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_id. + */ +static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this, + host_t *my_host, host_t *other_host, + identification_t *my_id, + identification_t *other_id) +{ + iterator_t *iterator; + entry_t *entry; + ike_sa_t *ike_sa = NULL; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + identification_t *found_my_id, *found_other_id; + host_t *found_my_host, *found_other_host; + int wc; + + if (!wait_for_entry(this, entry)) + { + continue; + } + + if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) + { + /* skip IKE_SA which are not useable */ + continue; + } + + found_my_id = entry->ike_sa->get_my_id(entry->ike_sa); + found_other_id = entry->ike_sa->get_other_id(entry->ike_sa); + found_my_host = entry->ike_sa->get_my_host(entry->ike_sa); + found_other_host = entry->ike_sa->get_other_host(entry->ike_sa); + + if (found_my_id->get_type(found_my_id) == ID_ANY && + found_other_id->get_type(found_other_id) == ID_ANY) + { + /* IKE_SA has no IDs yet, so we can't use it */ + continue; + } + + /* compare ID and hosts. Supplied ID may contain wildcards, and IP + * may be %any. */ + if ((found_my_host->is_anyaddr(found_my_host) || + my_host->ip_equals(my_host, found_my_host)) && + (found_other_host->is_anyaddr(found_other_host) || + other_host->ip_equals(other_host, found_other_host)) && + found_my_id->matches(found_my_id, my_id, &wc) && + found_other_id->matches(found_other_id, other_id, &wc)) + { + /* looks good, we take this one */ + DBG2(DBG_MGR, "found an existing IKE_SA for %H[%D]...%H[%D]", + my_host, other_host, my_id, other_id); + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + } + } + iterator->destroy(iterator); + + if (!ike_sa) + { + u_int64_t initiator_spi; + entry_t *new_entry; + ike_sa_id_t *new_ike_sa_id; + + initiator_spi = get_next_spi(this); + new_ike_sa_id = ike_sa_id_create(0, 0, TRUE); + new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi); + + /* create entry */ + new_entry = entry_create(new_ike_sa_id); + DBG2(DBG_MGR, "created IKE_SA: %J", new_ike_sa_id); + new_ike_sa_id->destroy(new_ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); + + /* check ike_sa out */ + DBG2(DBG_MGR, "new IKE_SA created for IDs [%D]...[%D]", my_id, other_id); + new_entry->checked_out = TRUE; + ike_sa = new_entry->ike_sa; + } + pthread_mutex_unlock(&(this->mutex)); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_id. + */ +static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, + bool child) +{ + iterator_t *iterator, *children; + entry_t *entry; + ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (wait_for_entry(this, entry)) + { + /* look for a child with such a reqid ... */ + if (child) + { + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (child_sa->get_reqid(child_sa) == id) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); + } + else /* ... or for a IKE_SA with such a unique id */ + { + if (entry->ike_sa->get_unique_id(entry->ike_sa) == id) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) + { + entry->checked_out = TRUE; + break; + } + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_name. + */ +static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, + bool child) +{ + iterator_t *iterator, *children; + entry_t *entry; + ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + if (wait_for_entry(this, entry)) + { + /* look for a child with such a policy name ... */ + if (child) + { + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (streq(child_sa->get_name(child_sa), name)) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); + } + else /* ... or for a IKE_SA with such a connection name */ + { + if (streq(entry->ike_sa->get_name(entry->ike_sa), name)) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) + { + entry->checked_out = TRUE; + break; + } + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** + * Iterator hook for iterate, gets ike_sas instead of entries + */ +static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in, + ike_sa_t **out) +{ + /* check out entry */ + if (wait_for_entry(this, in)) + { + *out = in->ike_sa; + return TRUE; + } + return FALSE; +} + +/** + * Implementation of ike_sa_manager_t.create_iterator. + */ +static iterator_t *create_iterator(private_ike_sa_manager_t* this) +{ + iterator_t *iterator = this->ike_sa_list->create_iterator_locked( + this->ike_sa_list, &this->mutex); + /* register hook to iterator over ike_sas, not entries */ + iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this); + return iterator; +} + +/** + * Implementation of ike_sa_manager_t.checkin. + */ +static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* to check the SA back in, we look for the pointer of the ike_sa + * in all entries. + * We can't search by SPI's since the MAY have changed (e.g. on reception + * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary... + */ + status_t retval; + entry_t *entry; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = ike_sa->get_id(ike_sa); + + DBG2(DBG_MGR, "checkin IKE_SA: %J", ike_sa_id); + + pthread_mutex_lock(&(this->mutex)); + + /* look for the entry */ + if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* ike_sa_id must be updated */ + entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); + /* signal waiting threads */ + entry->checked_out = FALSE; + entry->message_id = -1; + DBG2(DBG_MGR, "check-in of IKE_SA successful."); + pthread_cond_signal(&(entry->condvar)); + retval = SUCCESS; + } + else + { + DBG2(DBG_MGR, "tried to check in nonexisting IKE_SA"); + /* this SA is no more, this REALLY should not happen */ + retval = NOT_FOUND; + } + + DBG2(DBG_MGR, "%d IKE_SAs in manager now", + this->ike_sa_list->get_count(this->ike_sa_list)); + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, NULL); + return retval; +} + + +/** + * Implementation of ike_sa_manager_t.checkin_and_destroy. + */ +static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* deletion is a bit complex, we must garant that no thread is waiting for + * this SA. + * We take this SA from the list, and start signaling while threads + * are in the condvar. + */ + entry_t *entry; + status_t retval; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = ike_sa->get_id(ike_sa); + DBG2(DBG_MGR, "checkin and destroy IKE_SA: %J", ike_sa_id); + + pthread_mutex_lock(&(this->mutex)); + + if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* drive out waiting threads, as we are in hurry */ + entry->driveout_waiting_threads = TRUE; + + delete_entry(this, entry); + + DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful"); + retval = SUCCESS; + } + else + { + DBG2(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA"); + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + charon->bus->set_sa(charon->bus, ike_sa); + return retval; +} + +/** + * Implementation of ike_sa_manager_t.get_half_open_count. + */ +static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) +{ + iterator_t *iterator; + entry_t *entry; + int count = 0; + + pthread_mutex_lock(&(this->mutex)); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + /* we check if we have a responder CONNECTING IKE_SA without checkout */ + if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING) + { + /* if we have a host, we have wait until no other uses the IKE_SA */ + if (ip) + { + if (wait_for_entry(this, entry) && ip->ip_equals(ip, + entry->ike_sa->get_other_host(entry->ike_sa))) + { + count++; + } + } + else + { + count++; + } + } + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); + return count; +} + +/** + * Implementation of ike_sa_manager_t.destroy. + */ +static void destroy(private_ike_sa_manager_t *this) +{ + /* destroy all list entries */ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + entry_t *entry; + + pthread_mutex_lock(&(this->mutex)); + DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's"); + /* Step 1: drive out all waiting threads */ + DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's"); + iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) + { + /* do not accept new threads, drive out waiting threads */ + entry->driveout_new_threads = TRUE; + entry->driveout_waiting_threads = TRUE; + } + DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's"); + /* Step 2: wait until all are gone */ + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&entry)) + { + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_broadcast(&(entry->condvar)); + /* go sleeping until they are gone */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + } + DBG2(DBG_MGR, "delete all IKE_SA's"); + /* Step 3: initiate deletion of all IKE_SAs */ + iterator->reset(iterator); + while (iterator->iterate(iterator, (void**)&entry)) + { + entry->ike_sa->delete(entry->ike_sa); + } + iterator->destroy(iterator); + + DBG2(DBG_MGR, "destroy all entries"); + /* Step 4: destroy all entries */ + list->destroy_function(list, (void*)entry_destroy); + pthread_mutex_unlock(&(this->mutex)); + + this->randomizer->destroy(this->randomizer); + this->hasher->destroy(this->hasher); + + free(this); +} + +/* + * Described in header. + */ +ike_sa_manager_t *ike_sa_manager_create() +{ + private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t); + + /* assign public functions */ + this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; + this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; + this->public.checkout_new = (ike_sa_t*(*)(ike_sa_manager_t*,bool))checkout_new; + this->public.checkout_by_message = (ike_sa_t*(*)(ike_sa_manager_t*,message_t*))checkout_by_message; + this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer; + this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; + this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; + this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator; + this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; + this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; + this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count; + + /* initialize private variables */ + this->ike_sa_list = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + this->randomizer = randomizer_create(); + this->hasher = hasher_create(HASH_SHA1); + + return &this->public; +} diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h new file mode 100644 index 000000000..1125e5d16 --- /dev/null +++ b/src/charon/sa/ike_sa_manager.h @@ -0,0 +1,231 @@ +/** + * @file ike_sa_manager.h + * + * @brief Interface of ike_sa_manager_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_SA_MANAGER_H_ +#define IKE_SA_MANAGER_H_ + +typedef struct ike_sa_manager_t ike_sa_manager_t; + +#include +#include +#include + +/** + * @brief The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's. + * + * To avoid access from multiple threads, IKE_SAs must be checked out from + * the manager, and checked in after usage. + * The manager also handles deletion of SAs. + * + * @todo checking of double-checkouts from the same threads would be nice. + * This could be done by comparing thread-ids via pthread_self()... + * + * @todo Managing of ike_sa_t objects in a hash table instead of linked list. + * + * @b Constructors: + * - ike_sa_manager_create() + * + * @ingroup sa + */ +struct ike_sa_manager_t { + + /** + * @brief Checkout an existing IKE_SA. + * + * @param this the manager object + * @param ike_sa_id the SA identifier, will be updated + * @returns + * - checked out IKE_SA if found + * - NULL, if specified IKE_SA is not found. + */ + ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id); + + /** + * @brief Create and check out a new IKE_SA. + * + * @param this the manager object + * @param initiator TRUE for initiator, FALSE otherwise + * @returns created andchecked out IKE_SA + */ + ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, bool initiator); + + /** + * @brief Checkout an IKE_SA by a message. + * + * In some situations, it is necessary that the manager knows the + * message to use for the checkout. This has the folloing reasons: + * + * 1. If the targeted IKE_SA is already processing a message, we do not + * check it out if the message ID is the same. + * 2. If it is an IKE_SA_INIT request, we have to check if it is a + * retransmission. If so, we have to drop the message, we would + * create another unneded IKE_SA for each retransmitted packet. + * + * A call to checkout_by_message() returns a (maybe new created) IKE_SA. + * If processing the message does not make sense (for the reasons above), + * NULL is returned. + * + * @param this the manager object + * @param ike_sa_id the SA identifier, will be updated + * @returns + * - checked out/created IKE_SA + * - NULL to not process message further + */ + ike_sa_t* (*checkout_by_message) (ike_sa_manager_t* this, message_t *message); + + /** + * @brief Checkout an existing IKE_SA by hosts and identifications. + * + * Allows the lookup of an IKE_SA by user IDs and hosts. It returns the + * first found occurence, if there are multiple candidates. Supplied IDs + * may contain wildcards, hosts may be %any. + * If no IKE_SA is found, a new one is created. This is also the case when + * the found IKE_SA is in the DELETING state. + * + * @param this the manager object + * @param my_host address of our host + * @param other_id address of remote host + * @param my_id ID used by us + * @param other_id ID used by remote + * @return checked out/created IKE_SA + */ + ike_sa_t* (*checkout_by_peer) (ike_sa_manager_t* this, + host_t *my_host, host_t* other_host, + identification_t *my_id, + identification_t *other_id); + + /** + * @brief Check out an IKE_SA a unique ID. + * + * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. + * These checkout function uses, depending + * on the child parameter, the unique ID of the IKE_SA or the reqid + * of one of a IKE_SAs CHILD_SA. + * + * @param this the manager object + * @param id unique ID of the object + * @param child TRUE to use CHILD, FALSE to use IKE_SA + * @return + * - checked out IKE_SA, if found + * - NULL, if not found + */ + ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id, + bool child); + + /** + * @brief Check out an IKE_SA by the policy/connection name. + * + * Check out the IKE_SA by the connections name or by a CHILD_SAs policy + * name. + * + * @param this the manager object + * @param name name of the connection/policy + * @param child TRUE to use policy name, FALSE to use conn name + * @return + * - checked out IKE_SA, if found + * - NULL, if not found + */ + ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name, + bool child); + + /** + * @brief Create an iterator over all stored IKE_SAs. + * + * The avoid synchronization issues, the iterator locks access + * to the manager exclusively, until it gets destroyed. + * This iterator is for reading only! Writing will corrupt the manager. + * + * @param this the manager object + * @return iterator over all IKE_SAs. + */ + iterator_t *(*create_iterator) (ike_sa_manager_t* this); + + /** + * @brief Checkin the SA after usage. + * + * @warning the SA pointer MUST NOT be used after checkin! + * The SA must be checked out again! + * + * @param this the manager object + * @param ike_sa_id the SA identifier, will be updated + * @param ike_sa checked out SA + * @returns + * - SUCCESS if checked in + * - NOT_FOUND when not found (shouldn't happen!) + */ + status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @brief Destroy a checked out SA. + * + * The IKE SA is destroyed without notification of the remote peer. + * Use this only if the other peer doesn't respond or behaves not + * as predicted. + * Checking in and destruction is an atomic operation (for the IKE_SA), + * so this can be called if the SA is in a "unclean" state, without the + * risk that another thread can get the SA. + * + * @param this the manager object + * @param ike_sa SA to delete + * @returns + * - SUCCESS if found + * - NOT_FOUND when no such SA is available + */ + status_t (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @brief Get the number of IKE_SAs which are in the connecting state. + * + * To prevent the server from resource exhaustion, cookies and other + * mechanisms are used. The number of half open IKE_SAs is a good + * indicator to see if a peer is flooding the server. + * If a host is supplied, only the number of half open IKE_SAs initiated + * from this IP are counted. + * Only SAs for which we are the responder are counted. + * + * @param this the manager object + * @param ip NULL for all, IP for half open IKE_SAs with IP + * @return number of half open IKE_SAs + */ + int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip); + + /** + * @brief Destroys the manager with all associated SAs. + * + * Threads will be driven out, so all SAs can be deleted cleanly. + * + * @param this the manager object + */ + void (*destroy) (ike_sa_manager_t *this); +}; + +/** + * @brief Create a manager. + * + * @returns ike_sa_manager_t object + * + * @ingroup sa + */ +ike_sa_manager_t *ike_sa_manager_create(void); + +#endif /*IKE_SA_MANAGER_H_*/ diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c new file mode 100644 index 000000000..844300735 --- /dev/null +++ b/src/charon/sa/task_manager.c @@ -0,0 +1,854 @@ +/** + * @file task_manager.c + * + * @brief Implementation of task_manager_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "task_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct exchange_t exchange_t; + +/** + * An exchange in the air, used do detect and handle retransmission + */ +struct exchange_t { + + /** + * Message ID used for this transaction + */ + u_int32_t mid; + + /** + * generated packet for retransmission + */ + packet_t *packet; +}; + +typedef struct private_task_manager_t private_task_manager_t; + +/** + * private data of the task manager + */ +struct private_task_manager_t { + + /** + * public functions + */ + task_manager_t public; + + /** + * associated IKE_SA we are serving + */ + ike_sa_t *ike_sa; + + /** + * Exchange we are currently handling as responder + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * packet for retransmission + */ + packet_t *packet; + + } responding; + + /** + * Exchange we are currently handling as initiator + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * how many times we have retransmitted so far + */ + u_int retransmitted; + + /** + * packet for retransmission + */ + packet_t *packet; + + /** + * type of the initated exchange + */ + exchange_type_t type; + + } initiating; + + /** + * List of queued tasks not yet in action + */ + linked_list_t *queued_tasks; + + /** + * List of active tasks, initiated by ourselve + */ + linked_list_t *active_tasks; + + /** + * List of tasks initiated by peer + */ + linked_list_t *passive_tasks; +}; + +/** + * flush all tasks in the task manager + */ +static void flush(private_task_manager_t *this) +{ + task_t *task; + + this->queued_tasks->destroy_offset(this->queued_tasks, + offsetof(task_t, destroy)); + this->passive_tasks->destroy_offset(this->passive_tasks, + offsetof(task_t, destroy)); + + /* emmit outstanding signals for tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + switch (task->get_type(task)) + { + case IKE_AUTH: + SIG(IKE_UP_FAILED, "establishing IKE_SA failed"); + break; + case IKE_DELETE: + SIG(IKE_DOWN_FAILED, "IKE_SA deleted"); + break; + case IKE_REKEY: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed"); + break; + case CHILD_CREATE: + SIG(CHILD_UP_FAILED, "establishing CHILD_SA failed"); + break; + case CHILD_DELETE: + SIG(CHILD_DOWN_FAILED, "deleting CHILD_SA failed"); + break; + case CHILD_REKEY: + SIG(IKE_REKEY_FAILED, "rekeying CHILD_SA failed"); + break; + default: + break; + } + task->destroy(task); + } + this->queued_tasks = linked_list_create(); + this->passive_tasks = linked_list_create(); +} + +/** + * move a task of a specific type from the queue to the active list + */ +static bool activate_task(private_task_manager_t *this, task_type_t type) +{ + iterator_t *iterator; + task_t *task; + bool found = FALSE; + + iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&task)) + { + if (task->get_type(task) == type) + { + DBG2(DBG_IKE, " activating %N task", task_type_names, type); + iterator->remove(iterator); + this->active_tasks->insert_last(this->active_tasks, task); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of task_manager_t.retransmit + */ +static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) +{ + if (message_id == this->initiating.mid) + { + u_int32_t timeout; + job_t *job; + + timeout = charon->configuration->get_retransmit_timeout( + charon->configuration, this->initiating.retransmitted); + if (timeout == 0) + { + DBG1(DBG_IKE, "giving up after %d retransmits", + this->initiating.retransmitted - 1); + return DESTROY_ME; + } + + if (this->initiating.retransmitted) + { + DBG1(DBG_IKE, "retransmit %d of request with message ID %d", + this->initiating.retransmitted, message_id); + } + this->initiating.retransmitted++; + + charon->sender->send(charon->sender, + this->initiating.packet->clone(this->initiating.packet)); + job = (job_t*)retransmit_job_create(this->initiating.mid, + this->ike_sa->get_id(this->ike_sa)); + charon->event_queue->add_relative(charon->event_queue, job, timeout); + } + return SUCCESS; +} + +/** + * build a request using the active task list + * Implementation of task_manager_t.initiate + */ +static status_t build_request(private_task_manager_t *this) +{ + iterator_t *iterator; + task_t *task; + message_t *message; + status_t status; + exchange_type_t exchange = 0; + + if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) + { + DBG2(DBG_IKE, "delaying task initiation, exchange in progress"); + /* do not initiate if we already have a message in the air */ + return SUCCESS; + } + + if (this->active_tasks->get_count(this->active_tasks) == 0) + { + DBG2(DBG_IKE, "activating new tasks"); + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_CREATED: + if (activate_task(this, IKE_INIT)) + { + exchange = IKE_SA_INIT; + activate_task(this, IKE_NATD); + activate_task(this, IKE_CERT); + activate_task(this, IKE_AUTHENTICATE); + activate_task(this, IKE_CONFIG); + activate_task(this, CHILD_CREATE); + } + break; + case IKE_ESTABLISHED: + if (activate_task(this, CHILD_CREATE)) + { + exchange = CREATE_CHILD_SA; + activate_task(this, IKE_CONFIG); + break; + } + if (activate_task(this, CHILD_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, CHILD_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, IKE_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, IKE_DEADPEER)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_REKEYING: + if (activate_task(this, IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_DELETING: + default: + break; + } + } + else + { + DBG2(DBG_IKE, "reinitiating already active tasks"); + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&task)) + { + DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); + switch (task->get_type(task)) + { + case IKE_INIT: + exchange = IKE_SA_INIT; + break; + case IKE_AUTHENTICATE: + exchange = IKE_AUTH; + break; + default: + continue; + } + break; + } + iterator->destroy(iterator); + } + + if (exchange == 0) + { + DBG2(DBG_IKE, "nothing to initiate"); + /* nothing to do yet... */ + return SUCCESS; + } + + message = message_create(); + message->set_message_id(message, this->initiating.mid); + message->set_exchange_type(message, exchange); + this->initiating.type = exchange; + this->initiating.retransmitted = 0; + + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + message->destroy(message); + flush(this); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + DESTROY_IF(this->initiating.packet); + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->initiating.packet); + message->destroy(message); + if (status != SUCCESS) + { + /* message generation failed. There is nothing more to do than to + * close the SA */ + flush(this); + return DESTROY_ME; + } + + return retransmit(this, this->initiating.mid); +} + +/** + * handle an incoming response message + */ +static status_t process_response(private_task_manager_t *this, + message_t *message) +{ + iterator_t *iterator; + task_t *task; + + if (message->get_exchange_type(message) != this->initiating.type) + { + DBG1(DBG_IKE, "received %N response, but expected %N", + exchange_type_names, message->get_exchange_type(message), + exchange_type_names, this->initiating.type); + return DESTROY_ME; + } + + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + this->initiating.mid++; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + + return build_request(this); +} + +/** + * handle exchange collisions + */ +static void handle_collisions(private_task_manager_t *this, task_t *task) +{ + iterator_t *iterator; + task_t *active; + task_type_t type; + + type = task->get_type(task); + + /* do we have to check */ + if (type == IKE_REKEY || type == CHILD_REKEY || + type == CHILD_DELETE || type == IKE_DELETE) + { + /* find an exchange collision, and notify these tasks */ + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&active)) + { + switch (active->get_type(active)) + { + case IKE_REKEY: + if (type == IKE_REKEY || type == IKE_DELETE) + { + ike_rekey_t *rekey = (ike_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + case CHILD_REKEY: + if (type == CHILD_REKEY || type == CHILD_DELETE) + { + child_rekey_t *rekey = (child_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + default: + continue; + } + iterator->destroy(iterator); + return; + } + iterator->destroy(iterator); + } + /* destroy task if not registered in any active task */ + task->destroy(task); +} + +/** + * build a response depending on the "passive" task list + */ +static status_t build_response(private_task_manager_t *this, + exchange_type_t exchange) +{ + iterator_t *iterator; + task_t *task; + message_t *message; + bool delete = FALSE; + status_t status; + + message = message_create(); + message->set_exchange_type(message, exchange); + message->set_message_id(message, this->responding.mid); + message->set_request(message, FALSE); + + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + handle_collisions(this, task); + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* destroy IKE_SA, but SEND response first */ + delete = TRUE; + break; + } + if (delete) + { + break; + } + } + iterator->destroy(iterator); + + /* remove resonder SPI if IKE_SA_INIT failed */ + if (delete && exchange == IKE_SA_INIT) + { + ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa); + id->set_responder_spi(id, 0); + } + + /* message complete, send it */ + DESTROY_IF(this->responding.packet); + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->responding.packet); + message->destroy(message); + if (status != SUCCESS) + { + return DESTROY_ME; + } + + charon->sender->send(charon->sender, + this->responding.packet->clone(this->responding.packet)); + if (delete) + { + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * handle an incoming request message + */ +static status_t process_request(private_task_manager_t *this, + message_t *message) +{ + iterator_t *iterator; + task_t *task = NULL; + exchange_type_t exchange; + payload_t *payload; + notify_payload_t *notify; + + exchange = message->get_exchange_type(message); + + /* create tasks depending on request type */ + switch (exchange) + { + case IKE_SA_INIT: + { + task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_natd_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_cert_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_auth_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_config_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)child_create_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case CREATE_CHILD_SA: + { + bool notify_found = FALSE, ts_found = FALSE; + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case NOTIFY: + { + /* if we find a rekey notify, its CHILD_SA rekeying */ + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == REKEY_SA && + (notify->get_protocol_id(notify) == PROTO_AH || + notify->get_protocol_id(notify) == PROTO_ESP)) + { + notify_found = TRUE; + } + break; + } + case TRAFFIC_SELECTOR_INITIATOR: + case TRAFFIC_SELECTOR_RESPONDER: + { + /* if we don't find a TS, its IKE rekeying */ + ts_found = TRUE; + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + if (ts_found) + { + if (notify_found) + { + task = (task_t*)child_rekey_create(this->ike_sa, NULL); + } + else + { + task = (task_t*)child_create_create(this->ike_sa, NULL); + } + } + else + { + task = (task_t*)ike_rekey_create(this->ike_sa, FALSE); + } + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case INFORMATIONAL: + { + delete_payload_t *delete; + + delete = (delete_payload_t*)message->get_payload(message, DELETE); + if (delete) + { + if (delete->get_protocol_id(delete) == PROTO_IKE) + { + task = (task_t*)ike_delete_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + else + { + task = (task_t*)child_delete_create(this->ike_sa, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + } + else + { + task = (task_t*)ike_dpd_create(FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + break; + } + default: + break; + } + + /* let the tasks process the message */ + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs at least another call to build() */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); + return DESTROY_ME; + } + } + iterator->destroy(iterator); + + return build_response(this, exchange); +} + +/** + * Implementation of task_manager_t.process_message + */ +static status_t process_message(private_task_manager_t *this, message_t *msg) +{ + u_int32_t mid = msg->get_message_id(msg); + + if (msg->get_request(msg)) + { + if (mid == this->responding.mid) + { + if (process_request(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } + this->responding.mid++; + } + else if ((mid == this->responding.mid - 1) && this->responding.packet) + { + DBG1(DBG_IKE, "received retransmit of request with ID %d, " + "retransmitting response", mid); + charon->sender->send(charon->sender, + this->responding.packet->clone(this->responding.packet)); + } + else + { + DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored", + mid, this->responding.mid); + } + } + else + { + if (mid == this->initiating.mid) + { + if (process_response(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } + } + else + { + DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored", + mid, this->initiating.mid); + return SUCCESS; + } + } + return SUCCESS; +} + +/** + * Implementation of task_manager_t.queue_task + */ +static void queue_task(private_task_manager_t *this, task_t *task) +{ + DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); + this->queued_tasks->insert_last(this->queued_tasks, task); +} + +/** + * Implementation of task_manager_t.adopt_tasks + */ +static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other) +{ + task_t *task; + + /* move queued tasks from other to this */ + while (other->queued_tasks->remove_last(other->queued_tasks, + (void**)&task) == SUCCESS) + { + DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } + + /* reset active tasks and move them to others queued tasks */ + while (other->active_tasks->remove_last(other->active_tasks, + (void**)&task) == SUCCESS) + { + DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } +} + +/** + * Implementation of task_manager_t.busy + */ +static bool busy(private_task_manager_t *this) +{ + return (this->active_tasks->get_count(this->active_tasks) > 0); +} + +/** + * Implementation of task_manager_t.reset + */ +static void reset(private_task_manager_t *this) +{ + task_t *task; + + /* reset message counters and retransmit packets */ + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + this->responding.packet = NULL; + this->initiating.packet = NULL; + this->responding.mid = 0; + this->initiating.mid = -1; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + + /* reset active tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } +} + +/** + * Implementation of task_manager_t.destroy + */ +static void destroy(private_task_manager_t *this) +{ + flush(this); + + this->active_tasks->destroy(this->active_tasks); + this->queued_tasks->destroy(this->queued_tasks); + this->passive_tasks->destroy(this->passive_tasks); + + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + free(this); +} + +/* + * see header file + */ +task_manager_t *task_manager_create(ike_sa_t *ike_sa) +{ + private_task_manager_t *this = malloc_thing(private_task_manager_t); + + this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message; + this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task; + this->public.initiate = (status_t(*)(task_manager_t*))build_request; + this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit; + this->public.reset = (void(*)(task_manager_t*))reset; + this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks; + this->public.busy = (bool(*)(task_manager_t*))busy; + this->public.destroy = (void(*)(task_manager_t*))destroy; + + this->ike_sa = ike_sa; + this->responding.packet = NULL; + this->initiating.packet = NULL; + this->responding.mid = 0; + this->initiating.mid = 0; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + this->queued_tasks = linked_list_create(); + this->active_tasks = linked_list_create(); + this->passive_tasks = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h new file mode 100644 index 000000000..c766d4a65 --- /dev/null +++ b/src/charon/sa/task_manager.h @@ -0,0 +1,144 @@ +/** + * @file task_manager.h + * + * @brief Interface of task_manager_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef TASK_MANAGER_H_ +#define TASK_MANAGER_H_ + +typedef struct task_manager_t task_manager_t; + +#include +#include +#include +#include + +/** + * @brief The task manager, juggles task and handles message exchanges. + * + * On incoming requests, the task manager creates new tasks on demand and + * juggles the request through all available tasks. Each task inspects the + * request and adds payloads as necessary to the response. + * On outgoing requests, the task manager delivers the request through the tasks + * to build it, the response gets processed by each task to complete. + * The task manager has an internal Queue to store task which should get + * completed. + * For the initial IKE_SA setup, several tasks are queued: One for the + * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup + * and maybe one for virtual IP assignement. + * + * @b Constructors: + * - task_manager_create() + * + * @ingroup sa + */ +struct task_manager_t { + + /** + * @brief Process an incoming message. + * + * @param this calling object + * @param message message to add payloads to + * @return + * - DESTROY_ME if IKE_SA must be closed + * - SUCCESS otherwise + */ + status_t (*process_message) (task_manager_t *this, message_t *message); + + /** + * @brief Initiate an exchange with the currently queued tasks. + * + * @param this calling object + */ + status_t (*initiate) (task_manager_t *this); + + /** + * @brief Queue a task in the manager. + * + * @param this calling object + * @param task task to queue + */ + void (*queue_task) (task_manager_t *this, task_t *task); + + /** + * @brief Retransmit a request if it hasn't been acknowledged yet. + * + * A return value of INVALID_STATE means that the message was already + * acknowledged and has not to be retransmitted. A return value of SUCCESS + * means retransmission was required and the message has been resent. + * + * @param this calling object + * @param message_id ID of the message to retransmit + * @return + * - INVALID_STATE if retransmission not required + * - SUCCESS if retransmission sent + */ + status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); + + /** + * @brief Migrate all tasks from other to this. + * + * To rekey or reestablish an IKE_SA completely, all queued or active + * tasks should get migrated to the new IKE_SA. + * + * @param this manager which gets all tasks + * @param other manager which gives away its tasks + */ + void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); + + /** + * @brief Reset message ID counters of the task manager. + * + * The IKEv2 protocol requires to restart exchanges with message IDs + * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method + * resets the message IDs and resets all active tasks using the migrate() + * method. + * + * @param this calling object + * @param other manager which gives away its tasks + */ + void (*reset) (task_manager_t *this); + + /** + * @brief Check if we are currently waiting for a reply. + * + * @param this calling object + * @return TRUE if we are waiting, FALSE otherwise + */ + bool (*busy) (task_manager_t *this); + + /** + * @brief Destroy the task_manager_t. + * + * @param this calling object + */ + void (*destroy) (task_manager_t *this); +}; + +/** + * @brief Create an instance of the task manager. + * + * @param ike_sa IKE_SA to manage. + * + * @ingroup sa + */ +task_manager_t *task_manager_create(ike_sa_t *ike_sa); + +#endif /* TASK_MANAGER_H_ */ diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c new file mode 100644 index 000000000..781d679f2 --- /dev/null +++ b/src/charon/sa/tasks/child_create.c @@ -0,0 +1,804 @@ +/** + * @file child_create.c + * + * @brief Implementation of the child_create task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "child_create.h" + +#include +#include +#include +#include +#include +#include + + +typedef struct private_child_create_t private_child_create_t; + +/** + * Private members of a child_create_t task. + */ +struct private_child_create_t { + + /** + * Public methods and task_t interface. + */ + child_create_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * nonce chosen by us + */ + chunk_t my_nonce; + + /** + * nonce chosen by peer + */ + chunk_t other_nonce; + + /** + * policy to create the CHILD_SA from + */ + policy_t *policy; + + /** + * list of proposal candidates + */ + linked_list_t *proposals; + + /** + * selected proposal to use for CHILD_SA + */ + proposal_t *proposal; + + /** + * traffic selectors for initiators side + */ + linked_list_t *tsi; + + /** + * traffic selectors for responders side + */ + linked_list_t *tsr; + + /** + * mode the new CHILD_SA uses (transport/tunnel/beet) + */ + mode_t mode; + + /** + * reqid to use if we are rekeying + */ + u_int32_t reqid; + + /** + * CHILD_SA which gets established + */ + child_sa_t *child_sa; + + /** + * successfully established the CHILD? + */ + bool established; +}; + +/** + * get the nonce from a message + */ +static status_t get_nonce(message_t *message, chunk_t *nonce) +{ + nonce_payload_t *payload; + + payload = (nonce_payload_t*)message->get_payload(message, NONCE); + if (payload == NULL) + { + return FAILED; + } + *nonce = payload->get_nonce(payload); + return NEED_MORE; +} + +/** + * generate a new nonce to include in a CREATE_CHILD_SA message + */ +static status_t generate_nonce(chunk_t *nonce) +{ + status_t status; + randomizer_t *randomizer = randomizer_create(); + + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + nonce); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "error generating random nonce value"); + return FAILED; + } + return SUCCESS; +} + +/** + * Check a list of traffic selectors if any selector belongs to host + */ +static bool ts_list_is_host(linked_list_t *list, host_t *host) +{ + traffic_selector_t *ts; + bool is_host = TRUE; + iterator_t *iterator = list->create_iterator(list, TRUE); + + while (is_host && iterator->iterate(iterator, (void**)&ts)) + { + is_host = is_host && ts->is_host(ts, host); + } + iterator->destroy(iterator); + return is_host; +} + +/** + * Install a CHILD_SA for usage + */ +static status_t select_and_install(private_child_create_t *this) +{ + prf_plus_t *prf_plus; + status_t status; + chunk_t nonce_i, nonce_r, seed; + linked_list_t *my_ts, *other_ts; + host_t *me, *other, *other_vip, *my_vip; + + if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL) + { + SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message"); + return FAILED; + } + + if (this->initiator) + { + nonce_i = this->my_nonce; + nonce_r = this->other_nonce; + my_ts = this->tsi; + other_ts = this->tsr; + } + else + { + nonce_r = this->my_nonce; + nonce_i = this->other_nonce; + my_ts = this->tsr; + other_ts = this->tsi; + } + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); + other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); + + this->proposal = this->policy->select_proposal(this->policy, this->proposals); + + if (this->proposal == NULL) + { + SIG(CHILD_UP_FAILED, "no acceptable proposal found"); + return FAILED; + } + + if (this->initiator && my_vip) + { /* if we have a virtual IP, shorten our TS to the minimum */ + my_ts = this->policy->select_my_traffic_selectors(this->policy, my_ts, + my_vip); + /* to setup firewall rules correctly, CHILD_SA needs the virtual IP */ + this->child_sa->set_virtual_ip(this->child_sa, my_vip); + } + else + { /* shorten in the host2host case only */ + my_ts = this->policy->select_my_traffic_selectors(this->policy, + my_ts, me); + } + if (other_vip) + { /* if other has a virtual IP, shorten it's traffic selectors to it */ + other_ts = this->policy->select_other_traffic_selectors(this->policy, + other_ts, other_vip); + } + else + { /* use his host for the host2host case */ + other_ts = this->policy->select_other_traffic_selectors(this->policy, + other_ts, other); + } + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + if (this->initiator) + { + this->tsi = my_ts; + this->tsr = other_ts; + } + else + { + this->tsr = my_ts; + this->tsi = other_ts; + } + + if (this->tsi->get_count(this->tsi) == 0 || + this->tsr->get_count(this->tsr) == 0) + { + SIG(CHILD_UP_FAILED, "no acceptable traffic selectors found"); + return FAILED; + } + + if (!this->initiator) + { + /* check if requested mode is acceptable, downgrade if required */ + switch (this->mode) + { + case MODE_TRANSPORT: + if (!ts_list_is_host(this->tsi, other) || + !ts_list_is_host(this->tsr, me)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); + } + else if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, connection NATed"); + } + break; + case MODE_BEET: + if (!ts_list_is_host(this->tsi, NULL) || + !ts_list_is_host(this->tsr, NULL)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); + } + break; + default: + break; + } + } + + seed = chunk_cata("cc", nonce_i, nonce_r); + prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + + if (this->initiator) + { + status = this->child_sa->update(this->child_sa, this->proposal, + this->mode, prf_plus); + } + else + { + status = this->child_sa->add(this->child_sa, this->proposal, + this->mode, prf_plus); + } + prf_plus->destroy(prf_plus); + + if (status != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel"); + return status; + } + + status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts, + this->mode); + + if (status != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel"); + return status; + } + /* add to IKE_SA, and remove from task */ + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + this->established = TRUE; + return SUCCESS; +} + +/** + * build the payloads for the message + */ +static void build_payloads(private_child_create_t *this, message_t *message) +{ + sa_payload_t *sa_payload; + ts_payload_t *ts_payload; + nonce_payload_t *nonce_payload; + + /* add SA payload */ + if (this->initiator) + { + sa_payload = sa_payload_create_from_proposal_list(this->proposals); + } + else + { + sa_payload = sa_payload_create_from_proposal(this->proposal); + } + message->add_payload(message, (payload_t*)sa_payload); + + /* add nonce payload if not in IKE_AUTH */ + if (message->get_exchange_type(message) == CREATE_CHILD_SA) + { + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); + message->add_payload(message, (payload_t*)nonce_payload); + } + + /* add TSi/TSr payloads */ + ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); + message->add_payload(message, (payload_t*)ts_payload); + ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); + message->add_payload(message, (payload_t*)ts_payload); + + /* add a notify if we are not in tunnel mode */ + switch (this->mode) + { + case MODE_TRANSPORT: + message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty); + break; + case MODE_BEET: + message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty); + break; + default: + break; + } +} + +/** + * Read payloads from message + */ +static void process_payloads(private_child_create_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + sa_payload_t *sa_payload; + ts_payload_t *ts_payload; + notify_payload_t *notify_payload; + + /* defaults to TUNNEL mode */ + this->mode = MODE_TUNNEL; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + sa_payload = (sa_payload_t*)payload; + this->proposals = sa_payload->get_proposals(sa_payload); + break; + case TRAFFIC_SELECTOR_INITIATOR: + ts_payload = (ts_payload_t*)payload; + this->tsi = ts_payload->get_traffic_selectors(ts_payload); + break; + case TRAFFIC_SELECTOR_RESPONDER: + ts_payload = (ts_payload_t*)payload; + this->tsr = ts_payload->get_traffic_selectors(ts_payload); + break; + case NOTIFY: + notify_payload = (notify_payload_t*)payload; + switch (notify_payload ->get_notify_type(notify_payload )) + { + case USE_TRANSPORT_MODE: + this->mode = MODE_TRANSPORT; + break; + case USE_BEET_MODE: + this->mode = MODE_BEET; + break; + default: + break; + } + break; + default: + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_create_t *this, message_t *message) +{ + host_t *me, *other, *vip; + + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->my_nonce); + case CREATE_CHILD_SA: + if (generate_nonce(&this->my_nonce) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + break; + case IKE_AUTH: + if (!message->get_payload(message, ID_INITIATOR)) + { + /* send only in the first request, not in subsequent EAP */ + return NEED_MORE; + } + break; + default: + break; + } + + SIG(CHILD_UP_START, "establishing CHILD_SA"); + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + vip = this->policy->get_virtual_ip(this->policy, NULL); + + if (vip) + { /* propose a 0.0.0.0/0 subnet when we use virtual ip */ + this->tsi = this->policy->get_my_traffic_selectors(this->policy, NULL); + vip->destroy(vip); + } + else + { /* but shorten a 0.0.0.0/0 subnet to the actual address if host2host */ + this->tsi = this->policy->get_my_traffic_selectors(this->policy, me); + } + this->tsr = this->policy->get_other_traffic_selectors(this->policy, other); + this->proposals = this->policy->get_proposals(this->policy); + this->mode = this->policy->get_mode(this->policy); + + this->child_sa = child_sa_create(me, other, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->policy, this->reqid, + this->ike_sa->is_natt_enabled(this->ike_sa)); + + if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS) + { + SIG(CHILD_UP_FAILED, "unable to allocate SPIs from kernel"); + return FAILED; + } + + build_payloads(this, message); + + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + this->tsi = NULL; + this->tsr = NULL; + this->proposals = NULL; + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_create_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->other_nonce); + case CREATE_CHILD_SA: + get_nonce(message, &this->other_nonce); + break; + case IKE_AUTH: + if (message->get_payload(message, ID_INITIATOR) == NULL) + { + /* wait until extensible authentication completed, if used */ + return NEED_MORE; + } + default: + break; + } + + process_payloads(this, message); + + if (this->tsi == NULL || this->tsr == NULL) + { + DBG1(DBG_IKE, "TS payload missing in message"); + return NEED_MORE; + } + + this->policy = charon->policies->get_policy(charon->policies, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->tsr, this->tsi, + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); + + if (this->policy && this->ike_sa->get_policy(this->ike_sa) == NULL) + { + this->ike_sa->set_policy(this->ike_sa, this->policy); + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_create_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->my_nonce); + case CREATE_CHILD_SA: + if (generate_nonce(&this->my_nonce) != SUCCESS) + { + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + break; + case IKE_AUTH: + if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + /* wait until extensible authentication completed, if used */ + return NEED_MORE; + } + default: + break; + } + + if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) + { + SIG(CHILD_UP_FAILED, "unable to create CHILD_SA while rekeying IKE_SA"); + message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty); + return SUCCESS; + } + + if (this->policy == NULL) + { + SIG(CHILD_UP_FAILED, "no acceptable policy found"); + message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->policy, this->reqid, + this->ike_sa->is_natt_enabled(this->ike_sa)); + + if (select_and_install(this) != SUCCESS) + { + message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); + return SUCCESS; + } + + build_payloads(this, message); + + SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_create_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + return get_nonce(message, &this->other_nonce); + case CREATE_CHILD_SA: + get_nonce(message, &this->other_nonce); + break; + case IKE_AUTH: + if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + /* wait until extensible authentication completed, if used */ + return NEED_MORE; + } + default: + break; + } + + /* check for erronous notifies */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + /* handle notify errors related to CHILD_SA only */ + case NO_PROPOSAL_CHOSEN: + case SINGLE_PAIR_REQUIRED: + case NO_ADDITIONAL_SAS: + case INTERNAL_ADDRESS_FAILURE: + case FAILED_CP_REQUIRED: + case TS_UNACCEPTABLE: + case INVALID_SELECTORS: + { + SIG(CHILD_UP_FAILED, "received %N notify, no CHILD_SA built", + notify_type_names, type); + iterator->destroy(iterator); + /* an error in CHILD_SA creation is not critical */ + return SUCCESS; + } + default: + break; + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + if (select_and_install(this) == SUCCESS) + { + SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully"); + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_create_t *this) +{ + return CHILD_CREATE; +} + +/** + * Implementation of child_create_t.use_reqid + */ +static void use_reqid(private_child_create_t *this, u_int32_t reqid) +{ + this->reqid = reqid; +} + +/** + * Implementation of child_create_t.get_child + */ +static child_sa_t* get_child(private_child_create_t *this) +{ + return this->child_sa; +} + +/** + * Implementation of child_create_t.get_lower_nonce + */ +static chunk_t get_lower_nonce(private_child_create_t *this) +{ + if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr, + min(this->my_nonce.len, this->other_nonce.len)) < 0) + { + return this->my_nonce; + } + else + { + return this->other_nonce; + } +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + if (this->tsi) + { + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + } + if (this->tsr) + { + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + } + DESTROY_IF(this->child_sa); + DESTROY_IF(this->proposal); + if (this->proposals) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + } + + this->ike_sa = ike_sa; + this->proposals = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->child_sa = NULL; + this->mode = MODE_TUNNEL; + this->reqid = 0; + this->established = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_create_t *this) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + if (this->tsi) + { + this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); + } + if (this->tsr) + { + this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); + } + if (!this->established) + { + DESTROY_IF(this->child_sa); + } + DESTROY_IF(this->proposal); + if (this->proposals) + { + this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); + } + + DESTROY_IF(this->policy); + free(this); +} + +/* + * Described in header. + */ +child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy) +{ + private_child_create_t *this = malloc_thing(private_child_create_t); + + this->public.get_child = (child_sa_t*(*)(child_create_t*))get_child; + this->public.get_lower_nonce = (chunk_t(*)(child_create_t*))get_lower_nonce; + this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (policy) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + policy->get_ref(policy); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + + this->ike_sa = ike_sa; + this->policy = policy; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->proposals = NULL; + this->proposal = NULL; + this->tsi = NULL; + this->tsr = NULL; + this->child_sa = NULL; + this->mode = MODE_TUNNEL; + this->reqid = 0; + this->established = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h new file mode 100644 index 000000000..200d37457 --- /dev/null +++ b/src/charon/sa/tasks/child_create.h @@ -0,0 +1,88 @@ +/** + * @file child_create.h + * + * @brief Interface child_create_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CHILD_CREATE_H_ +#define CHILD_CREATE_H_ + +typedef struct child_create_t child_create_t; + +#include +#include +#include +#include + +/** + * @brief Task of type CHILD_CREATE, established a new CHILD_SA. + * + * This task may be included in the IKE_AUTH message or in a separate + * CREATE_CHILD_SA exchange. + * + * @b Constructors: + * - child_create_create() + * + * @ingroup tasks + */ +struct child_create_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Use a specific reqid for the CHILD_SA. + * + * When this task is used for rekeying, the same reqid is used + * for the new CHILD_SA. + * + * @param this calling object + * @param reqid reqid to use + */ + void (*use_reqid) (child_create_t *this, u_int32_t reqid); + + /** + * @brief Get the lower of the two nonces, used for rekey collisions. + * + * @param this calling object + * @return lower nonce + */ + chunk_t (*get_lower_nonce) (child_create_t *this); + + /** + * @brief Get the CHILD_SA established/establishing by this task. + * + * @param this calling object + * @return child_sa + */ + child_sa_t* (*get_child) (child_create_t *this); +}; + +/** + * @brief Create a new child_create task. + * + * @param ike_sa IKE_SA this task works for + * @param policy policy if task initiator, NULL if responder + * @return child_create task to handle by the task_manager + */ +child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy); + +#endif /* CHILD_CREATE_H_ */ diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c new file mode 100644 index 000000000..23d509de5 --- /dev/null +++ b/src/charon/sa/tasks/child_delete.c @@ -0,0 +1,292 @@ +/** + * @file child_delete.c + * + * @brief Implementation of the child_delete task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "child_delete.h" + +#include +#include + + +typedef struct private_child_delete_t private_child_delete_t; + +/** + * Private members of a child_delete_t task. + */ +struct private_child_delete_t { + + /** + * Public methods and task_t interface. + */ + child_delete_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * CHILD_SAs which get deleted + */ + linked_list_t *child_sas; +}; + +/** + * build the delete payloads from the listed child_sas + */ +static void build_payloads(private_child_delete_t *this, message_t *message) +{ + iterator_t *iterator; + delete_payload_t *ah = NULL, *esp = NULL; + u_int32_t spi; + child_sa_t *child_sa; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + spi = child_sa->get_spi(child_sa, TRUE); + switch (child_sa->get_protocol(child_sa)) + { + case PROTO_ESP: + if (esp == NULL) + { + esp = delete_payload_create(PROTO_ESP); + message->add_payload(message, (payload_t*)esp); + } + esp->add_spi(esp, spi); + break; + case PROTO_AH: + if (ah == NULL) + { + ah = delete_payload_create(PROTO_AH); + message->add_payload(message, (payload_t*)ah); + } + ah->add_spi(ah, spi); + break; + default: + break; + } + child_sa->set_state(child_sa, CHILD_DELETING); + } + iterator->destroy(iterator); +} + +/** + * read in payloads and find the children to delete + */ +static void process_payloads(private_child_delete_t *this, message_t *message) +{ + iterator_t *payloads, *spis; + payload_t *payload; + delete_payload_t *delete_payload; + u_int32_t *spi; + protocol_id_t protocol; + child_sa_t *child_sa; + + payloads = message->get_payload_iterator(message); + while (payloads->iterate(payloads, (void**)&payload)) + { + if (payload->get_type(payload) == DELETE) + { + delete_payload = (delete_payload_t*)payload; + protocol = delete_payload->get_protocol_id(delete_payload); + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + continue; + } + spis = delete_payload->create_spi_iterator(delete_payload); + while (spis->iterate(spis, (void**)&spi)) + { + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + *spi, FALSE); + if (child_sa == NULL) + { + DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, " + "but no such SA", protocol_id_names, protocol, ntohl(*spi)); + continue; + } + DBG2(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x", + protocol_id_names, protocol, ntohl(*spi)); + + switch (child_sa->get_state(child_sa)) + { + case CHILD_REKEYING: + /* we reply as usual, rekeying will fail */ + break; + case CHILD_DELETING: + /* we don't send back a delete if we initiated ourself */ + if (!this->initiator) + { + this->ike_sa->destroy_child_sa(this->ike_sa, + protocol, *spi); + continue; + } + default: + break; + } + + this->child_sas->insert_last(this->child_sas, child_sa); + } + spis->destroy(spis); + } + } + payloads->destroy(payloads); +} + +/** + * destroy the children listed in this->child_sas + */ +static void destroy_children(private_child_delete_t *this) +{ + iterator_t *iterator; + child_sa_t *child_sa; + protocol_id_t protocol; + u_int32_t spi; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + spi = child_sa->get_spi(child_sa, TRUE); + protocol = child_sa->get_protocol(child_sa); + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_delete_t *this, message_t *message) +{ + build_payloads(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_delete_t *this, message_t *message) +{ + /* flush the list before adding new SAs */ + this->child_sas->destroy(this->child_sas); + this->child_sas = linked_list_create(); + + process_payloads(this, message); + destroy_children(this); + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_delete_t *this, message_t *message) +{ + process_payloads(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_delete_t *this, message_t *message) +{ + /* if we are rekeying, we send an empty informational */ + if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING) + { + build_payloads(this, message); + } + destroy_children(this); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_delete_t *this) +{ + return CHILD_DELETE; +} + +/** + * Implementation of child_delete_t.get_child + */ +static child_sa_t* get_child(private_child_delete_t *this) +{ + child_sa_t *child_sa = NULL; + this->child_sas->get_first(this->child_sas, (void**)&child_sa); + return child_sa; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + + this->child_sas->destroy(this->child_sas); + this->child_sas = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_delete_t *this) +{ + this->child_sas->destroy(this->child_sas); + free(this); +} + +/* + * Described in header. + */ +child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + private_child_delete_t *this = malloc_thing(private_child_delete_t); + + this->public.get_child = (child_sa_t*(*)(child_delete_t*))get_child; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + this->ike_sa = ike_sa; + this->child_sas = linked_list_create(); + + if (child_sa != NULL) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + this->child_sas->insert_last(this->child_sas, child_sa); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + return &this->public; +} diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h new file mode 100644 index 000000000..a7e676a50 --- /dev/null +++ b/src/charon/sa/tasks/child_delete.h @@ -0,0 +1,66 @@ +/** + * @file child_delete.h + * + * @brief Interface child_delete_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CHILD_DELETE_H_ +#define CHILD_DELETE_H_ + +typedef struct child_delete_t child_delete_t; + +#include +#include +#include +#include + +/** + * @brief Task of type child_delete, delete a CHILD_SA. + * + * @b Constructors: + * - child_delete_create() + * + * @ingroup tasks + */ +struct child_delete_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Get the CHILD_SA to delete by this task. + * + * @param this calling object + * @return child_sa + */ + child_sa_t* (*get_child) (child_delete_t *this); +}; + +/** + * @brief Create a new child_delete task. + * + * @param ike_sa IKE_SA this task works for + * @param child_sa CHILD_SA to delete, or NULL as responder + * @return child_delete task to handle by the task_manager + */ +child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa); + +#endif /* CHILD_DELETE_H_ */ diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c new file mode 100644 index 000000000..745895dbb --- /dev/null +++ b/src/charon/sa/tasks/child_rekey.c @@ -0,0 +1,346 @@ +/** + * @file child_rekey.c + * + * @brief Implementation of the child_rekey task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "child_rekey.h" + +#include +#include +#include +#include +#include + + +typedef struct private_child_rekey_t private_child_rekey_t; + +/** + * Private members of a child_rekey_t task. + */ +struct private_child_rekey_t { + + /** + * Public methods and task_t interface. + */ + child_rekey_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * the CHILD_CREATE task which is reused to simplify rekeying + */ + child_create_t *child_create; + + /** + * CHILD_SA which gets rekeyed + */ + child_sa_t *child_sa; + + /** + * colliding task, may be delete or rekey + */ + task_t *collision; +}; + +/** + * find a child using the REKEY_SA notify + */ +static void find_child(private_child_rekey_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + notify_payload_t *notify; + u_int32_t spi; + protocol_id_t protocol; + + if (payload->get_type(payload) != NOTIFY) + { + continue; + } + + notify = (notify_payload_t*)payload; + protocol = notify->get_protocol_id(notify); + spi = notify->get_spi(notify); + + if (protocol != PROTO_ESP && protocol != PROTO_AH) + { + continue; + } + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); + break; + + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_child_rekey_t *this, message_t *message) +{ + notify_payload_t *notify; + protocol_id_t protocol; + u_int32_t spi, reqid; + + /* we just need the rekey notify ... */ + protocol = this->child_sa->get_protocol(this->child_sa); + spi = this->child_sa->get_spi(this->child_sa, TRUE); + notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA); + notify->set_spi(notify, spi); + message->add_payload(message, (payload_t*)notify); + + /* ... our CHILD_CREATE task does the hard work for us. */ + reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create->use_reqid(this->child_create, reqid); + this->child_create->task.build(&this->child_create->task, message); + + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_child_rekey_t *this, message_t *message) +{ + /* let the CHILD_CREATE task process the message */ + this->child_create->task.process(&this->child_create->task, message); + + find_child(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_child_rekey_t *this, message_t *message) +{ + u_int32_t reqid; + + if (this->child_sa == NULL || + this->child_sa->get_state(this->child_sa) == CHILD_DELETING) + { + DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + /* let the CHILD_CREATE task build the response */ + reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create->use_reqid(this->child_create, reqid); + this->child_create->task.build(&this->child_create->task, message); + + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) + { + /* rekeying failed, reuse old child */ + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + return SUCCESS; + } + + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_child_rekey_t *this, message_t *message) +{ + protocol_id_t protocol; + u_int32_t spi; + child_sa_t *to_delete; + + this->child_create->task.process(&this->child_create->task, message); + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) + { + /* establishing new child failed, reuse old. but not when we + * recieved a delete in the meantime */ + if (!(this->collision && + this->collision->get_type(this->collision) == CHILD_DELETE)) + { + job_t *job; + u_int32_t retry = charon->configuration->get_retry_interval( + charon->configuration); + job = (job_t*)rekey_child_sa_job_create( + this->child_sa->get_reqid(this->child_sa), + this->child_sa->get_protocol(this->child_sa), + this->child_sa->get_spi(this->child_sa, TRUE)); + DBG1(DBG_IKE, "CHILD_SA rekeying failed, " + "trying again in %d seconds", retry); + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + charon->event_queue->add_relative(charon->event_queue, job, retry * 1000); + } + return SUCCESS; + } + + to_delete = this->child_sa; + + /* check for rekey collisions */ + if (this->collision && + this->collision->get_type(this->collision) == CHILD_REKEY) + { + chunk_t this_nonce, other_nonce; + private_child_rekey_t *other = (private_child_rekey_t*)this->collision; + + this_nonce = this->child_create->get_lower_nonce(this->child_create); + other_nonce = other->child_create->get_lower_nonce(other->child_create); + + /* if we have the lower nonce, delete rekeyed SA. If not, delete + * the redundant. */ + if (memcmp(this_nonce.ptr, other_nonce.ptr, + min(this_nonce.len, other_nonce.len)) < 0) + { + DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child"); + } + else + { + DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child"); + to_delete = this->child_create->get_child(this->child_create); + if (to_delete == NULL) + { + /* ooops, should not happen, fallback */ + to_delete = this->child_sa; + } + } + } + + spi = to_delete->get_spi(to_delete, TRUE); + protocol = to_delete->get_protocol(to_delete); + if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_child_rekey_t *this) +{ + return CHILD_REKEY; +} + +/** + * Implementation of child_rekey_t.collide + */ +static void collide(private_child_rekey_t *this, task_t *other) +{ + /* the task manager only detects exchange collision, but not if + * the collision is for the same child. we check it here. */ + if (other->get_type(other) == CHILD_REKEY) + { + private_child_rekey_t *rekey = (private_child_rekey_t*)other; + if (rekey == NULL || rekey->child_sa != this->child_sa) + { + /* not the same child => no collision */ + return; + } + } + else if (other->get_type(other) == CHILD_DELETE) + { + child_delete_t *del = (child_delete_t*)other; + if (del == NULL || del->get_child(del) != this->child_sa) + { + /* not the same child => no collision */ + return; + } + } + else + { + /* any other task is not critical for collisisions, ignore */ + return; + } + DESTROY_IF(this->collision); + this->collision = other; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) +{ + this->child_create->task.migrate(&this->child_create->task, ike_sa); + DESTROY_IF(this->collision); + + this->ike_sa = ike_sa; + this->collision = NULL; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_child_rekey_t *this) +{ + this->child_create->task.destroy(&this->child_create->task); + DESTROY_IF(this->collision); + free(this); +} + +/* + * Described in header. + */ +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + private_child_rekey_t *this = malloc_thing(private_child_rekey_t); + policy_t *policy; + + this->public.collide = (void (*)(child_rekey_t*,task_t*))collide; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (child_sa != NULL) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + policy = child_sa->get_policy(child_sa); + this->child_create = child_create_create(ike_sa, policy); + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + this->child_create = child_create_create(ike_sa, NULL); + } + + this->ike_sa = ike_sa; + this->child_sa = child_sa; + this->collision = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h new file mode 100644 index 000000000..3515f0c3f --- /dev/null +++ b/src/charon/sa/tasks/child_rekey.h @@ -0,0 +1,70 @@ +/** + * @file child_rekey.h + * + * @brief Interface child_rekey_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CHILD_REKEY_H_ +#define CHILD_REKEY_H_ + +typedef struct child_rekey_t child_rekey_t; + +#include +#include +#include +#include + +/** + * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA. + * + * @b Constructors: + * - child_rekey_create() + * + * @ingroup tasks + */ +struct child_rekey_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Register a rekeying task which collides with this one + * + * If two peers initiate rekeying at the same time, the collision must + * be handled gracefully. The task manager is aware of what exchanges + * are going on and notifies the outgoing task by passing the incoming. + * + * @param this task initated by us + * @param other incoming task + */ + void (*collide)(child_rekey_t* this, task_t *other); +}; + +/** + * @brief Create a new CHILD_REKEY task. + * + * @param ike_sa IKE_SA this task works for + * @param child_sa child_sa to rekey, NULL if responder + * @return child_rekey task to handle by the task_manager + */ +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa); + +#endif /* CHILD_REKEY_H_ */ diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c new file mode 100644 index 000000000..541e1bb37 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth.c @@ -0,0 +1,750 @@ +/** + * @file ike_auth.c + * + * @brief Implementation of the ike_auth task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_auth.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + + + +typedef struct private_ike_auth_t private_ike_auth_t; + +/** + * Private members of a ike_auth_t task. + */ +struct private_ike_auth_t { + + /** + * Public methods and task_t interface. + */ + ike_auth_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Nonce chosen by us in ike_init + */ + chunk_t my_nonce; + + /** + * Nonce chosen by peer in ike_init + */ + chunk_t other_nonce; + + /** + * IKE_SA_INIT message sent by us + */ + packet_t *my_packet; + + /** + * IKE_SA_INIT message sent by peer + */ + packet_t *other_packet; + + /** + * EAP authenticator when using EAP + */ + eap_authenticator_t *eap_auth; + + /** + * EAP payload received and ready to process + */ + eap_payload_t *eap_payload; + + /** + * has the peer been authenticated successfully? + */ + bool peer_authenticated; +}; + +/** + * build the AUTH payload + */ +static status_t build_auth(private_ike_auth_t *this, message_t *message) +{ + authenticator_t *auth; + auth_payload_t *auth_payload; + policy_t *policy; + auth_method_t method; + status_t status; + + /* create own authenticator and add auth payload */ + policy = this->ike_sa->get_policy(this->ike_sa); + if (!policy) + { + SIG(IKE_UP_FAILED, "unable to authenticate, no policy found"); + return FAILED; + } + method = policy->get_auth_method(policy); + + auth = authenticator_create(this->ike_sa, method); + if (auth == NULL) + { + SIG(IKE_UP_FAILED, "configured authentication method %N not supported", + auth_method_names, method); + return FAILED; + } + + status = auth->build(auth, this->my_packet->get_data(this->my_packet), + this->other_nonce, &auth_payload); + auth->destroy(auth); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "generating authentication data failed"); + return FAILED; + } + message->add_payload(message, (payload_t*)auth_payload); + return SUCCESS; +} + +/** + * build ID payload(s) + */ +static status_t build_id(private_ike_auth_t *this, message_t *message) +{ + identification_t *me, *other; + id_payload_t *id; + policy_t *policy; + + me = this->ike_sa->get_my_id(this->ike_sa); + other = this->ike_sa->get_other_id(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + + if (me->contains_wildcards(me)) + { + me = policy->get_my_id(policy); + if (me->contains_wildcards(me)) + { + SIG(IKE_UP_FAILED, "negotiation of own ID failed"); + return FAILED; + } + this->ike_sa->set_my_id(this->ike_sa, me->clone(me)); + } + + id = id_payload_create_from_identification(this->initiator, me); + message->add_payload(message, (payload_t*)id); + + /* as initiator, include other ID if it does not contain wildcards */ + if (this->initiator && !other->contains_wildcards(other)) + { + id = id_payload_create_from_identification(FALSE, other); + message->add_payload(message, (payload_t*)id); + } + return SUCCESS; +} + +/** + * process AUTH payload + */ +static status_t process_auth(private_ike_auth_t *this, message_t *message) +{ + auth_payload_t *auth_payload; + authenticator_t *auth; + auth_method_t auth_method; + status_t status; + + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); + + if (auth_payload == NULL) + { + /* AUTH payload is missing, client wants to use EAP authentication */ + return NOT_FOUND; + } + + auth_method = auth_payload->get_auth_method(auth_payload); + auth = authenticator_create(this->ike_sa, auth_method); + + if (auth == NULL) + { + SIG(IKE_UP_FAILED, "authentication method %N used by %D not " + "supported", auth_method_names, auth_method, + this->ike_sa->get_other_id(this->ike_sa)); + return NOT_SUPPORTED; + } + status = auth->verify(auth, this->other_packet->get_data(this->other_packet), + this->my_nonce, auth_payload); + auth->destroy(auth); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, auth_method); + return FAILED; + } + return SUCCESS; +} + +/** + * process ID payload(s) + */ +static status_t process_id(private_ike_auth_t *this, message_t *message) +{ + identification_t *id; + id_payload_t *idr, *idi; + + idi = (id_payload_t*)message->get_payload(message, ID_INITIATOR); + idr = (id_payload_t*)message->get_payload(message, ID_RESPONDER); + + if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL)) + { + SIG(IKE_UP_FAILED, "ID payload missing in message"); + return FAILED; + } + + if (this->initiator) + { + id = idr->get_identification(idr); + this->ike_sa->set_other_id(this->ike_sa, id); + } + else + { + id = idi->get_identification(idi); + this->ike_sa->set_other_id(this->ike_sa, id); + if (idr) + { + id = idr->get_identification(idr); + this->ike_sa->set_my_id(this->ike_sa, id); + } + } + return SUCCESS; +} + +/** + * collect the needed information in the IKE_SA_INIT exchange from our message + */ +static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message) +{ + nonce_payload_t *nonce; + + /* get the nonce that was generated in ike_init */ + nonce = (nonce_payload_t*)message->get_payload(message, NONCE); + if (nonce == NULL) + { + return FAILED; + } + this->my_nonce = nonce->get_nonce(nonce); + + /* pre-generate the message, so we can store it for us */ + if (this->ike_sa->generate_message(this->ike_sa, message, + &this->my_packet) != SUCCESS) + { + return FAILED; + } + return NEED_MORE; +} + +/** + * collect the needed information in the IKE_SA_INIT exchange from others message + */ +static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message) +{ + /* we collect the needed information in the IKE_SA_INIT exchange */ + nonce_payload_t *nonce; + + /* get the nonce that was generated in ike_init */ + nonce = (nonce_payload_t*)message->get_payload(message, NONCE); + if (nonce == NULL) + { + return FAILED; + } + this->other_nonce = nonce->get_nonce(nonce); + + /* pre-generate the message, so we can store it for us */ + this->other_packet = message->get_packet(message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build to create AUTH payload from EAP data + */ +static status_t build_auth_eap(private_ike_auth_t *this, message_t *message) +{ + authenticator_t *auth; + auth_payload_t *auth_payload; + + auth = (authenticator_t*)this->eap_auth; + if (auth->build(auth, this->my_packet->get_data(this->my_packet), + this->other_nonce, &auth_payload) != SUCCESS) + { + SIG(IKE_UP_FAILED, "generating authentication data failed"); + if (!this->initiator) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + } + return FAILED; + } + message->add_payload(message, (payload_t*)auth_payload); + if (!this->initiator) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process to verify AUTH payload after EAP + */ +static status_t process_auth_eap(private_ike_auth_t *this, message_t *message) +{ + auth_payload_t *auth_payload; + authenticator_t *auth; + + auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION); + this->peer_authenticated = FALSE; + + if (auth_payload) + { + auth = (authenticator_t*)this->eap_auth; + if (auth->verify(auth, this->other_packet->get_data(this->other_packet), + this->my_nonce, auth_payload) == SUCCESS) + { + this->peer_authenticated = TRUE; + } + } + + if (!this->peer_authenticated) + { + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, AUTH_EAP); + if (this->initiator) + { + return FAILED; + } + return NEED_MORE; + } + if (this->initiator) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for EAP exchanges + */ +static status_t process_eap_i(private_ike_auth_t *this, message_t *message) +{ + eap_payload_t *eap; + + eap = (eap_payload_t*)message->get_payload(message, EXTENSIBLE_AUTHENTICATION); + if (eap == NULL) + { + SIG(IKE_UP_FAILED, "EAP payload missing"); + return FAILED; + } + switch (this->eap_auth->process(this->eap_auth, eap, &eap)) + { + case NEED_MORE: + this->eap_payload = eap; + return NEED_MORE; + case SUCCESS: + /* EAP exchange completed, now create and process AUTH */ + this->eap_payload = NULL; + this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap; + return NEED_MORE; + default: + this->eap_payload = NULL; + SIG(IKE_UP_FAILED, "failed to authenticate against %D using EAP", + this->ike_sa->get_other_id(this->ike_sa)); + return FAILED; + } +} + +/** + * Implementation of task_t.process for EAP exchanges + */ +static status_t process_eap_r(private_ike_auth_t *this, message_t *message) +{ + this->eap_payload = (eap_payload_t*)message->get_payload(message, + EXTENSIBLE_AUTHENTICATION); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for EAP exchanges + */ +static status_t build_eap_i(private_ike_auth_t *this, message_t *message) +{ + message->add_payload(message, (payload_t*)this->eap_payload); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for EAP exchanges + */ +static status_t build_eap_r(private_ike_auth_t *this, message_t *message) +{ + status_t status = NEED_MORE; + eap_payload_t *eap; + + if (this->eap_payload == NULL) + { + SIG(IKE_UP_FAILED, "EAP payload missing"); + return FAILED; + } + + switch (this->eap_auth->process(this->eap_auth, this->eap_payload, &eap)) + { + case NEED_MORE: + + break; + case SUCCESS: + /* EAP exchange completed, now create and process AUTH */ + this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap; + break; + default: + SIG(IKE_UP_FAILED, "authentication of %D using %N failed", + this->ike_sa->get_other_id(this->ike_sa), + auth_method_names, AUTH_EAP); + status = FAILED; + break; + } + message->add_payload(message, (payload_t*)eap); + return status; +} + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_auth_t *this, message_t *message) +{ + policy_t *policy; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_my_init_data(this, message); + } + + if (build_id(this, message) != SUCCESS) + { + return FAILED; + } + + policy = this->ike_sa->get_policy(this->ike_sa); + if (policy->get_auth_method(policy) == AUTH_EAP) + { + this->eap_auth = eap_authenticator_create(this->ike_sa); + } + else + { + if (build_auth(this, message) != SUCCESS) + { + return FAILED; + } + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_auth_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_other_init_data(this, message); + } + + if (process_id(this, message) != SUCCESS) + { + return NEED_MORE; + } + + switch (process_auth(this, message)) + { + case SUCCESS: + this->peer_authenticated = TRUE; + break; + case NOT_FOUND: + /* use EAP if no AUTH payload found */ + this->eap_auth = eap_authenticator_create(this->ike_sa); + break; + default: + break; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_auth_t *this, message_t *message) +{ + policy_t *policy; + eap_type_t eap_type; + eap_payload_t *eap_payload; + status_t status; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_my_init_data(this, message); + } + + policy = this->ike_sa->get_policy(this->ike_sa); + if (policy == NULL) + { + SIG(IKE_UP_FAILED, "no acceptable policy found"); + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + if (build_id(this, message) != SUCCESS || + build_auth(this, message) != SUCCESS) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + /* use "traditional" authentication if we could authenticate peer */ + if (this->peer_authenticated) + { + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; + } + + if (this->eap_auth == NULL) + { + /* peer not authenticated, nor does it want to use EAP */ + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + + /* initiate EAP authenitcation */ + eap_type = policy->get_eap_type(policy); + status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_payload); + message->add_payload(message, (payload_t*)eap_payload); + if (status != NEED_MORE) + { + SIG(IKE_UP_FAILED, "unable to initiate EAP authentication"); + return FAILED; + } + + /* switch to EAP methods */ + this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_r; + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_auth_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return collect_other_init_data(this, message); + } + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case NO_PROPOSAL_CHOSEN: + case SINGLE_PAIR_REQUIRED: + case NO_ADDITIONAL_SAS: + case INTERNAL_ADDRESS_FAILURE: + case FAILED_CP_REQUIRED: + case TS_UNACCEPTABLE: + case INVALID_SELECTORS: + /* these are errors, but are not critical as only the + * CHILD_SA won't get build, but IKE_SA establishes anyway */ + break; + default: + { + if (type < 16383) + { + SIG(IKE_UP_FAILED, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + return FAILED; + } + DBG1(DBG_IKE, "received %N notify", + notify_type_names, type); + break; + } + } + } + } + iterator->destroy(iterator); + + if (process_id(this, message) != SUCCESS || + process_auth(this, message) != SUCCESS) + { + return FAILED; + } + + if (this->eap_auth) + { + /* switch to EAP authentication methods */ + this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_i; + return process_eap_i(this, message); + } + + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D", + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_auth_t *this) +{ + return IKE_AUTHENTICATE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + DESTROY_IF(this->my_packet); + DESTROY_IF(this->other_packet); + if (this->eap_auth) + { + this->eap_auth->authenticator_interface.destroy( + &this->eap_auth->authenticator_interface); + } + + this->my_packet = NULL; + this->other_packet = NULL; + this->peer_authenticated = FALSE; + this->eap_auth = NULL; + this->eap_payload = NULL; + this->ike_sa = ike_sa; + if (this->initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_auth_t *this) +{ + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + DESTROY_IF(this->my_packet); + DESTROY_IF(this->other_packet); + if (this->eap_auth) + { + this->eap_auth->authenticator_interface.destroy( + &this->eap_auth->authenticator_interface); + } + free(this); +} + +/* + * Described in header. + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_auth_t *this = malloc_thing(private_ike_auth_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->my_packet = NULL; + this->other_packet = NULL; + this->peer_authenticated = FALSE; + this->eap_auth = NULL; + this->eap_payload = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h new file mode 100644 index 000000000..d7326c988 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth.h @@ -0,0 +1,64 @@ +/** + * @file ike_auth.h + * + * @brief Interface ike_auth_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_AUTH_H_ +#define IKE_AUTH_H_ + +typedef struct ike_auth_t ike_auth_t; + +#include +#include +#include + +/** + * @brief Task of type ike_auth, authenticates an IKE_SA using authenticators. + * + * The ike_auth task authenticates the IKE_SA using the IKE_AUTH + * exchange. It processes and build IDi and IDr payloads and also + * handles AUTH payloads. The AUTH payloads are passed to authenticator_t's, + * which do the actual authentication process. If the ike_auth task is used + * with EAP authentication, it stays alive over multiple exchanges until + * EAP has completed. + * + * @b Constructors: + * - ike_auth_create() + * + * @ingroup tasks + */ +struct ike_auth_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new task of type IKE_AUTHENTICATE. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the initator of an exchange + * @return ike_auth task to handle by the task_manager + */ +ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_AUTH_H_ */ diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c new file mode 100644 index 000000000..160600742 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert.c @@ -0,0 +1,370 @@ +/** + * @file ike_cert.c + * + * @brief Implementation of the ike_cert task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_cert.h" + +#include +#include +#include +#include +#include + + +typedef struct private_ike_cert_t private_ike_cert_t; + +/** + * Private members of a ike_cert_t task. + */ +struct private_ike_cert_t { + + /** + * Public methods and task_t interface. + */ + ike_cert_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * list of CA cert hashes requested, items point to 20 byte chunk + */ + linked_list_t *cas; + + /** + * have we seen a certificate request? + */ + bool certreq_seen; +}; + +/** + * read certificate requests + */ +static void process_certreqs(private_ike_cert_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE_REQUEST) + { + certreq_payload_t *certreq = (certreq_payload_t*)payload; + cert_encoding_t encoding; + chunk_t keyids, keyid; + + this->certreq_seen = TRUE; + + encoding = certreq->get_cert_encoding(certreq); + if (encoding != CERT_X509_SIGNATURE) + { + DBG1(DBG_IKE, "certreq payload %N not supported, ignored", + cert_encoding_names, encoding); + continue; + } + + keyids = certreq->get_data(certreq); + + while (keyids.len >= HASH_SIZE_SHA1) + { + keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1); + keyid = chunk_clone(keyid); + this->cas->insert_last(this->cas, keyid.ptr); + keyids = chunk_skip(keyids, HASH_SIZE_SHA1); + } + } + } + iterator->destroy(iterator); +} + +/** + * import certificates + */ +static void process_certs(private_ike_cert_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE) + { + cert_encoding_t encoding; + x509_t *cert; + chunk_t cert_data; + bool found; + cert_payload_t *cert_payload = (cert_payload_t*)payload; + + encoding = cert_payload->get_cert_encoding(cert_payload); + if (encoding != CERT_X509_SIGNATURE) + { + DBG1(DBG_IKE, "certificate payload %N not supported, ignored", + cert_encoding_names, encoding); + continue; + } + + cert_data = cert_payload->get_data_clone(cert_payload); + cert = x509_create_from_chunk(cert_data, 0); + if (cert) + { + if (charon->credentials->verify(charon->credentials, + cert, &found)) + { + DBG2(DBG_IKE, "received end entity certificate is trusted, " + "added to store"); + if (!found) + { + charon->credentials->add_end_certificate( + charon->credentials, cert); + } + else + { + cert->destroy(cert); + } + } + else + { + DBG1(DBG_IKE, "received end entity certificate is not " + "trusted, discarded"); + cert->destroy(cert); + } + } + else + { + DBG1(DBG_IKE, "parsing of received certificate failed, discarded"); + chunk_free(&cert_data); + } + } + } + iterator->destroy(iterator); +} + +/** + * build certificate requests + */ +static void build_certreqs(private_ike_cert_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + identification_t *ca; + certreq_payload_t *certreq; + + connection = this->ike_sa->get_connection(this->ike_sa); + + if (connection->get_certreq_policy(connection) != CERT_NEVER_SEND) + { + policy = this->ike_sa->get_policy(this->ike_sa); + + if (policy) + { + ca = policy->get_other_ca(policy); + + if (ca && ca->get_type(ca) != ID_ANY) + { + certreq = certreq_payload_create_from_cacert(ca); + } + else + { + certreq = certreq_payload_create_from_cacerts(); + } + } + else + { + certreq = certreq_payload_create_from_cacerts(); + } + + if (certreq) + { + message->add_payload(message, (payload_t*)certreq); + } + } +} + +/** + * add certificates to message + */ +static void build_certs(private_ike_cert_t *this, message_t *message) +{ + policy_t *policy; + connection_t *connection; + x509_t *cert; + cert_payload_t *payload; + + policy = this->ike_sa->get_policy(this->ike_sa); + connection = this->ike_sa->get_connection(this->ike_sa); + + if (policy && policy->get_auth_method(policy) == AUTH_RSA) + { + switch (connection->get_cert_policy(connection)) + { + case CERT_NEVER_SEND: + break; + case CERT_SEND_IF_ASKED: + if (!this->certreq_seen) + { + break; + } + /* FALL */ + case CERT_ALWAYS_SEND: + { + /* TODO: respect CA cert request */ + cert = charon->credentials->get_certificate(charon->credentials, + policy->get_my_id(policy)); + if (cert) + { + payload = cert_payload_create_from_x509(cert); + message->add_payload(message, (payload_t*)payload); + } + } + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + + build_certreqs(this, message); + build_certs(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + + process_certreqs(this, message); + process_certs(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + build_certreqs(this, message); + return NEED_MORE; + } + + build_certs(this, message); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_cert_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + process_certreqs(this, message); + return NEED_MORE; + } + + process_certs(this, message); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_cert_t *this) +{ + return IKE_CERT; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + + this->cas->destroy_function(this->cas, free); + this->cas = linked_list_create(); + this->certreq_seen = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_cert_t *this) +{ + this->cas->destroy_function(this->cas, free); + free(this); +} + +/* + * Described in header. + */ +ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_cert_t *this = malloc_thing(private_ike_cert_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->cas = linked_list_create(); + this->certreq_seen = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert.h new file mode 100644 index 000000000..ba0283953 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert.h @@ -0,0 +1,61 @@ +/** + * @file ike_cert.h + * + * @brief Interface ike_cert_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_CERT_H_ +#define IKE_CERT_H_ + +typedef struct ike_cert_t ike_cert_t; + +#include +#include +#include + +/** + * @brief Task of type ike_cert, exchanges certificates and + * certificate requests. + * + * @b Constructors: + * - ike_cert_create() + * + * @ingroup tasks + */ +struct ike_cert_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_cert task. + * + * The initiator parameter means the original initiator, not the initiator + * of the certificate request. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_cert task to handle by the task_manager + */ +ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_CERT_H_ */ diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c new file mode 100644 index 000000000..ce29b9220 --- /dev/null +++ b/src/charon/sa/tasks/ike_config.c @@ -0,0 +1,428 @@ +/** + * @file ike_config.c + * + * @brief Implementation of the ike_config task. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_config.h" + +#include +#include + +typedef struct private_ike_config_t private_ike_config_t; + +/** + * Private members of a ike_config_t task. + */ +struct private_ike_config_t { + + /** + * Public methods and task_t interface. + */ + ike_config_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * associated policy with virtual IP configuration + */ + policy_t *policy; + + /** + * virtual ip + */ + host_t *virtual_ip; + + /** + * list of DNS servers + */ + linked_list_t *dns; +}; + +/** + * build configuration payloads and attributes + */ +static void build_payloads(private_ike_config_t *this, message_t *message, + config_type_t type) +{ + cp_payload_t *cp; + configuration_attribute_t *ca; + chunk_t chunk, prefix; + + if (!this->virtual_ip) + { + return; + } + + cp = cp_payload_create(); + cp->set_config_type(cp, type); + + ca = configuration_attribute_create(); + + if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_ADDRESS); + if (this->virtual_ip->is_anyaddr(this->virtual_ip)) + { + chunk = chunk_empty; + } + else + { + chunk = this->virtual_ip->get_address(this->virtual_ip); + } + } + else + { + ca->set_type(ca, INTERNAL_IP6_ADDRESS); + if (this->virtual_ip->is_anyaddr(this->virtual_ip)) + { + chunk = chunk_empty; + } + else + { + prefix = chunk_alloca(1); + *prefix.ptr = 64; + chunk = this->virtual_ip->get_address(this->virtual_ip); + chunk = chunk_cata("cc", chunk, prefix); + } + } + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + + /* we currently always add a DNS request if we request an IP */ + if (this->initiator) + { + ca = configuration_attribute_create(); + if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_DNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_DNS); + } + cp->add_configuration_attribute(cp, ca); + } + else + { + host_t *ip; + iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE); + while (iterator->iterate(iterator, (void**)&ip)) + { + ca = configuration_attribute_create(); + if (ip->get_family(ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_DNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_DNS); + } + chunk = ip->get_address(ip); + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + } + iterator->destroy(iterator); + } + message->add_payload(message, (payload_t*)cp); +} + +/** + * process a single configuration attribute + */ +static void process_attribute(private_ike_config_t *this, + configuration_attribute_t *ca) +{ + host_t *ip; + chunk_t addr; + int family = AF_INET6; + + switch (ca->get_type(ca)) + { + case INTERNAL_IP4_ADDRESS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_ADDRESS: + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + /* skip prefix byte in IPv6 payload*/ + if (family == AF_INET6) + { + addr.len--; + } + ip = host_create_from_chunk(family, addr, 0); + } + if (ip && !this->virtual_ip) + { + this->virtual_ip = ip; + } + break; + } + case INTERNAL_IP4_DNS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_DNS: + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + ip = host_create_from_chunk(family, addr, 0); + } + if (ip) + { + this->dns->insert_last(this->dns, ip); + } + break; + } + case INTERNAL_IP4_NBNS: + case INTERNAL_IP6_NBNS: + /* TODO */ + default: + DBG1(DBG_IKE, "ignoring %N config attribute", + configuration_attribute_type_names, + ca->get_type(ca)); + break; + } +} + +/** + * Scan for configuration payloads and attributes + */ +static void process_payloads(private_ike_config_t *this, message_t *message) +{ + iterator_t *iterator, *attributes; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CONFIGURATION) + { + cp_payload_t *cp = (cp_payload_t*)payload; + configuration_attribute_t *ca; + switch (cp->get_config_type(cp)) + { + case CFG_REQUEST: + case CFG_REPLY: + { + attributes = cp->create_attribute_iterator(cp); + while (attributes->iterate(attributes, (void**)&ca)) + { + process_attribute(this, ca); + } + attributes->destroy(attributes); + break; + } + default: + DBG1(DBG_IKE, "ignoring %N config payload", + config_type_names, cp->get_config_type(cp)); + break; + } + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, ID_INITIATOR)) + { + this->virtual_ip = this->policy->get_virtual_ip(this->policy, NULL); + + build_payloads(this, message, CFG_REQUEST); + } + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, ID_INITIATOR)) + { + process_payloads(this, message); + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, EXTENSIBLE_AUTHENTICATION) == NULL) + { + this->policy = this->ike_sa->get_policy(this->ike_sa); + + if (this->policy && this->virtual_ip) + { + host_t *ip; + + DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); + ip = this->policy->get_virtual_ip(this->policy, this->virtual_ip); + if (ip == NULL || ip->is_anyaddr(ip)) + { + DBG1(DBG_IKE, "not assigning a virtual IP to peer"); + return SUCCESS; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip); + this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, ip); + + this->virtual_ip->destroy(this->virtual_ip); + this->virtual_ip = ip; + + /* DNS testing values + if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) + { + ip->destroy(ip); + ip = host_create_from_string("10.3.0.1", 0); + this->dns->insert_last(this->dns, ip); + ip = host_create_from_string("10.3.0.2", 0); + this->dns->insert_last(this->dns, ip); + } */ + + build_payloads(this, message, CFG_REPLY); + } + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_config_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + !message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + host_t *ip; + + DESTROY_IF(this->virtual_ip); + this->virtual_ip = NULL; + + process_payloads(this, message); + + if (this->virtual_ip) + { + this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + + while (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) + { + if (!ip->is_anyaddr(ip)) + { + this->ike_sa->add_dns_server(this->ike_sa, ip); + } + ip->destroy(ip); + } + } + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_config_t *this) +{ + return IKE_CONFIG; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa) +{ + DESTROY_IF(this->virtual_ip); + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + + this->ike_sa = ike_sa; + this->virtual_ip = NULL; + this->dns = linked_list_create(); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_config_t *this) +{ + DESTROY_IF(this->virtual_ip); + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy) +{ + private_ike_config_t *this = malloc_thing(private_ike_config_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (policy) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + this->initiator = TRUE; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + this->initiator = FALSE; + } + + this->ike_sa = ike_sa; + this->policy = policy; + this->virtual_ip = NULL; + this->dns = linked_list_create(); + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h new file mode 100644 index 000000000..0c9b961b4 --- /dev/null +++ b/src/charon/sa/tasks/ike_config.h @@ -0,0 +1,59 @@ +/** + * @file ike_config.h + * + * @brief Interface ike_config_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_CONFIG_H_ +#define IKE_CONFIG_H_ + +typedef struct ike_config_t ike_config_t; + +#include +#include +#include +#include + +/** + * @brief Task of type IKE_CONFIG, sets up a virtual IP and other + * configurations for an IKE_SA. + * + * @b Constructors: + * - ike_config_create() + * + * @ingroup tasks + */ +struct ike_config_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_config task. + * + * @param ike_sa IKE_SA this task works for + * @param policy policy for the initiator, NULL for the responder + * @return ike_config task to handle by the task_manager + */ +ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy); + +#endif /* IKE_CONFIG_H_ */ diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c new file mode 100644 index 000000000..9c4fdac0e --- /dev/null +++ b/src/charon/sa/tasks/ike_delete.c @@ -0,0 +1,172 @@ +/** + * @file ike_delete.c + * + * @brief Implementation of the ike_delete task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_delete.h" + +#include +#include + + +typedef struct private_ike_delete_t private_ike_delete_t; + +/** + * Private members of a ike_delete_t task. + */ +struct private_ike_delete_t { + + /** + * Public methods and task_t interface. + */ + ike_delete_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * are we responding to a delete, but have initated our own? + */ + bool simultaneous; +}; + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_delete_t *this, message_t *message) +{ + delete_payload_t *delete_payload; + + delete_payload = delete_payload_create(PROTO_IKE); + message->add_payload(message, (payload_t*)delete_payload); + + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_delete_t *this, message_t *message) +{ + /* completed, delete IKE_SA by returning FAILED */ + return FAILED; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_delete_t *this, message_t *message) +{ + /* we don't even scan the payloads, as the message wouldn't have + * come so far without being correct */ + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_DELETING: + this->simultaneous = TRUE; + break; + case IKE_ESTABLISHED: + DBG1(DBG_IKE, "deleting IKE_SA on request"); + break; + case IKE_REKEYING: + DBG1(DBG_IKE, "initiated rekeying, but received delete for IKE_SA"); + break; + default: + break; + } + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_delete_t *this, message_t *message) +{ + if (this->simultaneous) + { + /* wait for peers response for our delete request, but set a timeout */ + return SUCCESS; + } + /* completed, delete IKE_SA by returning FAILED */ + return FAILED; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_delete_t *this) +{ + return IKE_DELETE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_delete_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->simultaneous = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_delete_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_delete_t *this = malloc_thing(private_ike_delete_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->simultaneous = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h new file mode 100644 index 000000000..e8ec5ebbe --- /dev/null +++ b/src/charon/sa/tasks/ike_delete.h @@ -0,0 +1,57 @@ +/** + * @file ike_delete.h + * + * @brief Interface ike_delete_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_DELETE_H_ +#define IKE_DELETE_H_ + +typedef struct ike_delete_t ike_delete_t; + +#include +#include +#include + +/** + * @brief Task of type ike_delete, delete an IKE_SA. + * + * @b Constructors: + * - ike_delete_create() + * + * @ingroup tasks + */ +struct ike_delete_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_delete task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if we initiate the delete + * @return ike_delete task to handle by the task_manager + */ +ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_DELETE_H_ */ diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c new file mode 100644 index 000000000..1cb05c45c --- /dev/null +++ b/src/charon/sa/tasks/ike_dpd.c @@ -0,0 +1,106 @@ +/** + * @file ike_dpd.c + * + * @brief Implementation of the ike_dpd task. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_dpd.h" + +#include + + +typedef struct private_ike_dpd_t private_ike_dpd_t; + +/** + * Private members of a ike_dpd_t task. + */ +struct private_ike_dpd_t { + + /** + * Public methods and task_t interface. + */ + ike_dpd_t public; +}; + +/** + * Implementation of task_t.build for initiator + * Implementation of task_t.process for responder + */ +static status_t return_need_more(private_ike_dpd_t *this, message_t *message) +{ + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + * Implementation of task_t.build for responder + */ +static status_t return_success(private_ike_dpd_t *this, message_t *message) +{ + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_dpd_t *this) +{ + return IKE_DEADPEER; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_dpd_t *this, ike_sa_t *ike_sa) +{ + +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_dpd_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_dpd_t *ike_dpd_create(bool initiator) +{ + private_ike_dpd_t *this = malloc_thing(private_ike_dpd_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more; + this->public.task.process = (status_t(*)(task_t*,message_t*))return_success; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))return_success; + this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more; + } + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h new file mode 100644 index 000000000..531b0502d --- /dev/null +++ b/src/charon/sa/tasks/ike_dpd.h @@ -0,0 +1,58 @@ +/** + * @file ike_dpd.h + * + * @brief Interface ike_dpd_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_DPD_H_ +#define IKE_DPD_H_ + +typedef struct ike_dpd_t ike_dpd_t; + +#include +#include +#include + +/** + * @brief Task of type ike_dpd, detects dead peers. + * + * The DPD task actually does nothing, as a DPD has no associated payloads. + * + * @b Constructors: + * - ike_dpd_create() + * + * @ingroup tasks + */ +struct ike_dpd_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_dpd task. + * + * @param initiator TRUE if thask is the original initator + * @return ike_dpd task to handle by the task_manager + */ +ike_dpd_t *ike_dpd_create(bool initiator); + +#endif /* IKE_DPD_H_ */ diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c new file mode 100644 index 000000000..0b493666a --- /dev/null +++ b/src/charon/sa/tasks/ike_init.c @@ -0,0 +1,598 @@ +/** + * @file ike_init.c + * + * @brief Implementation of the ike_init task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_init.h" + +#include + +#include +#include +#include +#include +#include + +/** maximum retries to do with cookies/other dh groups */ +#define MAX_RETRIES 5 + +typedef struct private_ike_init_t private_ike_init_t; + +/** + * Private members of a ike_init_t task. + */ +struct private_ike_init_t { + + /** + * Public methods and task_t interface. + */ + ike_init_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Connection established by this IKE_SA + */ + connection_t *connection; + + /** + * diffie hellman group to use + */ + diffie_hellman_group_t dh_group; + + /** + * Diffie hellman object used to generate public DH value. + */ + diffie_hellman_t *diffie_hellman; + + /** + * nonce chosen by us + */ + chunk_t my_nonce; + + /** + * nonce chosen by peer + */ + chunk_t other_nonce; + + /** + * Negotiated proposal used for IKE_SA + */ + proposal_t *proposal; + + /** + * Old IKE_SA which gets rekeyed + */ + ike_sa_t *old_sa; + + /** + * cookie received from responder + */ + chunk_t cookie; + + /** + * retries done so far after failure (cookie or bad dh group) + */ + u_int retry; +}; + +/** + * build the payloads for the message + */ +static void build_payloads(private_ike_init_t *this, message_t *message) +{ + sa_payload_t *sa_payload; + ke_payload_t *ke_payload; + nonce_payload_t *nonce_payload; + linked_list_t *proposal_list; + ike_sa_id_t *id; + proposal_t *proposal; + iterator_t *iterator; + + id = this->ike_sa->get_id(this->ike_sa); + + this->connection = this->ike_sa->get_connection(this->ike_sa); + + if (this->initiator) + { + proposal_list = this->connection->get_proposals(this->connection); + if (this->old_sa) + { + /* include SPI of new IKE_SA when we are rekeying */ + iterator = proposal_list->create_iterator(proposal_list, TRUE); + while (iterator->iterate(iterator, (void**)&proposal)) + { + proposal->set_spi(proposal, id->get_initiator_spi(id)); + } + iterator->destroy(iterator); + } + + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); + } + else + { + if (this->old_sa) + { + /* include SPI of new IKE_SA when we are rekeying */ + this->proposal->set_spi(this->proposal, id->get_responder_spi(id)); + } + sa_payload = sa_payload_create_from_proposal(this->proposal); + } + message->add_payload(message, (payload_t*)sa_payload); + + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); + message->add_payload(message, (payload_t*)nonce_payload); + + ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman); + message->add_payload(message, (payload_t*)ke_payload); +} + +/** + * Read payloads from message + */ +static void process_payloads(private_ike_init_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + { + sa_payload_t *sa_payload = (sa_payload_t*)payload; + linked_list_t *proposal_list; + + proposal_list = sa_payload->get_proposals(sa_payload); + this->proposal = this->connection->select_proposal( + this->connection, proposal_list); + proposal_list->destroy_offset(proposal_list, + offsetof(proposal_t, destroy)); + break; + } + case KEY_EXCHANGE: + { + ke_payload_t *ke_payload = (ke_payload_t*)payload; + diffie_hellman_group_t dh_group; + chunk_t key_data; + + dh_group = ke_payload->get_dh_group_number(ke_payload); + + if (this->initiator) + { + if (dh_group != this->dh_group) + { + DBG1(DBG_IKE, "received a DH group not requested (%N)", + diffie_hellman_group_names, dh_group); + break; + } + } + else + { + this->dh_group = dh_group; + if (!this->connection->check_dh_group(this->connection, + dh_group)) + { + break; + } + this->diffie_hellman = diffie_hellman_create(dh_group); + } + if (this->diffie_hellman) + { + key_data = ke_payload->get_key_exchange_data(ke_payload); + this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data); + } + break; + } + case NONCE: + { + nonce_payload_t *nonce_payload = (nonce_payload_t*)payload; + this->other_nonce = nonce_payload->get_nonce(nonce_payload); + break; + } + default: + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_init_t *this, message_t *message) +{ + randomizer_t *randomizer; + status_t status; + + this->connection = this->ike_sa->get_connection(this->ike_sa); + SIG(IKE_UP_START, "initiating IKE_SA to %H", + this->connection->get_other_host(this->connection)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + if (this->retry++ >= MAX_RETRIES) + { + SIG(IKE_UP_FAILED, "giving up after %d retries", MAX_RETRIES); + return FAILED; + } + + /* if the DH group is set via use_dh_group(), we already have a DH object */ + if (!this->diffie_hellman) + { + this->dh_group = this->connection->get_dh_group(this->connection); + this->diffie_hellman = diffie_hellman_create(this->dh_group); + if (this->diffie_hellman == NULL) + { + SIG(IKE_UP_FAILED, "configured DH group %N not supported", + diffie_hellman_group_names, this->dh_group); + return FAILED; + } + } + + /* generate nonce only when we are trying the first time */ + if (this->my_nonce.ptr == NULL) + { + randomizer = randomizer_create(); + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + &this->my_nonce); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "error generating random nonce value"); + return FAILED; + } + } + + if (this->cookie.ptr) + { + message->add_notify(message, FALSE, COOKIE, this->cookie); + } + + build_payloads(this, message); + + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_init_t *this, message_t *message) +{ + randomizer_t *randomizer; + + this->connection = this->ike_sa->get_connection(this->ike_sa); + SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA", + message->get_source(message)); + this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); + + randomizer = randomizer_create(); + if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, + &this->my_nonce) != SUCCESS) + { + DBG1(DBG_IKE, "error generating random nonce value"); + } + randomizer->destroy(randomizer); + + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_init_t *this, message_t *message) +{ + chunk_t secret; + status_t status; + + /* check if we have everything we need */ + if (this->proposal == NULL || + this->other_nonce.len == 0 || this->my_nonce.len == 0) + { + SIG(IKE_UP_FAILED, "received proposals inacceptable"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return FAILED; + } + + if (this->diffie_hellman == NULL || + this->diffie_hellman->get_shared_secret(this->diffie_hellman, + &secret) != SUCCESS) + { + chunk_t chunk; + u_int16_t dh_enc; + + SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)", + diffie_hellman_group_names, this->dh_group); + this->dh_group = this->connection->get_dh_group(this->connection); + dh_enc = htons(this->dh_group); + chunk.ptr = (u_int8_t*)&dh_enc; + chunk.len = sizeof(dh_enc); + message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk); + DBG1(DBG_IKE, "requesting DH group %N", + diffie_hellman_group_names, this->dh_group); + return FAILED; + } + + + if (this->old_sa) + { + ike_sa_id_t *id; + prf_t *prf, *child_prf; + + /* Apply SPI if we are rekeying */ + id = this->ike_sa->get_id(this->ike_sa); + id->set_initiator_spi(id, this->proposal->get_spi(this->proposal)); + + /* setup crypto keys for the rekeyed SA */ + prf = this->old_sa->get_prf(this->old_sa); + child_prf = this->old_sa->get_child_prf(this->old_sa); + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->other_nonce, this->my_nonce, + FALSE, child_prf, prf); + } + else + { + /* setup crypto keys */ + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->other_nonce, this->my_nonce, + FALSE, NULL, NULL); + } + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "key derivation failed"); + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return FAILED; + } + + build_payloads(this, message); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_init_t *this, message_t *message) +{ + chunk_t secret; + status_t status; + iterator_t *iterator; + payload_t *payload; + + /* check for erronous notifies */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case INVALID_KE_PAYLOAD: + { + chunk_t data; + diffie_hellman_group_t old_dh_group; + + old_dh_group = this->dh_group; + data = notify->get_notification_data(notify); + this->dh_group = ntohs(*((u_int16_t*)data.ptr)); + + DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested" + " %N", diffie_hellman_group_names, old_dh_group, + diffie_hellman_group_names, this->dh_group); + if (!this->connection->check_dh_group(this->connection, + this->dh_group)) + { + DBG1(DBG_IKE, "requested DH group %N not acceptable, " + "giving up", diffie_hellman_group_names, + this->dh_group); + iterator->destroy(iterator); + return FAILED; + } + + this->ike_sa->reset(this->ike_sa); + + iterator->destroy(iterator); + return NEED_MORE; + } + case NAT_DETECTION_SOURCE_IP: + case NAT_DETECTION_DESTINATION_IP: + /* skip, handled in ike_natd_t */ + break; + case COOKIE: + { + chunk_free(&this->cookie); + this->cookie = chunk_clone(notify->get_notification_data(notify)); + this->ike_sa->reset(this->ike_sa); + iterator->destroy(iterator); + DBG1(DBG_IKE, "received %N notify", notify_type_names, type); + return NEED_MORE; + } + default: + { + if (type < 16383) + { + SIG(IKE_UP_FAILED, "received %N notify error", + notify_type_names, type); + iterator->destroy(iterator); + return FAILED; + } + DBG1(DBG_IKE, "received %N notify", + notify_type_names, type); + break; + } + } + } + } + iterator->destroy(iterator); + + process_payloads(this, message); + + /* check if we have everything */ + if (this->proposal == NULL || + this->other_nonce.len == 0 || this->my_nonce.len == 0) + { + SIG(IKE_UP_FAILED, "peers proposal selection invalid"); + return FAILED; + } + + if (this->diffie_hellman == NULL || + this->diffie_hellman->get_shared_secret(this->diffie_hellman, + &secret) != SUCCESS) + { + SIG(IKE_UP_FAILED, "peers DH group selection invalid"); + return FAILED; + } + + /* Apply SPI if we are rekeying */ + if (this->old_sa) + { + ike_sa_id_t *id; + prf_t *prf, *child_prf; + + id = this->ike_sa->get_id(this->ike_sa); + id->set_responder_spi(id, this->proposal->get_spi(this->proposal)); + + /* setup crypto keys for the rekeyed SA */ + prf = this->old_sa->get_prf(this->old_sa); + child_prf = this->old_sa->get_child_prf(this->old_sa); + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->my_nonce, this->other_nonce, + TRUE, child_prf, prf); + } + else + { + /* setup crypto keys for a new SA */ + status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret, + this->my_nonce, this->other_nonce, + TRUE, NULL, NULL); + } + if (status != SUCCESS) + { + SIG(IKE_UP_FAILED, "key derivation failed"); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_init_t *this) +{ + return IKE_INIT; +} + +/** + * Implementation of task_t.get_type + */ +static chunk_t get_lower_nonce(private_ike_init_t *this) +{ + if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr, + min(this->my_nonce.len, this->other_nonce.len)) < 0) + { + return this->my_nonce; + } + else + { + return this->other_nonce; + } +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) +{ + DESTROY_IF(this->proposal); + DESTROY_IF(this->diffie_hellman); + chunk_free(&this->other_nonce); + + this->ike_sa = ike_sa; + this->proposal = NULL; + this->diffie_hellman = diffie_hellman_create(this->dh_group); +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_init_t *this) +{ + DESTROY_IF(this->proposal); + DESTROY_IF(this->diffie_hellman); + chunk_free(&this->my_nonce); + chunk_free(&this->other_nonce); + chunk_free(&this->cookie); + free(this); +} + +/* + * Described in header. + */ +ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) +{ + private_ike_init_t *this = malloc_thing(private_ike_init_t); + + this->public.get_lower_nonce = (chunk_t(*)(ike_init_t*))get_lower_nonce; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->dh_group = MODP_NONE; + this->diffie_hellman = NULL; + this->my_nonce = chunk_empty; + this->other_nonce = chunk_empty; + this->cookie = chunk_empty; + this->proposal = NULL; + this->connection = NULL; + this->old_sa = old_sa; + this->retry = 0; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h new file mode 100644 index 000000000..f60c096e8 --- /dev/null +++ b/src/charon/sa/tasks/ike_init.h @@ -0,0 +1,68 @@ +/** + * @file ike_init.h + * + * @brief Interface ike_init_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_INIT_H_ +#define IKE_INIT_H_ + +typedef struct ike_init_t ike_init_t; + +#include +#include +#include + +/** + * @brief Task of type IKE_INIT, creates an IKE_SA without authentication. + * + * The authentication of is handle in the ike_auth task. + * + * @b Constructors: + * - ike_init_create() + * + * @ingroup tasks + */ +struct ike_init_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Get the lower of the two nonces, used for rekey collisions. + * + * @param this calling object + * @return lower nonce + */ + chunk_t (*get_lower_nonce) (ike_init_t *this); +}; + +/** + * @brief Create a new IKE_INIT task. + * + * @param ike_sa IKE_SA this task works for (new one when rekeying) + * @param initiator TRUE if thask is the original initator + * @param old_sa old IKE_SA when we are rekeying + * @return ike_init task to handle by the task_manager + */ +ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa); + +#endif /* IKE_INIT_H_ */ diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c new file mode 100644 index 000000000..50b5d652b --- /dev/null +++ b/src/charon/sa/tasks/ike_natd.c @@ -0,0 +1,371 @@ +/** + * @file ike_natd.c + * + * @brief Implementation of the ike_natd task. + * + */ + +/* + * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_natd.h" + +#include + +#include +#include +#include + + +typedef struct private_ike_natd_t private_ike_natd_t; + +/** + * Private members of a ike_natd_t task. + */ +struct private_ike_natd_t { + + /** + * Public methods and task_t interface. + */ + ike_natd_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Hasher used to build NAT detection hashes + */ + hasher_t *hasher; + + /** + * Did we process any NAT detection notifys for a source address? + */ + bool src_seen; + + /** + * Did we process any NAT detection notifys for a destination address? + */ + bool dst_seen; + + /** + * Have we found a matching source address NAT hash? + */ + bool src_matched; + + /** + * Have we found a matching destination address NAT hash? + */ + bool dst_matched; +}; + + +/** + * Build NAT detection hash for a host + */ +static chunk_t generate_natd_hash(private_ike_natd_t *this, + ike_sa_id_t *ike_sa_id, host_t *host) +{ + chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk; + chunk_t natd_hash; + u_int64_t spi_i, spi_r; + u_int16_t port; + + /* prepare all requred chunks */ + spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); + spi_r = ike_sa_id->get_responder_spi(ike_sa_id); + spi_i_chunk.ptr = (void*)&spi_i; + spi_i_chunk.len = sizeof(spi_i); + spi_r_chunk.ptr = (void*)&spi_r; + spi_r_chunk.len = sizeof(spi_r); + port = htons(host->get_port(host)); + port_chunk.ptr = (void*)&port; + port_chunk.len = sizeof(port); + addr_chunk = host->get_address(host); + + /* natd_hash = SHA1( spi_i | spi_r | address | port ) */ + natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk); + this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash); + DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); + DBG3(DBG_IKE, "natd_hash %B", &natd_hash); + + chunk_free(&natd_chunk); + return natd_hash; +} + +/** + * Build a NAT detection notify payload. + */ +static notify_payload_t *build_natd_payload(private_ike_natd_t *this, + notify_type_t type, host_t *host) +{ + chunk_t hash; + notify_payload_t *notify; + ike_sa_id_t *ike_sa_id; + + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + notify = notify_payload_create(); + notify->set_notify_type(notify, type); + hash = generate_natd_hash(this, ike_sa_id, host); + notify->set_notification_data(notify, hash); + chunk_free(&hash); + + return notify; +} + +/** + * read notifys from message and evaluate them + */ +static void process_payloads(private_ike_natd_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + notify_payload_t *notify; + chunk_t hash, src_hash, dst_hash; + ike_sa_id_t *ike_sa_id; + host_t *me, *other; + + /* Precompute NAT-D hashes for incoming NAT notify comparison */ + ike_sa_id = message->get_ike_sa_id(message); + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + dst_hash = generate_natd_hash(this, ike_sa_id, me); + src_hash = generate_natd_hash(this, ike_sa_id, other); + + DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); + DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) != NOTIFY) + { + continue; + } + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case NAT_DETECTION_DESTINATION_IP: + { + this->dst_seen = TRUE; + if (!this->dst_matched) + { + hash = notify->get_notification_data(notify); + DBG3(DBG_IKE, "received dst_hash %B", &hash); + if (chunk_equals(hash, dst_hash)) + { + this->dst_matched = TRUE; + } + } + break; + } + case NAT_DETECTION_SOURCE_IP: + { + this->src_seen = TRUE; + if (!this->src_matched) + { + hash = notify->get_notification_data(notify); + DBG3(DBG_IKE, "received src_hash %B", &hash); + if (chunk_equals(hash, src_hash)) + { + this->src_matched = TRUE; + } + } + break; + } + default: + break; + } + } + iterator->destroy(iterator); + + chunk_free(&src_hash); + chunk_free(&dst_hash); + + if (this->src_seen && this->dst_seen) + { + if (!this->dst_matched) + { + this->ike_sa->enable_natt(this->ike_sa, TRUE); + } + if (!this->src_matched) + { + this->ike_sa->enable_natt(this->ike_sa, FALSE); + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_natd_t *this, message_t *message) +{ + process_payloads(this, message); + + if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + host_t *me, *other; + + me = this->ike_sa->get_my_host(this->ike_sa); + me->set_port(me, IKEV2_NATT_PORT); + other = this->ike_sa->get_other_host(this->ike_sa); + other->set_port(other, IKEV2_NATT_PORT); + } + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_natd_t *this, message_t *message) +{ + notify_payload_t *notify; + linked_list_t *list; + host_t *host; + + /* include one notify if our address is defined, all addresses otherwise */ + host = this->ike_sa->get_my_host(this->ike_sa); + if (host->is_anyaddr(host)) + { + /* TODO: we could get the src address from netlink!? */ + list = charon->kernel_interface->create_address_list(charon->kernel_interface); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + host->destroy(host); + message->add_payload(message, (payload_t*)notify); + } + list->destroy(list); + } + else + { + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); + message->add_payload(message, (payload_t*)notify); + } + + host = this->ike_sa->get_other_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); + message->add_payload(message, (payload_t*)notify); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_natd_t *this, message_t *message) +{ + notify_payload_t *notify; + host_t *me, *other; + + /* only add notifies on successfull responses. */ + if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL) + { + return SUCCESS; + } + + if (this->src_seen && this->dst_seen) + { + /* initiator seems to support NAT detection, add response */ + me = this->ike_sa->get_my_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); + message->add_payload(message, (payload_t*)notify); + + other = this->ike_sa->get_other_host(this->ike_sa); + notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other); + message->add_payload(message, (payload_t*)notify); + } + return SUCCESS; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_natd_t *this, message_t *message) +{ + process_payloads(this, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_natd_t *this) +{ + return IKE_NATD; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_natd_t *this) +{ + this->hasher->destroy(this->hasher); + free(this); +} + +/* + * Described in header. + */ +ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_natd_t *this = malloc_thing(private_ike_natd_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->hasher = hasher_create(HASH_SHA1); + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h new file mode 100644 index 000000000..8d0cb58b4 --- /dev/null +++ b/src/charon/sa/tasks/ike_natd.h @@ -0,0 +1,57 @@ +/** + * @file ike_natd.h + * + * @brief Interface ike_natd_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_NATD_H_ +#define IKE_NATD_H_ + +typedef struct ike_natd_t ike_natd_t; + +#include +#include +#include + +/** + * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange. + * + * @b Constructors: + * - ike_natd_create() + * + * @ingroup tasks + */ +struct ike_natd_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new ike_natd task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_natd task to handle by the task_manager + */ +ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_NATD_H_ */ diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c new file mode 100644 index 000000000..a33e7ee34 --- /dev/null +++ b/src/charon/sa/tasks/ike_rekey.c @@ -0,0 +1,329 @@ +/** + * @file ike_rekey.c + * + * @brief Implementation of the ike_rekey task. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "ike_rekey.h" + +#include +#include +#include +#include +#include + + +typedef struct private_ike_rekey_t private_ike_rekey_t; + +/** + * Private members of a ike_rekey_t task. + */ +struct private_ike_rekey_t { + + /** + * Public methods and task_t interface. + */ + ike_rekey_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * New IKE_SA which replaces the current one + */ + ike_sa_t *new_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * the IKE_INIT task which is reused to simplify rekeying + */ + ike_init_t *ike_init; + + /** + * colliding task detected by the task manager + */ + task_t *collision; +}; + +/** + * Implementation of task_t.build for initiator + */ +static status_t build_i(private_ike_rekey_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + + this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + TRUE); + + connection = this->ike_sa->get_connection(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + this->new_sa->set_connection(this->new_sa, connection); + this->new_sa->set_policy(this->new_sa, policy); + + this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa); + this->ike_init->task.build(&this->ike_init->task, message); + + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_r(private_ike_rekey_t *this, message_t *message) +{ + connection_t *connection; + policy_t *policy; + iterator_t *iterator; + child_sa_t *child_sa; + + if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) + { + DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting"); + return NEED_MORE; + } + + iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_CREATED: + case CHILD_REKEYING: + case CHILD_DELETING: + /* we do not allow rekeying while we have children in-progress */ + DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open"); + iterator->destroy(iterator); + return NEED_MORE; + default: + break; + } + } + iterator->destroy(iterator); + + this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + FALSE); + + connection = this->ike_sa->get_connection(this->ike_sa); + policy = this->ike_sa->get_policy(this->ike_sa); + this->new_sa->set_connection(this->new_sa, connection); + this->new_sa->set_policy(this->new_sa, policy); + + this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa); + this->ike_init->task.process(&this->ike_init->task, message); + + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_rekey_t *this, message_t *message) +{ + if (this->new_sa == NULL) + { + /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */ + message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + return SUCCESS; + } + + if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) + { + return SUCCESS; + } + + this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); + + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_rekey_t *this, message_t *message) +{ + job_t *job; + ike_sa_id_t *to_delete; + + if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED) + { + /* rekeying failed, fallback to old SA */ + if (!(this->collision && + this->collision->get_type(this->collision) == IKE_DELETE)) + { + job_t *job; + u_int32_t retry = charon->configuration->get_retry_interval( + charon->configuration); + job = (job_t*)rekey_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), FALSE); + DBG1(DBG_IKE, "IKE_SA rekeying failed, " + "trying again in %d seconds", retry); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->event_queue->add_relative(charon->event_queue, job, retry * 1000); + } + return SUCCESS; + } + + this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED); + to_delete = this->ike_sa->get_id(this->ike_sa); + + /* check for collisions */ + if (this->collision && + this->collision->get_type(this->collision) == IKE_REKEY) + { + chunk_t this_nonce, other_nonce; + host_t *host; + private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision; + + this_nonce = this->ike_init->get_lower_nonce(this->ike_init); + other_nonce = other->ike_init->get_lower_nonce(other->ike_init); + + /* if we have the lower nonce, delete rekeyed SA. If not, delete + * the redundant. */ + if (memcmp(this_nonce.ptr, other_nonce.ptr, + min(this_nonce.len, other_nonce.len)) < 0) + { + DBG1(DBG_IKE, "IKE_SA rekey collision won, deleting rekeyed IKE_SA"); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa); + } + else + { + DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant IKE_SA"); + /* apply host for a proper delete */ + host = this->ike_sa->get_my_host(this->ike_sa); + this->new_sa->set_my_host(this->new_sa, host->clone(host)); + host = this->ike_sa->get_other_host(this->ike_sa); + this->new_sa->set_other_host(this->new_sa, host->clone(host)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + to_delete = this->new_sa->get_id(this->new_sa); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); + /* inherit to other->new_sa in destroy() */ + this->new_sa = other->new_sa; + other->new_sa = NULL; + } + } + + job = (job_t*)delete_ike_sa_job_create(to_delete, TRUE); + charon->job_queue->add(charon->job_queue, job); + + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_rekey_t *this) +{ + return IKE_REKEY; +} + +static void collide(private_ike_rekey_t* this, task_t *other) +{ + DESTROY_IF(this->collision); + this->collision = other; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa) +{ + if (this->ike_init) + { + this->ike_init->task.destroy(&this->ike_init->task); + } + if (this->new_sa) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + this->new_sa); + } + DESTROY_IF(this->collision); + + this->collision = NULL; + this->ike_sa = ike_sa; + this->new_sa = NULL; + this->ike_init = NULL; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_rekey_t *this) +{ + if (this->new_sa) + { + if (this->new_sa->get_state(this->new_sa) == IKE_ESTABLISHED && + this->new_sa->inherit(this->new_sa, this->ike_sa) != DESTROY_ME) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa); + } + else + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + this->new_sa); + } + } + if (this->ike_init) + { + this->ike_init->task.destroy(&this->ike_init->task); + } + DESTROY_IF(this->collision); + free(this); +} + +/* + * Described in header. + */ +ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t); + + this->public.collide = (void(*)(ike_rekey_t*,task_t*))collide; + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->new_sa = NULL; + this->ike_init = NULL; + this->initiator = initiator; + this->collision = NULL; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h new file mode 100644 index 000000000..125422efd --- /dev/null +++ b/src/charon/sa/tasks/ike_rekey.h @@ -0,0 +1,69 @@ +/** + * @file ike_rekey.h + * + * @brief Interface ike_rekey_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef IKE_REKEY_H_ +#define IKE_REKEY_H_ + +typedef struct ike_rekey_t ike_rekey_t; + +#include +#include +#include + +/** + * @brief Task of type IKE_REKEY, rekey an established IKE_SA. + * + * @b Constructors: + * - ike_rekey_create() + * + * @ingroup tasks + */ +struct ike_rekey_t { + + /** + * Implements the task_t interface + */ + task_t task; + + /** + * @brief Register a rekeying task which collides with this one. + * + * If two peers initiate rekeying at the same time, the collision must + * be handled gracefully. The task manager is aware of what exchanges + * are going on and notifies the outgoing task by passing the incoming. + * + * @param this task initated by us + * @param other incoming task + */ + void (*collide)(ike_rekey_t* this, task_t *other); +}; + +/** + * @brief Create a new IKE_REKEY task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE for initiator, FALSE for responder + * @return IKE_REKEY task to handle by the task_manager + */ +ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_REKEY_H_ */ diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c new file mode 100644 index 000000000..68d8ebf0c --- /dev/null +++ b/src/charon/sa/tasks/task.c @@ -0,0 +1,38 @@ +/** + * @file task.c + * + * @brief Enum values for task types + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "task.h" + +ENUM(task_type_names, IKE_INIT, CHILD_REKEY, + "IKE_INIT", + "IKE_NATD", + "IKE_AUTHENTICATE", + "IKE_CERT", + "IKE_CONFIG", + "IKE_DPD", + "IKE_REKEY", + "IKE_DELETE", + "IKE_DEADPEER", + "CHILD_CREATE", + "CHILD_DELETE", + "CHILD_REKEY", +); diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h new file mode 100644 index 000000000..128d7db4a --- /dev/null +++ b/src/charon/sa/tasks/task.h @@ -0,0 +1,151 @@ +/** + * @file task.h + * + * @brief Interface task_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef TASK_H_ +#define TASK_H_ + +typedef enum task_type_t task_type_t; +typedef struct task_t task_t; + +#include +#include +#include + +/** + * @brief Different kinds of tasks. + * + * @ingroup tasks + */ +enum task_type_t { + /** establish an unauthenticated IKE_SA */ + IKE_INIT, + /** detect NAT situation */ + IKE_NATD, + /** authenticate the initiated IKE_SA */ + IKE_AUTHENTICATE, + /** exchange certificates and requests */ + IKE_CERT, + /** Configuration payloads, virtual IP and such */ + IKE_CONFIG, + /** DPD detection */ + IKE_DEADPEER, + /** rekey an IKE_SA */ + IKE_REKEY, + /** delete an IKE_SA */ + IKE_DELETE, + /** liveness check */ + IKE_DPD, + /** establish a CHILD_SA within an IKE_SA */ + CHILD_CREATE, + /** delete an established CHILD_SA */ + CHILD_DELETE, + /** rekey an CHILD_SA */ + CHILD_REKEY, +}; + +/** + * enum names for task_type_t. + */ +extern enum_name_t *task_type_names; + +/** + * @brief Interface for a task, an operation handled within exchanges. + * + * A task is an elemantary operation. It may be handled by a single or by + * multiple exchanges. An exchange may even complete multiple tasks. + * A task has a build() and an process() operation. The build() operation + * creates payloads and adds it to the message. The process() operation + * inspects a message and handles its payloads. An initiator of an exchange + * first calls build() to build the request, and processes the response message + * with the process() method. + * A responder does the opposite; it calls process() first to handle an incoming + * request and secondly calls build() to build an appropriate response. + * Both methods return either SUCCESS, NEED_MORE or FAILED. A SUCCESS indicates + * that the task completed, even when the task completed unsuccesfully. The + * manager then removes the task from the list. A NEED_MORE is returned when + * the task needs further build()/process() calls to complete, the manager + * leaves the taks in the queue. A returned FAILED indicates a critical failure. + * The manager closes the IKE_SA whenever a task returns FAILED. + * + * @b Constructors: + * - None, use implementations specific constructors + * + * @ingroup tasks + */ +struct task_t { + + /** + * @brief Build a request or response message for this task. + * + * @param this calling object + * @param message message to add payloads to + * @return + * - FAILED if a critical error occured + * - NEED_MORE if another call to build/process needed + * - SUCCESS if task completed + */ + status_t (*build) (task_t *this, message_t *message); + + /** + * @brief Process a request or response message for this task. + * + * @param this calling object + * @param message message to read payloads from + * @return + * - FAILED if a critical error occured + * - NEED_MORE if another call to build/process needed + * - SUCCESS if task completed + */ + status_t (*process) (task_t *this, message_t *message); + + /** + * @brief Get the type of the task implementation. + * + * @param this calling object + */ + task_type_t (*get_type) (task_t *this); + + /** + * @brief Migrate a task to a new IKE_SA. + * + * After migrating a task, it goes back to a state where it can be + * used again to initate an exchange. This is useful when a task + * has to get migrated to a new IKE_SA. + * A special usage is when a INVALID_KE_PAYLOAD is received. A call + * to reset resets the task, but uses another DH group for the next + * try. + * The ike_sa is the new IKE_SA this task belongs to and operates on. + * + * @param this calling object + * @param ike_sa new IKE_SA this task works for + */ + void (*migrate) (task_t *this, ike_sa_t *ike_sa); + + /** + * @brief Destroys a task_t object. + * + * @param this calling object + */ + void (*destroy) (task_t *this); +}; + +#endif /* TASK_H_ */ diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c new file mode 100644 index 000000000..4a70d2ecf --- /dev/null +++ b/src/charon/threads/kernel_interface.c @@ -0,0 +1,1964 @@ +/** + * @file kernel_interface.c + * + * @brief Implementation of kernel_interface_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2003 Herbert Xu. + * + * Based on xfrm code from pluto. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_interface.h" + +#include +#include +#include +#include +#include + +/** kernel level protocol identifiers */ +#define KERNEL_ESP 50 +#define KERNEL_AH 51 + +/** default priority of installed policies */ +#define PRIO_LOW 3000 +#define PRIO_HIGH 2000 + +#define BUFFER_SIZE 1024 + +/** + * returns a pointer to the first rtattr following the nlmsghdr *nlh and the + * 'usual' netlink data x like 'struct xfrm_usersa_info' + */ +#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x)))) +/** + * returns a pointer to the next rtattr following rta. + * !!! do not use this to parse messages. use RTA_NEXT and RTA_OK instead !!! + */ +#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +/** + * returns the total size of attached rta data + * (after 'usual' netlink data x like 'struct xfrm_usersa_info') + */ +#define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x)) + +typedef struct kernel_algorithm_t kernel_algorithm_t; + +/** + * Mapping from the algorithms defined in IKEv2 to + * kernel level algorithm names and their key length + */ +struct kernel_algorithm_t { + /** + * Identifier specified in IKEv2 + */ + int ikev2_id; + + /** + * Name of the algorithm, as used as kernel identifier + */ + char *name; + + /** + * Key length in bits, if fixed size + */ + u_int key_size; +}; +#define END_OF_LIST -1 + +/** + * Algorithms for encryption + */ +kernel_algorithm_t encryption_algs[] = { +/* {ENCR_DES_IV64, "***", 0}, */ + {ENCR_DES, "des", 64}, + {ENCR_3DES, "des3_ede", 192}, +/* {ENCR_RC5, "***", 0}, */ +/* {ENCR_IDEA, "***", 0}, */ + {ENCR_CAST, "cast128", 0}, + {ENCR_BLOWFISH, "blowfish", 0}, +/* {ENCR_3IDEA, "***", 0}, */ +/* {ENCR_DES_IV32, "***", 0}, */ + {ENCR_NULL, "cipher_null", 0}, + {ENCR_AES_CBC, "aes", 0}, +/* {ENCR_AES_CTR, "***", 0}, */ + {END_OF_LIST, NULL, 0}, +}; + +/** + * Algorithms for integrity protection + */ +kernel_algorithm_t integrity_algs[] = { + {AUTH_HMAC_MD5_96, "md5", 128}, + {AUTH_HMAC_SHA1_96, "sha1", 160}, + {AUTH_HMAC_SHA2_256_128, "sha256", 256}, + {AUTH_HMAC_SHA2_384_192, "sha384", 384}, + {AUTH_HMAC_SHA2_512_256, "sha512", 512}, +/* {AUTH_DES_MAC, "***", 0}, */ +/* {AUTH_KPDK_MD5, "***", 0}, */ +/* {AUTH_AES_XCBC_96, "***", 0}, */ + {END_OF_LIST, NULL, 0}, +}; + +/** + * Look up a kernel algorithm name and its key size + */ +char* lookup_algorithm(kernel_algorithm_t *kernel_algo, + algorithm_t *ikev2_algo, u_int *key_size) +{ + while (kernel_algo->ikev2_id != END_OF_LIST) + { + if (ikev2_algo->algorithm == kernel_algo->ikev2_id) + { + /* match, evaluate key length */ + if (ikev2_algo->key_size) + { /* variable length */ + *key_size = ikev2_algo->key_size; + } + else + { /* fixed length */ + *key_size = kernel_algo->key_size; + } + return kernel_algo->name; + } + kernel_algo++; + } + return NULL; +} + +typedef struct route_entry_t route_entry_t; + +/** + * installed routing entry + */ +struct route_entry_t { + + /** Index of the interface the route is bound to */ + int if_index; + + /** Source ip of the route */ + host_t *src_ip; + + /** Destination net */ + chunk_t dst_net; + + /** Destination net prefixlen */ + u_int8_t prefixlen; +}; + +/** + * destroy an route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + this->src_ip->destroy(this->src_ip); + chunk_free(&this->dst_net); + free(this); +} + +typedef struct policy_entry_t policy_entry_t; + +/** + * installed kernel policy. + */ +struct policy_entry_t { + + /** direction of this policy: in, out, forward */ + u_int8_t direction; + + /** reqid of the policy */ + u_int32_t reqid; + + /** parameters of installed policy */ + struct xfrm_selector sel; + + /** associated route installed for this policy */ + route_entry_t *route; + + /** by how many CHILD_SA's this policy is used */ + u_int refcount; +}; + +typedef struct vip_entry_t vip_entry_t; + +/** + * Installed virtual ip + */ +struct vip_entry_t { + /** Index of the interface the ip is bound to */ + u_int8_t if_index; + + /** The ip address */ + host_t *ip; + + /** Number of times this IP is used */ + u_int refcount; +}; + +/** + * destroy a vip_entry_t object + */ +static void vip_entry_destroy(vip_entry_t *this) +{ + this->ip->destroy(this->ip); + free(this); +} + +typedef struct address_entry_t address_entry_t; + +/** + * an address found on the system, containg address and interface info + */ +struct address_entry_t { + + /** address of this entry */ + host_t *host; + + /** interface index */ + int ifindex; + + /** name of the index */ + char ifname[IFNAMSIZ]; +}; + +/** + * destroy an address entry + */ +static void address_entry_destroy(address_entry_t *this) +{ + this->host->destroy(this->host); + free(this); +} + +typedef struct private_kernel_interface_t private_kernel_interface_t; + +/** + * Private variables and functions of kernel_interface class. + */ +struct private_kernel_interface_t { + /** + * Public part of the kernel_interface_t object. + */ + kernel_interface_t public; + + /** + * List of installed policies (kernel_entry_t) + */ + linked_list_t *policies; + + /** + * Mutex locks access to policies + */ + pthread_mutex_t policies_mutex; + + /** + * List of installed virtual IPs. (vip_entry_t) + */ + linked_list_t *vips; + + /** + * Mutex to lock access to vips. + */ + pthread_mutex_t vips_mutex; + + /** + * netlink xfrm socket to receive acquire and expire events + */ + int socket_xfrm_events; + + /** + * Netlink xfrm socket (IPsec) + */ + int socket_xfrm; + + /** + * Netlink rt socket (routing) + */ + int socket_rt; + + /** + * Thread receiving events from kernel + */ + pthread_t event_thread; +}; + +/** + * convert a host_t to a struct xfrm_address + */ +static void host2xfrm(host_t *host, xfrm_address_t *xfrm) +{ + chunk_t chunk = host->get_address(host); + memcpy(xfrm, chunk.ptr, min(chunk.len, sizeof(xfrm_address_t))); +} + +/** + * convert a traffic selector address range to subnet and its mask. + */ +static void ts2subnet(traffic_selector_t* ts, + xfrm_address_t *net, u_int8_t *mask) +{ + /* there is no way to do this cleanly, as the address range may + * be anything else but a subnet. We use from_addr as subnet + * and try to calculate a usable subnet mask. + */ + int byte, bit; + bool found = FALSE; + chunk_t from, to; + size_t size = (ts->get_type(ts) == TS_IPV4_ADDR_RANGE) ? 4 : 16; + + from = ts->get_from_address(ts); + to = ts->get_to_address(ts); + + *mask = (size * 8); + /* go trough all bits of the addresses, beginning in the front. + * as long as they are equal, the subnet gets larger + */ + for (byte = 0; byte < size; byte++) + { + for (bit = 7; bit >= 0; bit--) + { + if ((1<get_from_port(ts); + to = ts->get_to_port(ts); + + if (from == to) + { + *port = htons(from); + *mask = ~0; + } + else + { + *port = 0; + *mask = 0; + } +} + +/** + * convert a pair of traffic_selectors to a xfrm_selector + */ +static struct xfrm_selector ts2selector(traffic_selector_t *src, + traffic_selector_t *dst) +{ + struct xfrm_selector sel; + + memset(&sel, 0, sizeof(sel)); + sel.family = src->get_type(src) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + /* src or dest proto may be "any" (0), use more restrictive one */ + sel.proto = max(src->get_protocol(src), dst->get_protocol(dst)); + ts2subnet(dst, &sel.daddr, &sel.prefixlen_d); + ts2subnet(src, &sel.saddr, &sel.prefixlen_s); + ts2ports(dst, &sel.dport, &sel.dport_mask); + ts2ports(src, &sel.sport, &sel.sport_mask); + sel.ifindex = 0; + sel.user = 0; + + return sel; +} + +/** + * Creates an rtattr and adds it to the netlink message + */ +static void add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, + size_t buflen) +{ + struct rtattr *rta; + + if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data.len) > buflen) + { + DBG1(DBG_KNL, "unable to add attribute, buffer too small"); + return; + } + + rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len)); + rta->rta_type = rta_type; + rta->rta_len = RTA_LENGTH(data.len); + memcpy(RTA_DATA(rta), data.ptr, data.len); + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; +} + +/** + * Receives events from kernel + */ +static void receive_events(private_kernel_interface_t *this) +{ + while(TRUE) + { + unsigned char response[512]; + struct nlmsghdr *hdr; + struct sockaddr_nl addr; + socklen_t addr_len = sizeof(addr); + int len; + + hdr = (struct nlmsghdr*)response; + len = recvfrom(this->socket_xfrm_events, response, sizeof(response), + 0, (struct sockaddr*)&addr, &addr_len); + if (len < 0) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + charon->kill(charon, "unable to receive netlink events"); + } + + if (!NLMSG_OK(hdr, len)) + { + /* bad netlink message */ + continue; + } + + if (addr.nl_pid != 0) + { + /* not from kernel. not interested, try another one */ + continue; + } + + /* we handle ACQUIRE and EXPIRE messages directly */ + if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) + { + u_int32_t reqid = 0; + job_t *job; + struct rtattr *rtattr = XFRM_RTA(hdr, struct xfrm_user_acquire); + size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_user_tmpl); + if (RTA_OK(rtattr, rtsize)) + { + if (rtattr->rta_type == XFRMA_TMPL) + { + struct xfrm_user_tmpl* tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rtattr); + reqid = tmpl->reqid; + } + } + if (reqid == 0) + { + DBG1(DBG_KNL, "received a XFRM_MSG_ACQUIRE, but no reqid found"); + } + else + { + DBG2(DBG_KNL, "received a XFRM_MSG_ACQUIRE"); + DBG1(DBG_KNL, "creating acquire job for CHILD_SA with reqid %d", + reqid); + job = (job_t*)acquire_job_create(reqid); + charon->job_queue->add(charon->job_queue, job); + } + } + else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) + { + job_t *job; + protocol_id_t protocol; + u_int32_t spi, reqid; + struct xfrm_user_expire *expire; + + expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); + protocol = expire->state.id.proto == KERNEL_ESP ? + PROTO_ESP : PROTO_AH; + spi = expire->state.id.spi; + reqid = expire->state.reqid; + + DBG2(DBG_KNL, "received a XFRM_MSG_EXPIRE"); + DBG1(DBG_KNL, "creating %s job for %N CHILD_SA 0x%x (reqid %d)", + expire->hard ? "delete" : "rekey", protocol_id_names, + protocol, ntohl(spi), reqid); + if (expire->hard) + { + job = (job_t*)delete_child_sa_job_create(reqid, protocol, spi); + } + else + { + job = (job_t*)rekey_child_sa_job_create(reqid, protocol, spi); + } + charon->job_queue->add(charon->job_queue, job); + } + } +} + +/** + * send a netlink message and wait for a reply + */ +static status_t netlink_send(int socket, struct nlmsghdr *in, + struct nlmsghdr **out, size_t *out_len) +{ + int len, addr_len; + struct sockaddr_nl addr; + chunk_t result = chunk_empty, tmp; + struct nlmsghdr *msg, peek; + + static int seq = 200; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + + pthread_mutex_lock(&mutex); + + in->nlmsg_seq = ++seq; + in->nlmsg_pid = getpid(); + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + while (TRUE) + { + len = sendto(socket, in, in->nlmsg_len, 0, + (struct sockaddr*)&addr, sizeof(addr)); + + if (len != in->nlmsg_len) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + pthread_mutex_unlock(&mutex); + DBG1(DBG_KNL, "error sending to netlink socket: %m"); + return FAILED; + } + break; + } + + while (TRUE) + { + char buf[1024]; + tmp.len = sizeof(buf); + tmp.ptr = buf; + msg = (struct nlmsghdr*)tmp.ptr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0; + addr_len = sizeof(addr); + + len = recvfrom(socket, tmp.ptr, tmp.len, 0, + (struct sockaddr*)&addr, &addr_len); + + if (len < 0) + { + if (errno == EINTR) + { + DBG1(DBG_IKE, "got interrupted"); + /* interrupted, try again */ + continue; + } + DBG1(DBG_IKE, "error reading from netlink socket: %m"); + pthread_mutex_unlock(&mutex); + return FAILED; + } + if (!NLMSG_OK(msg, len)) + { + DBG1(DBG_IKE, "received corrupted netlink message"); + pthread_mutex_unlock(&mutex); + return FAILED; + } + if (msg->nlmsg_seq != seq) + { + DBG1(DBG_IKE, "received invalid netlink sequence number"); + if (msg->nlmsg_seq < seq) + { + continue; + } + pthread_mutex_unlock(&mutex); + return FAILED; + } + + tmp.len = len; + result = chunk_cata("cc", result, tmp); + + /* NLM_F_MULTI flag does not seem to be set correctly, we use sequence + * numbers to detect multi header messages */ + len = recvfrom(socket, &peek, sizeof(peek), MSG_PEEK | MSG_DONTWAIT, + (struct sockaddr*)&addr, &addr_len); + + if (len == sizeof(peek) && peek.nlmsg_seq == seq) + { + /* seems to be multipart */ + continue; + } + break; + } + + *out_len = result.len; + *out = (struct nlmsghdr*)clalloc(result.ptr, result.len); + + pthread_mutex_unlock(&mutex); + + return SUCCESS; +} + +/** + * send a netlink message and wait for its acknowlegde + */ +static status_t netlink_send_ack(int socket, struct nlmsghdr *in) +{ + struct nlmsghdr *out, *hdr; + size_t len; + + if (netlink_send(socket, in, &out, &len) != SUCCESS) + { + return FAILED; + } + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case NLMSG_ERROR: + { + struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(hdr); + + if (err->error) + { + DBG1(DBG_KNL, "received netlink error: %s (%d)", + strerror(-err->error), -err->error); + free(out); + return FAILED; + } + free(out); + return SUCCESS; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + DBG1(DBG_KNL, "netlink request not acknowlegded"); + free(out); + return FAILED; +} + +/** + * Create a list of local addresses. + */ +static linked_list_t *create_address_list(private_kernel_interface_t *this) +{ + char request[BUFFER_SIZE]; + struct nlmsghdr *out, *hdr; + struct rtgenmsg *msg; + size_t len; + linked_list_t *list; + + DBG2(DBG_IKE, "getting local address list"); + + list = linked_list_create(); + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)&request; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + hdr->nlmsg_type = RTM_GETADDR; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT; + msg = (struct rtgenmsg*)NLMSG_DATA(hdr); + msg->rtgen_family = AF_UNSPEC; + + if (netlink_send(this->socket_rt, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case RTM_NEWADDR: + { + struct ifaddrmsg* msg = (struct ifaddrmsg*)(NLMSG_DATA(hdr)); + struct rtattr *rta = IFA_RTA(msg); + size_t rtasize = IFA_PAYLOAD (hdr); + host_t *host = NULL; + char *name = NULL; + chunk_t local = chunk_empty, address = chunk_empty; + + while(RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFA_LOCAL: + local.ptr = RTA_DATA(rta); + local.len = RTA_PAYLOAD(rta); + break; + case IFA_ADDRESS: + address.ptr = RTA_DATA(rta); + address.len = RTA_PAYLOAD(rta); + break; + case IFA_LABEL: + name = RTA_DATA(rta); + break; + } + rta = RTA_NEXT(rta, rtasize); + } + + /* For PPP interfaces, we need the IFA_LOCAL address, + * IFA_ADDRESS is the peers address. But IFA_LOCAL is + * not included in all cases, so fallback to IFA_ADDRESS. */ + if (local.ptr) + { + host = host_create_from_chunk(msg->ifa_family, local, 0); + } + else if (address.ptr) + { + host = host_create_from_chunk(msg->ifa_family, address, 0); + } + + if (host) + { + address_entry_t *entry; + + entry = malloc_thing(address_entry_t); + entry->host = host; + entry->ifindex = msg->ifa_index; + if (name) + { + memcpy(entry->ifname, name, IFNAMSIZ); + } + else + { + strcpy(entry->ifname, "(unknown)"); + } + list->insert_last(list, entry); + } + hdr = NLMSG_NEXT(hdr, len); + continue; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + free(out); + } + else + { + DBG1(DBG_IKE, "unable to get local address list"); + } + + return list; +} + +/** + * Implements kernel_interface_t.create_address_list. + */ +static linked_list_t *create_address_list_public(private_kernel_interface_t *this) +{ + linked_list_t *result, *list; + address_entry_t *entry; + + result = linked_list_create(); + list = create_address_list(this); + while (list->remove_last(list, (void**)&entry) == SUCCESS) + { + result->insert_last(result, entry->host); + free(entry); + } + list->destroy(list); + + return result; +} + +/** + * implementation of kernel_interface_t.get_interface_name + */ +static char *get_interface_name(private_kernel_interface_t *this, host_t* ip) +{ + linked_list_t *list; + address_entry_t *entry; + char *name = NULL; + + DBG2(DBG_IKE, "getting interface name for %H", ip); + + list = create_address_list(this); + while (!name && list->remove_last(list, (void**)&entry) == SUCCESS) + { + if (ip->ip_equals(ip, entry->host)) + { + name = strdup(entry->ifname); + } + address_entry_destroy(entry); + } + list->destroy_function(list, (void*)address_entry_destroy); + + if (name) + { + DBG2(DBG_IKE, "%H is on interface %s", ip, name); + } + else + { + DBG2(DBG_IKE, "%H is not a local address", ip); + } + return name; +} + +/** + * Tries to find an ip address of a local interface that is included in the + * supplied traffic selector. + */ +static status_t get_address_by_ts(private_kernel_interface_t *this, + traffic_selector_t *ts, host_t **ip) +{ + address_entry_t *entry; + host_t *host; + int family; + linked_list_t *list; + bool found = FALSE; + + DBG2(DBG_IKE, "getting a local address in traffic selector %R", ts); + + /* if we have a family which includes localhost, we do not + * search for an IP, we use the default */ + family = ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + + if (family == AF_INET) + { + host = host_create_from_string("127.0.0.1", 0); + } + else + { + host = host_create_from_string("::1", 0); + } + + if (ts->includes(ts, host)) + { + *ip = host_create_any(family); + host->destroy(host); + DBG2(DBG_IKE, "using host %H", *ip); + return SUCCESS; + } + host->destroy(host); + + list = create_address_list(this); + while (!found && list->remove_last(list, (void**)&entry) == SUCCESS) + { + if (ts->includes(ts, entry->host)) + { + found = TRUE; + *ip = entry->host->clone(entry->host); + } + address_entry_destroy(entry); + } + list->destroy_function(list, (void*)address_entry_destroy); + + if (!found) + { + DBG1(DBG_IKE, "no local address found in traffic selector %R", ts); + return FAILED; + } + DBG2(DBG_IKE, "using host %H", *ip); + return SUCCESS; +} + +/** + * get the interface of a local address + */ +static int get_interface_index(private_kernel_interface_t *this, host_t* ip) +{ + linked_list_t *list; + address_entry_t *entry; + int ifindex = 0; + + DBG2(DBG_IKE, "getting iface for %H", ip); + + list = create_address_list(this); + while (!ifindex && list->remove_last(list, (void**)&entry) == SUCCESS) + { + if (ip->ip_equals(ip, entry->host)) + { + ifindex = entry->ifindex; + } + address_entry_destroy(entry); + } + list->destroy_function(list, (void*)address_entry_destroy); + + if (ifindex == 0) + { + DBG1(DBG_IKE, "unable to get interface for %H", ip); + } + return ifindex; +} + +/** + * Manages the creation and deletion of ip addresses on an interface. + * By setting the appropriate nlmsg_type, the ip will be set or unset. + */ +static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type, + int flags, int if_index, host_t *ip) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct ifaddrmsg *msg; + chunk_t chunk; + + memset(&request, 0, sizeof(request)); + + chunk = ip->get_address(ip); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + msg = (struct ifaddrmsg*)NLMSG_DATA(hdr); + msg->ifa_family = ip->get_family(ip); + msg->ifa_flags = 0; + msg->ifa_prefixlen = 8 * chunk.len; + msg->ifa_scope = RT_SCOPE_UNIVERSE; + msg->ifa_index = if_index; + + add_attribute(hdr, IFA_LOCAL, chunk, sizeof(request)); + + return netlink_send_ack(this->socket_rt, hdr); +} + +/** + * Manages source routes in the routing table. + * By setting the appropriate nlmsg_type, the route added or r. + */ +static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type, + int flags, route_entry_t *route) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct rtmsg *msg; + chunk_t chunk; + + /* if route is 0.0.0.0/0, we can't install it, as it would + * overwrite the default route. Instead, we add two routes: + * 0.0.0.0/1 and 128.0.0.0/1 + * TODO: use metrics instead */ + if (route->prefixlen == 0) + { + route_entry_t half; + status_t status; + + half.dst_net = chunk_alloca(route->dst_net.len); + memset(half.dst_net.ptr, 0, half.dst_net.len); + half.src_ip = route->src_ip; + half.if_index = route->if_index; + half.prefixlen = 1; + + status = manage_srcroute(this, nlmsg_type, flags, &half); + half.dst_net.ptr[0] |= 0x80; + status = manage_srcroute(this, nlmsg_type, flags, &half); + return status; + } + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + hdr->nlmsg_type = nlmsg_type; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + + msg = (struct rtmsg*)NLMSG_DATA(hdr); + msg->rtm_family = route->src_ip->get_family(route->src_ip); + msg->rtm_dst_len = route->prefixlen; + msg->rtm_table = RT_TABLE_MAIN; + msg->rtm_protocol = RTPROT_STATIC; + msg->rtm_type = RTN_UNICAST; + msg->rtm_scope = RT_SCOPE_UNIVERSE; + + add_attribute(hdr, RTA_DST, route->dst_net, sizeof(request)); + chunk = route->src_ip->get_address(route->src_ip); + add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request)); + chunk.ptr = (char*)&route->if_index; + chunk.len = sizeof(route->if_index); + add_attribute(hdr, RTA_OIF, chunk, sizeof(request)); + + return netlink_send_ack(this->socket_rt, hdr); +} + + +/** + * Implementation of kernel_interface_t.add_ip. + */ +static status_t add_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *iface_ip) +{ + int targetif; + vip_entry_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "adding virtual IP %H", virtual_ip); + + targetif = get_interface_index(this, iface_ip); + if (targetif == 0) + { + DBG1(DBG_KNL, "unable to add virtual IP %H, no iface found for %H", + virtual_ip, iface_ip); + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vips_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount++; + iterator->destroy(iterator); + DBG2(DBG_KNL, "virtual IP %H already added to iface %d reusing it", + virtual_ip, targetif); + return SUCCESS; + } + } + iterator->destroy(iterator); + + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + targetif, virtual_ip) == SUCCESS) + { + listed = malloc_thing(vip_entry_t); + listed->ip = virtual_ip->clone(virtual_ip); + listed->if_index = targetif; + listed->refcount = 1; + this->vips->insert_last(this->vips, listed); + DBG2(DBG_KNL, "virtual IP %H added to iface %d", + virtual_ip, targetif); + return SUCCESS; + } + + DBG2(DBG_KNL, "unable to add virtual IP %H to iface %d", + virtual_ip, targetif); + return FAILED; +} + +/** + * Implementation of kernel_interface_t.del_ip. + */ +static status_t del_ip(private_kernel_interface_t *this, + host_t *virtual_ip, host_t *iface_ip) +{ + int targetif; + vip_entry_t *listed; + iterator_t *iterator; + + DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip); + + targetif = get_interface_index(this, iface_ip); + if (targetif == 0) + { + DBG1(DBG_KNL, "unable to delete virtual IP %H, no iface found for %H", + virtual_ip, iface_ip); + return FAILED; + } + + /* beware of deadlocks (e.g. send/receive packets while holding the lock) */ + iterator = this->vips->create_iterator_locked(this->vips, &(this->vips_mutex)); + while (iterator->iterate(iterator, (void**)&listed)) + { + if (listed->if_index == targetif && + virtual_ip->ip_equals(virtual_ip, listed->ip)) + { + listed->refcount--; + if (listed->refcount == 0) + { + iterator->remove(iterator); + vip_entry_destroy(listed); + iterator->destroy(iterator); + return manage_ipaddr(this, RTM_DELADDR, 0, targetif, virtual_ip); + } + iterator->destroy(iterator); + DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", + virtual_ip); + return SUCCESS; + } + } + iterator->destroy(iterator); + + DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); + return FAILED; +} + +/** + * Implementation of kernel_interface_t.get_spi. + */ +static status_t get_spi(private_kernel_interface_t *this, + host_t *src, host_t *dst, + protocol_id_t protocol, u_int32_t reqid, + u_int32_t *spi) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr, *out; + struct xfrm_userspi_info *userspi; + u_int32_t received_spi = 0; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "getting SPI for reqid %d", reqid); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_ALLOCSPI; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userspi_info)); + + userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr); + host2xfrm(src, &userspi->info.saddr); + host2xfrm(dst, &userspi->info.id.daddr); + userspi->info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + userspi->info.mode = TRUE; /* tunnel mode */ + userspi->info.reqid = reqid; + userspi->info.family = src->get_family(src); + userspi->min = 0xc0000000; + userspi->max = 0xcFFFFFFF; + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + struct xfrm_usersa_info* usersa = NLMSG_DATA(hdr); + received_spi = usersa->id.spi; + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + + DBG1(DBG_KNL, "allocating SPI failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + free(out); + } + + if (received_spi == 0) + { + DBG1(DBG_KNL, "unable to get SPI for reqid %d", reqid); + return FAILED; + } + + DBG2(DBG_KNL, "got SPI 0x%x for reqid %d", received_spi, reqid); + + *spi = received_spi; + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.add_sa. + */ +static status_t add_sa(private_kernel_interface_t *this, + host_t *src, host_t *dst, u_int32_t spi, + protocol_id_t protocol, u_int32_t reqid, + u_int64_t expire_soft, u_int64_t expire_hard, + algorithm_t *enc_alg, algorithm_t *int_alg, + prf_plus_t *prf_plus, natt_conf_t *natt, mode_t mode, + bool replace) +{ + unsigned char request[BUFFER_SIZE]; + char *alg_name; + u_int key_size; + struct nlmsghdr *hdr; + struct xfrm_usersa_info *sa; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "adding SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr); + host2xfrm(src, &sa->saddr); + host2xfrm(dst, &sa->id.daddr); + sa->id.spi = spi; + sa->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa->family = src->get_family(src); + sa->mode = mode; + sa->replay_window = 32; + sa->reqid = reqid; + /* we currently do not expire SAs by volume/packet count */ + sa->lft.soft_byte_limit = XFRM_INF; + sa->lft.hard_byte_limit = XFRM_INF; + sa->lft.soft_packet_limit = XFRM_INF; + sa->lft.hard_packet_limit = XFRM_INF; + /* we use lifetimes since added, not since used */ + sa->lft.soft_add_expires_seconds = expire_soft; + sa->lft.hard_add_expires_seconds = expire_hard; + sa->lft.soft_use_expires_seconds = 0; + sa->lft.hard_use_expires_seconds = 0; + + struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info); + + if (enc_alg->algorithm != ENCR_UNDEFINED) + { + rthdr->rta_type = XFRMA_ALG_CRYPT; + alg_name = lookup_algorithm(encryption_algs, enc_alg, &key_size); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + encryption_algorithm_names, enc_alg->algorithm); + return FAILED; + } + DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", + encryption_algorithm_names, enc_alg->algorithm, key_size); + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = key_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); + + rthdr = XFRM_RTA_NEXT(rthdr); + } + + if (int_alg->algorithm != AUTH_UNDEFINED) + { + rthdr->rta_type = XFRMA_ALG_AUTH; + alg_name = lookup_algorithm(integrity_algs, int_alg, &key_size); + if (alg_name == NULL) + { + DBG1(DBG_KNL, "algorithm %N not supported by kernel!", + integrity_algorithm_names, int_alg->algorithm); + return FAILED; + } + DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", + integrity_algorithm_names, int_alg->algorithm, key_size); + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = key_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); + + rthdr = XFRM_RTA_NEXT(rthdr); + } + + /* TODO: add IPComp here */ + + if (natt) + { + rthdr->rta_type = XFRMA_ENCAP; + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); + + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_encap_tmpl* encap = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); + encap->encap_type = UDP_ENCAP_ESPINUDP; + encap->encap_sport = htons(natt->sport); + encap->encap_dport = htons(natt->dport); + memset(&encap->encap_oa, 0, sizeof (xfrm_address_t)); + /* encap_oa could probably be derived from the + * traffic selectors [rfc4306, p39]. In the netlink kernel implementation + * pluto does the same as we do here but it uses encap_oa in the + * pfkey implementation. BUT as /usr/src/linux/net/key/af_key.c indicates + * the kernel ignores it anyway + * -> does that mean that NAT-T encap doesn't work in transport mode? + * No. The reason the kernel ignores NAT-OA is that it recomputes + * (or, rather, just ignores) the checksum. If packets pass + * the IPsec checks it marks them "checksum ok" so OA isn't needed. */ + rthdr = XFRM_RTA_NEXT(rthdr); + } + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unalbe to add SAD entry with SPI 0x%x", spi); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.update_sa. + */ +static status_t update_sa(private_kernel_interface_t *this, + host_t *src, host_t *dst, + host_t *new_src, host_t *new_dst, + host_diff_t src_changes, host_diff_t dst_changes, + u_int32_t spi, protocol_id_t protocol) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr, *out = NULL; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *sa = NULL; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + sa = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + if (sa == NULL) + { + DBG1(DBG_KNL, "unable to update SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + + DBG2(DBG_KNL, "updating SAD entry with SPI 0x%x", spi); + + hdr = out; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_UPDSA; + + if (src_changes & HOST_DIFF_ADDR) + { + host2xfrm(new_src, &sa->saddr); + } + + if (dst_changes & HOST_DIFF_ADDR) + { + hdr->nlmsg_type = XFRM_MSG_NEWSA; + host2xfrm(new_dst, &sa->id.daddr); + } + + if (src_changes & HOST_DIFF_PORT || dst_changes & HOST_DIFF_PORT) + { + struct rtattr *rtattr = XFRM_RTA(hdr, struct xfrm_usersa_info); + size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_usersa_info); + while (RTA_OK(rtattr, rtsize)) + { + if (rtattr->rta_type == XFRMA_ENCAP) + { + struct xfrm_encap_tmpl* encap; + encap = (struct xfrm_encap_tmpl*)RTA_DATA(rtattr); + encap->encap_sport = ntohs(new_src->get_port(new_src)); + encap->encap_dport = ntohs(new_dst->get_port(new_dst)); + break; + } + rtattr = RTA_NEXT(rtattr, rtsize); + } + } + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unalbe to update SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + free(out); + + if (dst_changes & HOST_DIFF_ADDR) + { + return this->public.del_sa(&this->public, dst, spi, protocol); + } + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.query_sa. + */ +static status_t query_sa(private_kernel_interface_t *this, host_t *dst, + u_int32_t spi, protocol_id_t protocol, + u_int32_t *use_time) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *out = NULL, *hdr; + struct xfrm_usersa_id *sa_id; + struct xfrm_usersa_info *sa = NULL; + size_t len; + + DBG2(DBG_KNL, "querying SAD entry with SPI 0x%x", spi); + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWSA: + { + sa = NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (sa == NULL) + { + DBG1(DBG_KNL, "unable to query SAD entry with SPI 0x%x", spi); + free(out); + return FAILED; + } + + *use_time = sa->curlft.use_time; + free (out); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.del_sa. + */ +static status_t del_sa(private_kernel_interface_t *this, host_t *dst, + u_int32_t spi, protocol_id_t protocol) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct xfrm_usersa_id *sa_id; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "deleting SAD entry with SPI 0x%x", spi); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + host2xfrm(dst, &sa_id->daddr); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unalbe to delete SAD entry with SPI 0x%x", spi); + return FAILED; + } + DBG2(DBG_KNL, "deleted SAD entry with SPI 0x%x", spi); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.add_policy. + */ +static status_t add_policy(private_kernel_interface_t *this, + host_t *src, host_t *dst, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, protocol_id_t protocol, + u_int32_t reqid, bool high_prio, mode_t mode, + bool update) +{ + iterator_t *iterator; + policy_entry_t *current, *policy; + bool found = FALSE; + unsigned char request[BUFFER_SIZE]; + struct xfrm_userpolicy_info *policy_info; + struct nlmsghdr *hdr; + + /* create a policy */ + policy = malloc_thing(policy_entry_t); + memset(policy, 0, sizeof(policy_entry_t)); + policy->sel = ts2selector(src_ts, dst_ts); + policy->direction = direction; + + /* find the policy, which matches EXACTLY */ + pthread_mutex_lock(&this->policies_mutex); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (memcmp(¤t->sel, &policy->sel, sizeof(struct xfrm_selector)) == 0 && + policy->direction == current->direction) + { + /* use existing policy */ + if (!update) + { + current->refcount++; + DBG2(DBG_KNL, "policy %R===%R already exists, increasing ", + "refcount", src_ts, dst_ts); + } + free(policy); + policy = current; + found = TRUE; + break; + } + } + iterator->destroy(iterator); + if (!found) + { /* apply the new one, if we have no such policy */ + this->policies->insert_last(this->policies, policy); + policy->refcount = 1; + } + + DBG2(DBG_KNL, "adding policy %R===%R", src_ts, dst_ts); + + memset(&request, 0, sizeof(request)); + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); + + policy_info = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); + policy_info->sel = policy->sel; + policy_info->dir = policy->direction; + /* calculate priority based on source selector size, small size = high prio */ + policy_info->priority = high_prio ? PRIO_HIGH : PRIO_LOW; + policy_info->priority -= policy->sel.prefixlen_s * 10; + policy_info->priority -= policy->sel.proto ? 2 : 0; + policy_info->priority -= policy->sel.sport_mask ? 1 : 0; + policy_info->action = XFRM_POLICY_ALLOW; + policy_info->share = XFRM_SHARE_ANY; + pthread_mutex_unlock(&this->policies_mutex); + + /* policies don't expire */ + policy_info->lft.soft_byte_limit = XFRM_INF; + policy_info->lft.soft_packet_limit = XFRM_INF; + policy_info->lft.hard_byte_limit = XFRM_INF; + policy_info->lft.hard_packet_limit = XFRM_INF; + policy_info->lft.soft_add_expires_seconds = 0; + policy_info->lft.hard_add_expires_seconds = 0; + policy_info->lft.soft_use_expires_seconds = 0; + policy_info->lft.hard_use_expires_seconds = 0; + + struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info); + rthdr->rta_type = XFRMA_TMPL; + + rthdr->rta_len = sizeof(struct xfrm_user_tmpl); + rthdr->rta_len = RTA_LENGTH(rthdr->rta_len); + + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + tmpl->reqid = reqid; + tmpl->id.proto = (protocol == PROTO_AH) ? KERNEL_AH : KERNEL_ESP; + tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; + tmpl->mode = mode; + tmpl->family = src->get_family(src); + + host2xfrm(src, &tmpl->saddr); + host2xfrm(dst, &tmpl->id.daddr); + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to add policy %R===%R", src_ts, dst_ts); + return FAILED; + } + + /* install a route, if: + * - we are NOT updating a policy + * - this is a forward policy (to just get one for each child) + * - we are in tunnel mode + * - we are not using IPv6 (does not work correctly yet!) + */ + if (policy->route == NULL && direction == POLICY_FWD && + mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6) + { + policy->route = malloc_thing(route_entry_t); + if (get_address_by_ts(this, dst_ts, &policy->route->src_ip) == SUCCESS) + { + policy->route->if_index = get_interface_index(this, dst); + policy->route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); + memcpy(policy->route->dst_net.ptr, &policy->sel.saddr, policy->route->dst_net.len); + policy->route->prefixlen = policy->sel.prefixlen_s; + + if (manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + policy->route) != SUCCESS) + { + DBG1(DBG_KNL, "unable to install source route for %H", + policy->route->src_ip); + route_entry_destroy(policy->route); + policy->route = NULL; + } + } + else + { + free(policy->route); + policy->route = NULL; + } + } + + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.query_policy. + */ +static status_t query_policy(private_kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, u_int32_t *use_time) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *out = NULL, *hdr; + struct xfrm_userpolicy_id *policy_id; + struct xfrm_userpolicy_info *policy = NULL; + size_t len; + + memset(&request, 0, sizeof(request)); + + DBG2(DBG_KNL, "querying policy %R===%R", src_ts, dst_ts); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_GETPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); + policy_id->sel = ts2selector(src_ts, dst_ts); + policy_id->dir = direction; + + if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) + { + hdr = out; + while (NLMSG_OK(hdr, len)) + { + switch (hdr->nlmsg_type) + { + case XFRM_MSG_NEWPOLICY: + { + policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(hdr); + DBG1(DBG_KNL, "querying policy failed: %s (%d)", + strerror(-err->error), -err->error); + break; + } + default: + hdr = NLMSG_NEXT(hdr, len); + continue; + case NLMSG_DONE: + break; + } + break; + } + } + + if (policy == NULL) + { + DBG2(DBG_KNL, "unable to query policy %R===%R", src_ts, dst_ts); + free(out); + return FAILED; + } + *use_time = (time_t)policy->curlft.use_time; + + free(out); + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.del_policy. + */ +static status_t del_policy(private_kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction) +{ + policy_entry_t *current, policy, *to_delete = NULL; + route_entry_t *route; + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *hdr; + struct xfrm_userpolicy_id *policy_id; + iterator_t *iterator; + + DBG2(DBG_KNL, "deleting policy %R===%R", src_ts, dst_ts); + + /* create a policy */ + memset(&policy, 0, sizeof(policy_entry_t)); + policy.sel = ts2selector(src_ts, dst_ts); + policy.direction = direction; + + /* find the policy */ + pthread_mutex_lock(&this->policies_mutex); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (memcmp(¤t->sel, &policy.sel, sizeof(struct xfrm_selector)) == 0 && + policy.direction == current->direction) + { + to_delete = current; + if (--to_delete->refcount > 0) + { + /* is used by more SAs, keep in kernel */ + DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed"); + iterator->destroy(iterator); + pthread_mutex_unlock(&this->policies_mutex); + return SUCCESS; + } + /* remove if last reference */ + iterator->remove(iterator); + break; + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&this->policies_mutex); + if (!to_delete) + { + DBG1(DBG_KNL, "deleting policy %R===%R failed, not found", src_ts, dst_ts); + return NOT_FOUND; + } + + memset(&request, 0, sizeof(request)); + + hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); + + policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); + policy_id->sel = to_delete->sel; + policy_id->dir = direction; + + route = to_delete->route; + free(to_delete); + + if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS) + { + DBG1(DBG_KNL, "unable to delete policy %R===%R", src_ts, dst_ts); + return FAILED; + } + + if (route) + { + if (manage_srcroute(this, RTM_DELROUTE, 0, route) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with " + "policy %R===%R", src_ts, dst_ts); + } + route_entry_destroy(route); + } + return SUCCESS; +} + +/** + * Implementation of kernel_interface_t.destroy. + */ +static void destroy(private_kernel_interface_t *this) +{ + pthread_cancel(this->event_thread); + pthread_join(this->event_thread, NULL); + close(this->socket_xfrm_events); + close(this->socket_xfrm); + close(this->socket_rt); + this->vips->destroy(this->vips); + this->policies->destroy(this->policies); + free(this); +} + +/* + * Described in header. + */ +kernel_interface_t *kernel_interface_create() +{ + private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t); + struct sockaddr_nl addr; + + /* public functions */ + this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi; + this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,natt_conf_t*,mode_t,bool))add_sa; + this->public.update_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_diff_t,host_diff_t))update_sa; + this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_sa; + this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; + this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t,bool))add_policy; + this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy; + this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy; + + this->public.get_interface = (char*(*)(kernel_interface_t*,host_t*))get_interface_name; + this->public.create_address_list = (linked_list_t*(*)(kernel_interface_t*))create_address_list_public; + this->public.add_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) add_ip; + this->public.del_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) del_ip; + this->public.destroy = (void(*)(kernel_interface_t*)) destroy; + + /* private members */ + this->vips = linked_list_create(); + this->policies = linked_list_create(); + pthread_mutex_init(&this->policies_mutex,NULL); + pthread_mutex_init(&this->vips_mutex,NULL); + + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + /* create and bind XFRM socket */ + this->socket_xfrm = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm <= 0) + { + charon->kill(charon, "unable to create XFRM netlink socket"); + } + + if (bind(this->socket_xfrm, (struct sockaddr*)&addr, sizeof(addr))) + { + charon->kill(charon, "unable to bind XFRM netlink socket"); + } + + /* create and bind RT socket */ + this->socket_rt = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->socket_rt <= 0) + { + charon->kill(charon, "unable to create RT netlink socket"); + } + + if (bind(this->socket_rt, (struct sockaddr*)&addr, sizeof(addr))) + { + charon->kill(charon, "unable to bind RT netlink socket"); + } + + /* create and bind XFRM socket for ACQUIRE & EXPIRE */ + addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; + this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm_events <= 0) + { + charon->kill(charon, "unable to create XFRM event socket"); + } + + if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) + { + charon->kill(charon, "unable to bind XFRM event socket"); + } + + /* create a thread receiving ACQUIRE & EXPIRE events */ + if (pthread_create(&this->event_thread, NULL, + (void*(*)(void*))receive_events, this)) + { + charon->kill(charon, "unable to create xfrm event dispatcher thread"); + } + + return &this->public; +} + +/* vim: set ts=4 sw=4 noet: */ diff --git a/src/charon/threads/kernel_interface.h b/src/charon/threads/kernel_interface.h new file mode 100644 index 000000000..34b06f594 --- /dev/null +++ b/src/charon/threads/kernel_interface.h @@ -0,0 +1,331 @@ +/** + * @file kernel_interface.h + * + * @brief Interface of kernel_interface_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef KERNEL_INTERFACE_H_ +#define KERNEL_INTERFACE_H_ + +typedef struct natt_conf_t natt_conf_t; +typedef enum policy_dir_t policy_dir_t; +typedef struct kernel_interface_t kernel_interface_t; + +#include +#include +#include + +/** + * Configuration for NAT-T + */ +struct natt_conf_t { + /** source port to use for UDP-encapsulated packets */ + u_int16_t sport; + /** dest port to use for UDP-encapsulated packets */ + u_int16_t dport; +}; + +/** + * Direction of a policy. These are equal to those + * defined in xfrm.h, but we want to stay implementation + * neutral here. + */ +enum policy_dir_t { + /** Policy for inbound traffic */ + POLICY_IN = 0, + /** Policy for outbound traffic */ + POLICY_OUT = 1, + /** Policy for forwarded traffic */ + POLICY_FWD = 2, +}; + +/** + * @brief Interface to the kernel. + * + * The kernel interface handles the communication with the kernel + * for SA and policy management. It allows setup of these, and provides + * further the handling of kernel events. + * Policy information are cached in the interface. This is necessary to do + * reference counting. The Linux kernel does not allow the same policy + * installed twice, but we need this as CHILD_SA exist multiple times + * when rekeying. Thats why we do reference counting of policies. + * + * @b Constructors: + * - kernel_interface_create() + * + * @ingroup threads + */ +struct kernel_interface_t { + + /** + * @brief Get a SPI from the kernel. + * + * @warning get_spi() implicitely creates an SA with + * the allocated SPI, therefore the replace flag + * in add_sa() must be set when installing this SA. + * + * @param this calling object + * @param src source address of SA + * @param dst destination address of SA + * @param protocol protocol for SA (ESP/AH) + * @param reqid unique ID for this SA + * @param[out] spi allocated spi + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*get_spi)(kernel_interface_t *this, host_t *src, host_t *dst, + protocol_id_t protocol, u_int32_t reqid, u_int32_t *spi); + + /** + * @brief Add an SA to the SAD. + * + * add_sa() may update an already allocated + * SPI (via get_spi). In this case, the replace + * flag must be set. + * This function does install a single SA for a + * single protocol in one direction. The kernel-interface + * gets the keys itself from the PRF, as we don't know + * his algorithms and key sizes. + * + * @param this calling object + * @param src source address for this SA + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param reqid unique ID for this SA + * @param expire_soft lifetime in seconds before rekeying + * @param expire_hard lieftime in seconds before delete + * @param enc_alg Algorithm to use for encryption (ESP only) + * @param int_alg Algorithm to use for integrity protection + * @param prf_plus PRF to derive keys from + * @param natt NAT-T Configuration, or NULL of no NAT-T used + * @param mode mode of the SA (tunnel, transport) + * @param replace Should an already installed SA be updated? + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_sa) (kernel_interface_t *this, + host_t *src, host_t *dst, u_int32_t spi, + protocol_id_t protocol, u_int32_t reqid, + u_int64_t expire_soft, u_int64_t expire_hard, + algorithm_t *enc_alg, algorithm_t *int_alg, + prf_plus_t *prf_plus, natt_conf_t *natt, + mode_t mode, bool update); + + /** + * @brief Update the hosts on an installed SA. + * + * We cannot directly update the destination address as the kernel + * requires the spi, the protocol AND the destination address (and family) + * to identify SAs. Therefore if the destination address changed we + * create a new SA and delete the old one. + * + * @param this calling object + * @param dst destination address for this SA + * @param spi SPI of the SA + * @param protocol protocol for this SA (ESP/AH) + * @param new_src new source address for this SA + * @param new_dst new destination address for this SA + * @param src_changes changes in src + * @param dst_changes changes in dst + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*update_sa)(kernel_interface_t *this, host_t *dst, u_int32_t spi, + protocol_id_t protocol, + host_t *new_src, host_t *new_dst, + host_diff_t src_changes, host_diff_t dst_changes); + + /** + * @brief Query the use time of an SA. + * + * The use time of an SA is not the time of the last usage, but + * the time of the first usage of the SA. + * + * @param this calling object + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @param[out] use_time the time of this SA's last use + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*query_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, + protocol_id_t protocol, u_int32_t *use_time); + + /** + * @brief Delete a previusly installed SA from the SAD. + * + * @param this calling object + * @param dst destination address for this SA + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, + protocol_id_t protocol); + + /** + * @brief Add a policy to the SPD. + * + * A policy is always associated to an SA. Traffic which matches a + * policy is handled by the SA with the same reqid. + * If the update flag is set, the policy is updated with the new + * src/dst addresses. + * If the update flag is not set, but a such policy is already in the + * kernel, the reference count to this policy is increased. + * + * @param this calling object + * @param src source address of SA + * @param dst dest address of SA + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD + * @param protocol protocol to use to protect traffic (AH/ESP) + * @param reqid uniqe ID of an SA to use to enforce policy + * @param high_prio if TRUE, uses a higher priority than any with FALSE + * @param mode mode of SA (tunnel, transport) + * @param update update an existing policy, if TRUE + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_policy) (kernel_interface_t *this, + host_t *src, host_t *dst, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, protocol_id_t protocol, + u_int32_t reqid, bool high_prio, + mode_t mode, bool update); + + /** + * @brief Query the use time of a policy. + * + * The use time of a policy is the time the policy was used + * for the last time. + * + * @param this calling object + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD + * @param[out] use_time the time of this SA's last use + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*query_policy) (kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction, u_int32_t *use_time); + + /** + * @brief Remove a policy from the SPD. + * + * The kernel interface implements reference counting for policies. + * If the same policy is installed multiple times (in the case of rekeying), + * the reference counter is increased. del_policy() decreases the ref counter + * and removes the policy only when no more references are available. + * + * @param this calling object + * @param src_ts traffic selector to match traffic source + * @param dst_ts traffic selector to match traffic dest + * @param direction direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_policy) (kernel_interface_t *this, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, + policy_dir_t direction); + + /** + * @brief Get the interface name of a local address. + * + * @param this calling object + * @param host address to get interface name from + * @return allocated interface name, or NULL if not found + */ + char* (*get_interface) (kernel_interface_t *this, host_t *host); + + /** + * @brief Creates a list of all local addresses. + * + * @param this calling object + * @return allocated list with host_t objects + */ + linked_list_t *(*create_address_list) (kernel_interface_t *this); + + /** + * @brief Add a virtual IP to an interface. + * + * Virtual IPs are attached to an interface. If an IP is added multiple + * times, the IP is refcounted and not removed until del_ip() was called + * as many times as add_ip(). + * The virtual IP is attached to the interface where the iface_ip is found. + * + * @param this calling object + * @param virtual_ip virtual ip address to assign + * @param iface_ip IP of an interface to attach virtual IP + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_ip) (kernel_interface_t *this, host_t *virtual_ip, + host_t *iface_ip); + + /** + * @brief Remove a virtual IP from an interface. + * + * The kernel interface uses refcounting, see add_ip(). + * + * @param this calling object + * @param virtual_ip virtual ip address to assign + * @param iface_ip IP of an interface to remove virtual IP from + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_ip) (kernel_interface_t *this, host_t *virtual_ip, + host_t *iface_ip); + + /** + * @brief Destroys a kernel_interface object. + * + * @param kernel_interface_t calling object + */ + void (*destroy) (kernel_interface_t *kernel_interface); +}; + +/** + * @brief Creates an object of type kernel_interface_t. + * + * @ingroup threads + */ +kernel_interface_t *kernel_interface_create(void); + +#endif /*KERNEL_INTERFACE_H_*/ diff --git a/src/charon/threads/receiver.c b/src/charon/threads/receiver.c new file mode 100644 index 000000000..7195c162d --- /dev/null +++ b/src/charon/threads/receiver.c @@ -0,0 +1,372 @@ +/** + * @file receiver.c + * + * @brief Implementation of receiver_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "receiver.h" + +#include +#include +#include +#include +#include +#include + +/** length of the full cookie, including time (u_int32_t + SHA1()) */ +#define COOKIE_LENGTH 24 +/** lifetime of a cookie, in seconds */ +#define COOKIE_LIFETIME 10 +/** how many times to reuse the secret */ +#define COOKIE_REUSE 10000 +/** require cookies after half open IKE_SAs */ +#define COOKIE_TRESHOLD 10 +/** how many half open IKE_SAs per peer before blocking */ +#define BLOCK_TRESHOLD 5 +/** length of the secret to use for cookie calculation */ +#define SECRET_LENGTH 16 + +typedef struct private_receiver_t private_receiver_t; + +/** + * Private data of a receiver_t object. + */ +struct private_receiver_t { + /** + * Public part of a receiver_t object. + */ + receiver_t public; + + /** + * Assigned thread. + */ + pthread_t assigned_thread; + + /** + * current secret to use for cookie calculation + */ + char secret[SECRET_LENGTH]; + + /** + * previous secret used to verify older cookies + */ + char secret_old[SECRET_LENGTH]; + + /** + * how many times we have used "secret" so far + */ + u_int32_t secret_used; + + /** + * time we did the cookie switch + */ + u_int32_t secret_switch; + + /** + * time offset to use, hides our system time + */ + u_int32_t secret_offset; + + /** + * the randomizer to use for secret generation + */ + randomizer_t *randomizer; + + /** + * hasher to use for cookie calculation + */ + hasher_t *hasher; +}; + +/** + * send a notify back to the sender + */ +static void send_notify(message_t *request, notify_type_t type, chunk_t data) +{ + if (request->get_request(request) && + request->get_exchange_type(request) == IKE_SA_INIT) + { + message_t *response; + host_t *src, *dst; + packet_t *packet; + ike_sa_id_t *ike_sa_id; + + response = message_create(); + dst = request->get_source(request); + src = request->get_destination(request); + response->set_source(response, src->clone(src)); + response->set_destination(response, dst->clone(dst)); + response->set_exchange_type(response, request->get_exchange_type(request)); + response->set_request(response, FALSE); + response->set_message_id(response, 0); + ike_sa_id = request->get_ike_sa_id(request); + ike_sa_id->switch_initiator(ike_sa_id); + response->set_ike_sa_id(response, ike_sa_id); + response->add_notify(response, FALSE, type, data); + if (response->generate(response, NULL, NULL, &packet) == SUCCESS) + { + charon->sender->send(charon->sender, packet); + response->destroy(response); + } + } +} + +/** + * build a cookie + */ +static chunk_t cookie_build(private_receiver_t *this, message_t *message, + u_int32_t t, chunk_t secret) +{ + u_int64_t spi = message->get_initiator_spi(message); + host_t *ip = message->get_source(message); + chunk_t input, hash = chunk_alloca(this->hasher->get_hash_size(this->hasher)); + + /* COOKIE = t | sha1( IPi | SPIi | t | secret ) */ + input = chunk_cata("cccc", ip->get_address(ip), chunk_from_thing(spi), + chunk_from_thing(t), secret); + this->hasher->get_hash(this->hasher, input, hash.ptr); + return chunk_cat("cc", chunk_from_thing(t), hash); +} + +/** + * verify a received cookie + */ +static bool cookie_verify(private_receiver_t *this, message_t *message, + chunk_t cookie) +{ + u_int32_t t, now; + chunk_t reference; + chunk_t secret; + + now = time(NULL); + t = *(u_int32_t*)cookie.ptr; + + if (cookie.len != COOKIE_LENGTH || + t < now - this->secret_offset - COOKIE_LIFETIME) + { + DBG2(DBG_NET, "received cookie lifetime expired, rejecting"); + return FALSE; + } + + /* check if cookie is derived from old_secret */ + if (t + this->secret_offset > this->secret_switch) + { + secret = chunk_from_thing(this->secret); + } + else + { + secret = chunk_from_thing(this->secret_old); + } + + /* compare own calculation against received */ + reference = cookie_build(this, message, t, secret); + if (chunk_equals(reference, cookie)) + { + chunk_free(&reference); + return TRUE; + } + chunk_free(&reference); + return FALSE; +} + +/** + * check if cookies are required, and if so, a valid cookie is included + */ +static bool cookie_required(private_receiver_t *this, message_t *message) +{ + bool failed = FALSE; + + if (charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, + NULL) >= COOKIE_TRESHOLD) + { + /* check for a cookie. We don't use our parser here and do it + * quick and dirty for performance reasons. + * we assume to cookie is the first payload (which is a MUST), and + * the cookies SPI length is zero. */ + packet_t *packet = message->get_packet(message); + chunk_t data = packet->get_data(packet); + if (data.len < + IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH + COOKIE_LENGTH || + *(data.ptr + 16) != NOTIFY || + *(u_int16_t*)(data.ptr + IKE_HEADER_LENGTH + 6) != htons(COOKIE)) + { + /* no cookie found */ + failed = TRUE; + } + else + { + data.ptr += IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH; + data.len = COOKIE_LENGTH; + if (!cookie_verify(this, message, data)) + { + DBG2(DBG_NET, "found cookie, but content invalid"); + failed = TRUE; + } + } + packet->destroy(packet); + } + return failed; +} + +/** + * check if peer has to many half open IKE_SAs + */ +static bool peer_to_aggressive(private_receiver_t *this, message_t *message) +{ + if (charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, + message->get_source(message)) >= BLOCK_TRESHOLD) + { + return TRUE; + } + return FALSE; +} + +/** + * Implementation of receiver_t.receive_packets. + */ +static void receive_packets(private_receiver_t *this) +{ + packet_t *packet; + message_t *message; + job_t *job; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + DBG1(DBG_NET, "receiver thread running, thread_ID: %06u", + (int)pthread_self()); + + while (TRUE) + { + /* read in a packet */ + if (charon->socket->receive(charon->socket, &packet) != SUCCESS) + { + DBG1(DBG_NET, "receiving from socket failed!"); + continue; + } + + /* parse message header */ + message = message_create_from_packet(packet); + if (message->parse_header(message) != SUCCESS) + { + DBG1(DBG_NET, "received invalid IKE header from %H, ignored", + packet->get_source(packet)); + message->destroy(message); + continue; + } + + /* check IKE major version */ + if (message->get_major_version(message) != IKE_MAJOR_VERSION) + { + DBG1(DBG_NET, "received unsupported IKE version %d.%d from %H, " + "sending INVALID_MAJOR_VERSION", message->get_major_version(message), + message->get_minor_version(message), packet->get_source(packet)); + send_notify(message, INVALID_MAJOR_VERSION, chunk_empty); + message->destroy(message); + continue; + } + + if (message->get_request(message) && + message->get_exchange_type(message) == IKE_SA_INIT) + { + /* check for cookies */ + if (cookie_required(this, message)) + { + u_int32_t now = time(NULL); + chunk_t cookie = cookie_build(this, message, now - this->secret_offset, + chunk_from_thing(this->secret)); + + DBG2(DBG_NET, "received packet from: %#H to %#H", + message->get_source(message), + message->get_destination(message)); + DBG2(DBG_NET, "sending COOKIE notify to %H", + message->get_source(message)); + send_notify(message, COOKIE, cookie); + chunk_free(&cookie); + if (++this->secret_used > COOKIE_REUSE) + { + /* create new cookie */ + DBG1(DBG_NET, "generating new cookie secret after %d uses", + this->secret_used); + memcpy(this->secret_old, this->secret, SECRET_LENGTH); + this->randomizer->get_pseudo_random_bytes(this->randomizer, + SECRET_LENGTH, this->secret); + this->secret_switch = now; + this->secret_used = 0; + } + message->destroy(message); + continue; + } + + /* check if peer has not too many IKE_SAs half open */ + if (peer_to_aggressive(this, message)) + { + DBG1(DBG_NET, "ignoring IKE_SA setup from %H, " + "peer to aggressive", message->get_source(message)); + message->destroy(message); + continue; + } + } + job = (job_t *)process_message_job_create(message); + charon->job_queue->add(charon->job_queue, job); + } +} + +/** + * Implementation of receiver_t.destroy. + */ +static void destroy(private_receiver_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + this->randomizer->destroy(this->randomizer); + this->hasher->destroy(this->hasher); + free(this); +} + +/* + * Described in header. + */ +receiver_t *receiver_create() +{ + private_receiver_t *this = malloc_thing(private_receiver_t); + u_int32_t now = time(NULL); + + this->public.destroy = (void(*)(receiver_t*)) destroy; + + this->randomizer = randomizer_create(); + this->hasher = hasher_create(HASH_SHA1); + this->secret_switch = now; + this->secret_offset = random() % now; + this->secret_used = 0; + this->randomizer->get_pseudo_random_bytes(this->randomizer, SECRET_LENGTH, + this->secret); + memcpy(this->secret_old, this->secret, SECRET_LENGTH); + + if (pthread_create(&this->assigned_thread, NULL, + (void*)receive_packets, this) != 0) + { + free(this); + charon->kill(charon, "unable to create receiver thread"); + } + + return &this->public; +} diff --git a/src/charon/threads/receiver.h b/src/charon/threads/receiver.h new file mode 100644 index 000000000..68d9136c0 --- /dev/null +++ b/src/charon/threads/receiver.h @@ -0,0 +1,81 @@ +/** + * @file receiver.h + * + * @brief Interface of receiver_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef RECEIVER_H_ +#define RECEIVER_H_ + +typedef struct receiver_t receiver_t; + +#include +#include + +/** + * @brief Receives packets from the socket and adds them to the job queue. + * + * The receiver starts a thread, wich reads on the blocking socket. A received + * packet is preparsed and a process_message_job is queued in the job queue. + * + * To endure DoS attacks, cookies are enabled when to many IKE_SAs are half + * open. The calculation of cookies is slightly different from the proposed + * method in RFC4306. We do not include a nonce, because we think the advantage + * we gain does not justify the overhead to parse the whole message. + * Instead of VersionIdOfSecret, we include a timestamp. This allows us to + * find out wich key was used for cookie creation. Further, we can set a + * lifetime for the cookie, which allows us to reuse the secret for a longer + * time. + * COOKIE = time | sha1( IPi | SPIi | time | secret ) + * + * The secret is changed after a certain amount of cookies sent. The old + * secret is stored to allow a clean migration between secret changes. + * + * Further, the number of half-initiated IKE_SAs is limited per peer. This + * mades it impossible for a peer to flood the server with its real IP address. + * + * @b Constructors: + * - receiver_create() + * + * @ingroup threads + */ +struct receiver_t { + + /** + * @brief Destroys a receiver_t object. + * + * @param receiver receiver object + */ + void (*destroy) (receiver_t *receiver); +}; + +/** + * @brief Create a receiver_t object. + * + * The receiver thread will start working, get data + * from the socket and add those packets to the job queue. + * + * @return receiver_t object + * + * @ingroup threads + */ +receiver_t * receiver_create(void); + +#endif /*RECEIVER_H_*/ diff --git a/src/charon/threads/scheduler.c b/src/charon/threads/scheduler.c new file mode 100644 index 000000000..74091e3a3 --- /dev/null +++ b/src/charon/threads/scheduler.c @@ -0,0 +1,102 @@ +/** + * @file scheduler.c + * + * @brief Implementation of scheduler_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "scheduler.h" + +#include +#include + + +typedef struct private_scheduler_t private_scheduler_t; + +/** + * Private data of a scheduler_t object. + */ +struct private_scheduler_t { + /** + * Public part of a scheduler_t object. + */ + scheduler_t public; + + /** + * Assigned thread. + */ + pthread_t assigned_thread; +}; + +/** + * Implementation of private_scheduler_t.get_events. + */ +static void get_events(private_scheduler_t * this) +{ + job_t *current_job; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + DBG1(DBG_JOB, "scheduler thread running, thread_ID: %06u", + (int)pthread_self()); + + while (TRUE) + { + DBG2(DBG_JOB, "waiting for next event..."); + /* get a job, this block until one is available */ + current_job = charon->event_queue->get(charon->event_queue); + /* queue the job in the job queue, workers will eat them */ + DBG2(DBG_JOB, "got event, adding job %N to job-queue", + job_type_names, current_job->get_type(current_job)); + charon->job_queue->add(charon->job_queue, current_job); + } +} + +/** + * Implementation of scheduler_t.destroy. + */ +static void destroy(private_scheduler_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + free(this); +} + +/* + * Described in header. + */ +scheduler_t * scheduler_create() +{ + private_scheduler_t *this = malloc_thing(private_scheduler_t); + + this->public.destroy = (void(*)(scheduler_t*)) destroy; + + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))get_events, this) != 0) + { + /* thread could not be created */ + free(this); + charon->kill(charon, "unable to create scheduler thread"); + } + + return &(this->public); +} diff --git a/src/charon/threads/scheduler.h b/src/charon/threads/scheduler.h new file mode 100644 index 000000000..daecce3c6 --- /dev/null +++ b/src/charon/threads/scheduler.h @@ -0,0 +1,68 @@ +/** + * @file scheduler.h + * + * @brief Interface of scheduler_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SCHEDULER_H_ +#define SCHEDULER_H_ + +typedef struct scheduler_t scheduler_t; + +#include + +/** + * @brief The scheduler thread is responsible for timed events. + * + * The scheduler thread takes out jobs from the event-queue and adds them + * to the job-queue. + * + * Starts a thread which does the work, since event-queue is blocking. + * + * @b Constructors: + * - scheduler_create() + * + * @ingroup threads + */ +struct scheduler_t { + + /** + * @brief Destroys a scheduler object. + * + * @param scheduler calling object + */ + void (*destroy) (scheduler_t *scheduler); +}; + +/** + * @brief Create a scheduler with its associated thread. + * + * The thread will start to get jobs form the event queue + * and adds them to the job queue. + * + * @return + * - scheduler_t object + * - NULL if thread could not be started + * + * @ingroup threads + */ +scheduler_t * scheduler_create(void); + +#endif /*SCHEDULER_H_*/ diff --git a/src/charon/threads/sender.c b/src/charon/threads/sender.c new file mode 100644 index 000000000..c1cd0a68c --- /dev/null +++ b/src/charon/threads/sender.c @@ -0,0 +1,149 @@ +/** + * @file sender.c + * + * @brief Implementation of sender_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "sender.h" + +#include +#include + + +typedef struct private_sender_t private_sender_t; + +/** + * Private data of a sender_t object. + */ +struct private_sender_t { + /** + * Public part of a sender_t object. + */ + sender_t public; + + /** + * Assigned thread. + */ + pthread_t assigned_thread; + + /** + * The packets are stored in a linked list + */ + linked_list_t *list; + + /** + * mutex to synchronize access to list + */ + pthread_mutex_t mutex; + + /** + * condvar to signal for packets in list + */ + pthread_cond_t condvar; +}; + +/** + * implements sender_t.send + */ +static void send_(private_sender_t *this, packet_t *packet) +{ + host_t *src, *dst; + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + DBG1(DBG_NET, "sending packet: from %#H to %#H", src, dst); + + pthread_mutex_lock(&this->mutex); + this->list->insert_last(this->list, packet); + pthread_mutex_unlock(&this->mutex); + pthread_cond_signal(&this->condvar); +} + +/** + * Implementation of private_sender_t.send_packets. + */ +static void send_packets(private_sender_t * this) +{ + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + DBG1(DBG_NET, "sender thread running, thread_ID: %06u", (int)pthread_self()); + + while (TRUE) + { + packet_t *packet; + int oldstate; + + pthread_mutex_lock(&this->mutex); + /* go to wait while no packets available */ + while (this->list->get_count(this->list) == 0) + { + /* add cleanup handler, wait for packet, remove cleanup handler */ + pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&this->mutex); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + pthread_cond_wait(&this->condvar, &this->mutex); + + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + } + this->list->remove_first(this->list, (void**)&packet); + pthread_mutex_unlock(&this->mutex); + + charon->socket->send(charon->socket, packet); + packet->destroy(packet); + } +} + +/** + * Implementation of sender_t.destroy. + */ +static void destroy(private_sender_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + this->list->destroy_offset(this->list, offsetof(packet_t, destroy)); + free(this); +} + +/* + * Described in header. + */ +sender_t * sender_create() +{ + private_sender_t *this = malloc_thing(private_sender_t); + + this->public.send = (void(*)(sender_t*,packet_t*))send_; + this->public.destroy = (void(*)(sender_t*)) destroy; + + this->list = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + pthread_cond_init(&this->condvar, NULL); + + if (pthread_create(&this->assigned_thread, NULL, + (void*)send_packets, this) != 0) + { + charon->kill(charon, "unable to create sender thread"); + } + + return &(this->public); +} diff --git a/src/charon/threads/sender.h b/src/charon/threads/sender.h new file mode 100644 index 000000000..4f42f6f9e --- /dev/null +++ b/src/charon/threads/sender.h @@ -0,0 +1,74 @@ +/** + * @file sender.h + * + * @brief Interface of sender_t. + * + */ + +/* + * Copyright (C) 2005-2007 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SENDER_H_ +#define SENDER_H_ + +typedef struct sender_t sender_t; + +#include +#include + +/** + * @brief Thread responsible for sending packets over the socket. + * + * @b Constructors: + * - sender_create() + * + * @ingroup threads + */ +struct sender_t { + + /** + * @brief Send a packet over the network. + * + * This function is non blocking and adds the packet to a queue. + * Whenever the sender thread things it's good to send the packet, + * it'll do so. + * + * @param this calling object + * @param packet packet to send + */ + void (*send) (sender_t *this, packet_t *packet); + + /** + * @brief Destroys a sender object. + * + * @param this calling object + */ + void (*destroy) (sender_t *this); +}; + +/** + * @brief Create the sender thread. + * + * The thread will start to work, getting packets + * from its queue and sends them out. + * + * @return created sender object + * + * @ingroup threads + */ +sender_t * sender_create(void); + +#endif /*SENDER_H_*/ diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c new file mode 100755 index 000000000..a9074debb --- /dev/null +++ b/src/charon/threads/stroke_interface.c @@ -0,0 +1,1456 @@ +/** + * @file stroke.c + * + * @brief Implementation of stroke_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stroke_interface.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IKE_PORT 500 +#define PATH_BUF 256 + + +struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET}; + + +typedef struct private_stroke_t private_stroke_t; + +/** + * Private data of an stroke_t object. + */ +struct private_stroke_t { + + /** + * Public part of stroke_t object. + */ + stroke_t public; + + /** + * Output stream (stroke console) + */ + FILE *out; + + /** + * Unix socket to listen for strokes + */ + int socket; + + /** + * Thread which reads from the Socket + */ + pthread_t assigned_thread; +}; + +/** + * Helper function which corrects the string pointers + * in a stroke_msg_t. Strings in a stroke_msg sent over "wire" + * contains RELATIVE addresses (relative to the beginning of the + * stroke_msg). They must be corrected if they reach our address + * space... + */ +static void pop_string(stroke_msg_t *msg, char **string) +{ + if (*string == NULL) + return; + + /* check for sanity of string pointer and string */ + if (string < (char**)msg + || string > (char**)msg + sizeof(stroke_msg_t) + || (unsigned long)*string < (unsigned long)((char*)msg->buffer - (char*)msg) + || (unsigned long)*string > msg->length) + { + *string = "(invalid pointer in stroke msg)"; + } + else + { + *string = (char*)msg + (unsigned long)*string; + } +} + +/** + * Load end entitity certificate + */ +static x509_t* load_end_certificate(const char *filename, identification_t **idp) +{ + char path[PATH_BUF]; + x509_t *cert; + + if (*filename == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%s", filename); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename); + } + + cert = x509_create_from_file(path, "end entity"); + + if (cert) + { + identification_t *id = *idp; + identification_t *subject = cert->get_subject(cert); + + err_t ugh = cert->is_valid(cert, NULL); + + if (ugh != NULL) + { + DBG1(DBG_CFG, "warning: certificate %s", ugh); + } + if (!id->equals(id, subject) && !cert->equals_subjectAltName(cert, id)) + { + id->destroy(id); + id = subject; + *idp = id->clone(id); + } + return charon->credentials->add_end_certificate(charon->credentials, cert); + } + return NULL; +} + +/** + * Load ca certificate + */ +static x509_t* load_ca_certificate(const char *filename) +{ + char path[PATH_BUF]; + x509_t *cert; + + if (*filename == '/') + { + /* absolute path name */ + snprintf(path, sizeof(path), "%s", filename); + } + else + { + /* relative path name */ + snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename); + } + + cert = x509_create_from_file(path, "ca"); + + if (cert) + { + if (cert->is_ca(cert)) + { + return charon->credentials->add_auth_certificate(charon->credentials, cert, AUTH_CA); + } + else + { + DBG1(DBG_CFG, " CA basic constraints flag not set, cert discarded"); + cert->destroy(cert); + } + } + return NULL; +} + +/** + * Add a connection to the configuration list + */ +static void stroke_add_conn(stroke_msg_t *msg, FILE *out) +{ + connection_t *connection; + policy_t *policy; + identification_t *my_id, *other_id; + identification_t *my_ca = NULL; + identification_t *other_ca = NULL; + bool my_ca_same = FALSE; + bool other_ca_same =FALSE; + host_t *my_host, *other_host, *my_subnet, *other_subnet; + host_t *my_vip = NULL, *other_vip = NULL; + proposal_t *proposal; + traffic_selector_t *my_ts, *other_ts; + char *interface; + + pop_string(msg, &msg->add_conn.name); + pop_string(msg, &msg->add_conn.me.address); + pop_string(msg, &msg->add_conn.other.address); + pop_string(msg, &msg->add_conn.me.subnet); + pop_string(msg, &msg->add_conn.other.subnet); + pop_string(msg, &msg->add_conn.me.sourceip); + pop_string(msg, &msg->add_conn.other.sourceip); + pop_string(msg, &msg->add_conn.me.id); + pop_string(msg, &msg->add_conn.other.id); + pop_string(msg, &msg->add_conn.me.cert); + pop_string(msg, &msg->add_conn.other.cert); + pop_string(msg, &msg->add_conn.me.ca); + pop_string(msg, &msg->add_conn.other.ca); + pop_string(msg, &msg->add_conn.me.updown); + pop_string(msg, &msg->add_conn.other.updown); + pop_string(msg, &msg->add_conn.algorithms.ike); + pop_string(msg, &msg->add_conn.algorithms.esp); + + DBG1(DBG_CFG, "received stroke: add connection '%s'", msg->add_conn.name); + + DBG2(DBG_CFG, "conn %s", msg->add_conn.name); + DBG2(DBG_CFG, " left=%s", msg->add_conn.me.address); + DBG2(DBG_CFG, " right=%s", msg->add_conn.other.address); + DBG2(DBG_CFG, " leftsubnet=%s", msg->add_conn.me.subnet); + DBG2(DBG_CFG, " rightsubnet=%s", msg->add_conn.other.subnet); + DBG2(DBG_CFG, " leftsourceip=%s", msg->add_conn.me.sourceip); + DBG2(DBG_CFG, " rightsourceip=%s", msg->add_conn.other.sourceip); + DBG2(DBG_CFG, " leftid=%s", msg->add_conn.me.id); + DBG2(DBG_CFG, " rightid=%s", msg->add_conn.other.id); + DBG2(DBG_CFG, " leftcert=%s", msg->add_conn.me.cert); + DBG2(DBG_CFG, " rightcert=%s", msg->add_conn.other.cert); + DBG2(DBG_CFG, " leftca=%s", msg->add_conn.me.ca); + DBG2(DBG_CFG, " rightca=%s", msg->add_conn.other.ca); + DBG2(DBG_CFG, " ike=%s", msg->add_conn.algorithms.ike); + DBG2(DBG_CFG, " esp=%s", msg->add_conn.algorithms.esp); + + my_host = msg->add_conn.me.address? + host_create_from_string(msg->add_conn.me.address, IKE_PORT) : NULL; + if (my_host == NULL) + { + DBG1(DBG_CFG, "invalid host: %s\n", msg->add_conn.me.address); + return; + } + + other_host = msg->add_conn.other.address ? + host_create_from_string(msg->add_conn.other.address, IKE_PORT) : NULL; + if (other_host == NULL) + { + DBG1(DBG_CFG, "invalid host: %s\n", msg->add_conn.other.address); + my_host->destroy(my_host); + return; + } + + interface = charon->kernel_interface->get_interface(charon->kernel_interface, + other_host); + if (interface) + { + stroke_end_t tmp_end; + host_t *tmp_host; + + DBG2(DBG_CFG, "left is other host, swapping ends\n"); + + tmp_host = my_host; + my_host = other_host; + other_host = tmp_host; + + tmp_end = msg->add_conn.me; + msg->add_conn.me = msg->add_conn.other; + msg->add_conn.other = tmp_end; + free(interface); + } + if (!interface) + { + interface = charon->kernel_interface->get_interface( + charon->kernel_interface, my_host); + if (!interface) + { + DBG1(DBG_CFG, "left nor right host is our side, aborting\n"); + goto destroy_hosts; + } + free(interface); + } + + my_id = identification_create_from_string(msg->add_conn.me.id ? + msg->add_conn.me.id : msg->add_conn.me.address); + if (my_id == NULL) + { + DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.me.id); + goto destroy_hosts; + } + + other_id = identification_create_from_string(msg->add_conn.other.id ? + msg->add_conn.other.id : msg->add_conn.other.address); + if (other_id == NULL) + { + DBG1(DBG_CFG, "invalid ID: %s\n", msg->add_conn.other.id); + my_id->destroy(my_id); + goto destroy_hosts; + } + + my_subnet = host_create_from_string(msg->add_conn.me.subnet ? + msg->add_conn.me.subnet : msg->add_conn.me.address, IKE_PORT); + if (my_subnet == NULL) + { + DBG1(DBG_CFG, "invalid subnet: %s\n", msg->add_conn.me.subnet); + goto destroy_ids; + } + + other_subnet = host_create_from_string(msg->add_conn.other.subnet ? + msg->add_conn.other.subnet : msg->add_conn.other.address, IKE_PORT); + if (other_subnet == NULL) + { + DBG1(DBG_CFG, "invalid subnet: %s\n", msg->add_conn.me.subnet); + my_subnet->destroy(my_subnet); + goto destroy_ids; + } + + if (msg->add_conn.me.virtual_ip) + { + my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0); + } + other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0); + + if (msg->add_conn.me.tohost) + { + my_ts = traffic_selector_create_dynamic(msg->add_conn.me.protocol, + my_host->get_family(my_host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE, + msg->add_conn.me.port ? msg->add_conn.me.port : 0, + msg->add_conn.me.port ? msg->add_conn.me.port : 65535); + } + else + { + my_ts = traffic_selector_create_from_subnet(my_subnet, + msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 0, + msg->add_conn.me.protocol, msg->add_conn.me.port); + } + my_subnet->destroy(my_subnet); + + if (msg->add_conn.other.tohost) + { + other_ts = traffic_selector_create_dynamic(msg->add_conn.other.protocol, + other_host->get_family(other_host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE, + msg->add_conn.other.port ? msg->add_conn.other.port : 0, + msg->add_conn.other.port ? msg->add_conn.other.port : 65535); + } + else + { + other_ts = traffic_selector_create_from_subnet(other_subnet, + msg->add_conn.other.subnet ? msg->add_conn.other.subnet_mask : 0, + msg->add_conn.other.protocol, msg->add_conn.other.port); + } + other_subnet->destroy(other_subnet); + + if (msg->add_conn.me.ca) + { + if (streq(msg->add_conn.me.ca, "%same")) + { + my_ca_same = TRUE; + } + else + { + my_ca = identification_create_from_string(msg->add_conn.me.ca); + } + } + if (msg->add_conn.other.ca) + { + if (streq(msg->add_conn.other.ca, "%same")) + { + other_ca_same = TRUE; + } + else + { + other_ca = identification_create_from_string(msg->add_conn.other.ca); + } + } + if (msg->add_conn.me.cert) + { + x509_t *cert = load_end_certificate(msg->add_conn.me.cert, &my_id); + + if (my_ca == NULL && !my_ca_same && cert) + { + identification_t *issuer = cert->get_issuer(cert); + + my_ca = issuer->clone(issuer); + } + } + if (msg->add_conn.other.cert) + { + x509_t *cert = load_end_certificate(msg->add_conn.other.cert, &other_id); + + if (other_ca == NULL && !other_ca_same && cert) + { + identification_t *issuer = cert->get_issuer(cert); + + other_ca = issuer->clone(issuer); + } + } + if (other_ca_same && my_ca) + { + other_ca = my_ca->clone(my_ca); + } + else if (my_ca_same && other_ca) + { + my_ca = other_ca->clone(other_ca); + } + if (my_ca == NULL) + { + my_ca = identification_create_from_string("%any"); + } + if (other_ca == NULL) + { + other_ca = identification_create_from_string("%any"); + } + DBG2(DBG_CFG, " my ca: '%D'", my_ca); + DBG2(DBG_CFG, " other ca:'%D'", other_ca); + DBG2(DBG_CFG, " updown: '%s'", msg->add_conn.me.updown); + + connection = connection_create(msg->add_conn.name, + msg->add_conn.ikev2, + msg->add_conn.me.sendcert, + msg->add_conn.other.sendcert, + my_host, other_host, + msg->add_conn.dpd.delay, + msg->add_conn.rekey.reauth, + msg->add_conn.rekey.tries, + msg->add_conn.rekey.ike_lifetime, + msg->add_conn.rekey.ike_lifetime - msg->add_conn.rekey.margin, + msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100); + + if (msg->add_conn.algorithms.ike) + { + char *proposal_string; + char *strict = msg->add_conn.algorithms.ike + strlen(msg->add_conn.algorithms.ike) - 1; + + if (*strict == '!') + *strict = '\0'; + else + strict = NULL; + + while ((proposal_string = strsep(&msg->add_conn.algorithms.ike, ","))) + { + proposal = proposal_create_from_string(PROTO_IKE, proposal_string); + if (proposal == NULL) + { + DBG1(DBG_CFG, "invalid IKE proposal string: %s", proposal_string); + my_id->destroy(my_id); + other_id->destroy(other_id); + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); + my_ca->destroy(my_ca); + other_ca->destroy(other_ca); + connection->destroy(connection); + return; + } + connection->add_proposal(connection, proposal); + } + if (!strict) + { + proposal = proposal_create_default(PROTO_IKE); + connection->add_proposal(connection, proposal); + } + } + else + { + proposal = proposal_create_default(PROTO_IKE); + connection->add_proposal(connection, proposal); + } + + policy = policy_create(msg->add_conn.name, my_id, other_id, my_vip, other_vip, + msg->add_conn.auth_method, msg->add_conn.eap_type, + msg->add_conn.rekey.ipsec_lifetime, + msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin, + msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100, + msg->add_conn.me.updown, msg->add_conn.me.hostaccess, + msg->add_conn.mode, msg->add_conn.dpd.action); + policy->add_my_traffic_selector(policy, my_ts); + policy->add_other_traffic_selector(policy, other_ts); + policy->add_authorities(policy, my_ca, other_ca); + + if (msg->add_conn.algorithms.esp) + { + char *proposal_string; + char *strict = msg->add_conn.algorithms.esp + strlen(msg->add_conn.algorithms.esp) - 1; + + if (*strict == '!') + *strict = '\0'; + else + strict = NULL; + + while ((proposal_string = strsep(&msg->add_conn.algorithms.esp, ","))) + { + proposal = proposal_create_from_string(PROTO_ESP, proposal_string); + if (proposal == NULL) + { + DBG1(DBG_CFG, "invalid ESP proposal string: %s", proposal_string); + policy->destroy(policy); + connection->destroy(connection); + return; + } + policy->add_proposal(policy, proposal); + } + if (!strict) + { + proposal = proposal_create_default(PROTO_ESP); + policy->add_proposal(policy, proposal); + } + } + else + { + proposal = proposal_create_default(PROTO_ESP); + policy->add_proposal(policy, proposal); + } + + /* add to global connection list */ + charon->connections->add_connection(charon->connections, connection); + DBG1(DBG_CFG, "added connection '%s': %H[%D]...%H[%D]", + msg->add_conn.name, my_host, my_id, other_host, other_id); + /* add to global policy list */ + charon->policies->add_policy(charon->policies, policy); + + return; + + /* mopping up after parsing errors */ + +destroy_ids: + my_id->destroy(my_id); + other_id->destroy(other_id); + +destroy_hosts: + my_host->destroy(my_host); + other_host->destroy(other_host); +} + +/** + * Delete a connection from the list + */ +static void stroke_del_conn(stroke_msg_t *msg, FILE *out) +{ + status_t status; + + pop_string(msg, &(msg->del_conn.name)); + DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name); + + status = charon->connections->delete_connection(charon->connections, + msg->del_conn.name); + charon->policies->delete_policy(charon->policies, msg->del_conn.name); + if (status == SUCCESS) + { + fprintf(out, "deleted connection '%s'\n", msg->del_conn.name); + } + else + { + fprintf(out, "no connection named '%s'\n", msg->del_conn.name); + } +} + +/** + * initiate a connection by name + */ +static void stroke_initiate(stroke_msg_t *msg, FILE *out) +{ + initiate_job_t *job; + connection_t *connection; + policy_t *policy; + ike_sa_t *init_ike_sa = NULL; + signal_t signal; + + pop_string(msg, &(msg->initiate.name)); + DBG1(DBG_CFG, "received stroke: initiate '%s'", msg->initiate.name); + + connection = charon->connections->get_connection_by_name(charon->connections, + msg->initiate.name); + if (connection == NULL) + { + if (msg->output_verbosity >= 0) + { + fprintf(out, "no connection named '%s'\n", msg->initiate.name); + } + return; + } + if (!connection->is_ikev2(connection)) + { + connection->destroy(connection); + return; + } + + policy = charon->policies->get_policy_by_name(charon->policies, + msg->initiate.name); + if (policy == NULL) + { + if (msg->output_verbosity >= 0) + { + fprintf(out, "no policy named '%s'\n", msg->initiate.name); + } + connection->destroy(connection); + return; + } + + job = initiate_job_create(connection, policy); + charon->bus->set_listen_state(charon->bus, TRUE); + charon->job_queue->add(charon->job_queue, (job_t*)job); + while (TRUE) + { + level_t level; + int thread; + ike_sa_t *ike_sa; + char* format; + va_list args; + + signal = charon->bus->listen(charon->bus, &level, &thread, &ike_sa, &format, &args); + + if ((init_ike_sa == NULL || ike_sa == init_ike_sa) && + level <= msg->output_verbosity) + { + if (vfprintf(out, format, args) < 0 || + fprintf(out, "\n") < 0 || + fflush(out)) + { + charon->bus->set_listen_state(charon->bus, FALSE); + break; + } + } + + switch (signal) + { + case CHILD_UP_SUCCESS: + case CHILD_UP_FAILED: + case IKE_UP_FAILED: + if (ike_sa == init_ike_sa) + { + charon->bus->set_listen_state(charon->bus, FALSE); + return; + } + continue; + case CHILD_UP_START: + case IKE_UP_START: + if (init_ike_sa == NULL) + { + init_ike_sa = ike_sa; + } + continue; + default: + continue; + } + } +} + +/** + * route/unroute a policy (install SPD entries) + */ +static void stroke_route(stroke_msg_t *msg, FILE *out, bool route) +{ + route_job_t *job; + connection_t *connection; + policy_t *policy; + + pop_string(msg, &(msg->route.name)); + DBG1(DBG_CFG, "received stroke: %s '%s'", + route ? "route" : "unroute", msg->route.name); + + /* we wouldn't need a connection, but we only want to route policies + * whose connections are keyexchange=ikev2. */ + connection = charon->connections->get_connection_by_name(charon->connections, + msg->route.name); + if (connection == NULL) + { + fprintf(out, "no connection named '%s'\n", msg->route.name); + return; + } + if (!connection->is_ikev2(connection)) + { + connection->destroy(connection); + return; + } + + policy = charon->policies->get_policy_by_name(charon->policies, + msg->route.name); + if (policy == NULL) + { + fprintf(out, "no policy named '%s'\n", msg->route.name); + connection->destroy(connection); + return; + } + fprintf(out, "%s policy '%s'\n", + route ? "routing" : "unrouting", msg->route.name); + job = route_job_create(connection, policy, route); + charon->job_queue->add(charon->job_queue, (job_t*)job); +} + +/** + * terminate a connection by name + */ +static void stroke_terminate(stroke_msg_t *msg, FILE *out) +{ + char *string, *pos = NULL, *name = NULL; + u_int32_t id = 0; + bool child; + int len; + status_t status = SUCCESS;; + ike_sa_t *ike_sa; + + pop_string(msg, &(msg->terminate.name)); + string = msg->terminate.name; + DBG1(DBG_CFG, "received stroke: terminate '%s'", string); + + len = strlen(string); + if (len < 1) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + switch (string[len-1]) + { + case '}': + child = TRUE; + pos = strchr(string, '{'); + break; + case ']': + child = FALSE; + pos = strchr(string, '['); + break; + default: + name = string; + child = FALSE; + break; + } + + if (name) + { /* must be a single name */ + DBG1(DBG_CFG, "check out by single name '%s'", name); + ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager, + name, child); + } + else if (pos == string + len - 2) + { /* must be name[] or name{} */ + string[len-2] = '\0'; + DBG1(DBG_CFG, "check out by name '%s'", string); + ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager, + string, child); + } + else + { /* must be name[123] or name{23} */ + string[len-1] = '\0'; + id = atoi(pos + 1); + if (id == 0) + { + DBG1(DBG_CFG, "error parsing string"); + return; + } + DBG1(DBG_CFG, "check out by id '%d'", id); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + id, child); + } + if (ike_sa == NULL) + { + DBG1(DBG_CFG, "no such IKE_SA found"); + return; + } + + if (!child) + { + status = ike_sa->delete(ike_sa); + } + else + { + child_sa_t *child_sa; + iterator_t *iterator = ike_sa->create_child_sa_iterator(ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if ((id && id == child_sa->get_reqid(child_sa)) || + (string && streq(string, child_sa->get_name(child_sa)))) + { + u_int32_t spi = child_sa->get_spi(child_sa, TRUE); + protocol_id_t proto = child_sa->get_protocol(child_sa); + + status = ike_sa->delete_child_sa(ike_sa, proto, spi); + break; + } + } + iterator->destroy(iterator); + } + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + return; + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); +} + +/** + * Add a ca information record to the cainfo list + */ +static void stroke_add_ca(stroke_msg_t *msg, FILE *out) +{ + x509_t *cacert; + ca_info_t *ca_info; + + pop_string(msg, &msg->add_ca.name); + pop_string(msg, &msg->add_ca.cacert); + pop_string(msg, &msg->add_ca.crluri); + pop_string(msg, &msg->add_ca.crluri2); + pop_string(msg, &msg->add_ca.ocspuri); + pop_string(msg, &msg->add_ca.ocspuri2); + + DBG1(DBG_CFG, "received stroke: add ca '%s'", msg->add_ca.name); + + DBG2(DBG_CFG, "ca %s", msg->add_ca.name); + DBG2(DBG_CFG, " cacert=%s", msg->add_ca.cacert); + DBG2(DBG_CFG, " crluri=%s", msg->add_ca.crluri); + DBG2(DBG_CFG, " crluri2=%s", msg->add_ca.crluri2); + DBG2(DBG_CFG, " ocspuri=%s", msg->add_ca.ocspuri); + DBG2(DBG_CFG, " ocspuri2=%s", msg->add_ca.ocspuri2); + + if (msg->add_ca.cacert == NULL) + { + DBG1(DBG_CFG, "missing cacert parameter\n"); + return; + } + + cacert = load_ca_certificate(msg->add_ca.cacert); + + if (cacert == NULL) + { + return; + } + ca_info = ca_info_create(msg->add_ca.name, cacert); + + if (msg->add_ca.crluri) + { + chunk_t uri = { msg->add_ca.crluri, strlen(msg->add_ca.crluri) }; + + ca_info->add_crluri(ca_info, uri); + } + if (msg->add_ca.crluri2) + { + chunk_t uri = { msg->add_ca.crluri2, strlen(msg->add_ca.crluri2) }; + + ca_info->add_crluri(ca_info, uri); + } + if (msg->add_ca.ocspuri) + { + chunk_t uri = { msg->add_ca.ocspuri, strlen(msg->add_ca.ocspuri) }; + + ca_info->add_ocspuri(ca_info, uri); + } + if (msg->add_ca.ocspuri2) + { + chunk_t uri = { msg->add_ca.ocspuri2, strlen(msg->add_ca.ocspuri2) }; + + ca_info->add_ocspuri(ca_info, uri); + } + charon->credentials->add_ca_info(charon->credentials, ca_info); + DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name); + +} + +/** + * Delete a ca information record from the cainfo list + */ +static void stroke_del_ca(stroke_msg_t *msg, FILE *out) +{ + status_t status; + + pop_string(msg, &(msg->del_ca.name)); + DBG1(DBG_CFG, "received stroke: delete ca '%s'", msg->del_ca.name); + + status = charon->credentials->release_ca_info(charon->credentials, + msg->del_ca.name); + + if (status == SUCCESS) + { + fprintf(out, "deleted ca '%s'\n", msg->del_ca.name); + } + else + { + fprintf(out, "no ca named '%s'\n", msg->del_ca.name); + } +} + +/** + * show status of daemon + */ +static void stroke_statusall(stroke_msg_t *msg, FILE *out) +{ + iterator_t *iterator; + linked_list_t *list; + host_t *host; + connection_t *connection; + policy_t *policy; + ike_sa_t *ike_sa; + char *name = NULL; + + leak_detective_status(out); + + fprintf(out, "Performance:\n"); + fprintf(out, " worker threads: %d idle of %d,", + charon->thread_pool->get_idle_threads(charon->thread_pool), + charon->thread_pool->get_pool_size(charon->thread_pool)); + fprintf(out, " job queue load: %d,", + charon->job_queue->get_count(charon->job_queue)); + fprintf(out, " scheduled events: %d\n", + charon->event_queue->get_count(charon->event_queue)); + list = charon->kernel_interface->create_address_list(charon->kernel_interface); + + fprintf(out, "Listening on %d IP addresses:\n", list->get_count(list)); + while (list->remove_first(list, (void**)&host) == SUCCESS) + { + fprintf(out, " %H\n", host); + host->destroy(host); + } + list->destroy(list); + + if (msg->status.name) + { + pop_string(msg, &(msg->status.name)); + name = msg->status.name; + } + + iterator = charon->connections->create_iterator(charon->connections); + if (iterator->get_count(iterator) > 0) + { + fprintf(out, "Connections:\n"); + } + while (iterator->iterate(iterator, (void**)&connection)) + { + if (connection->is_ikev2(connection) + && (name == NULL || streq(name, connection->get_name(connection)))) + { + fprintf(out, "%12s: %H...%H\n", + connection->get_name(connection), + connection->get_my_host(connection), + connection->get_other_host(connection)); + } + } + iterator->destroy(iterator); + + iterator = charon->policies->create_iterator(charon->policies); + if (iterator->get_count(iterator) > 0) + { + fprintf(out, "Policies:\n"); + } + while (iterator->iterate(iterator, (void**)&policy)) + { + if (name == NULL || streq(name, policy->get_name(policy))) + { + fprintf(out, "%12s: '%D'...'%D'\n", + policy->get_name(policy), + policy->get_my_id(policy), + policy->get_other_id(policy)); + } + } + iterator->destroy(iterator); + + iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager); + if (iterator->get_count(iterator) > 0) + { + fprintf(out, "Security Associations:\n"); + } + while (iterator->iterate(iterator, (void**)&ike_sa)) + { + bool ike_sa_printed = FALSE; + child_sa_t *child_sa; + iterator_t *children = ike_sa->create_child_sa_iterator(ike_sa); + + /* print IKE_SA */ + if (name == NULL || strncmp(name, ike_sa->get_name(ike_sa), strlen(name)) == 0) + { + fprintf(out, "%#K\n", ike_sa); + ike_sa_printed = TRUE; + } + + while (children->iterate(children, (void**)&child_sa)) + { + bool child_sa_match = name == NULL || + strncmp(name, child_sa->get_name(child_sa), strlen(name)) == 0; + + /* print IKE_SA if its name differs from the CHILD_SA's name */ + if (!ike_sa_printed && child_sa_match) + { + fprintf(out, "%#K\n", ike_sa); + ike_sa_printed = TRUE; + } + + /* print CHILD_SA */ + if (child_sa_match) + { + fprintf(out, "%#P\n", child_sa); + } + } + children->destroy(children); + } + iterator->destroy(iterator); +} + +/** + * show status of daemon + */ +static void stroke_status(stroke_msg_t *msg, FILE *out) +{ + iterator_t *iterator; + ike_sa_t *ike_sa; + char *name = NULL; + + if (msg->status.name) + { + pop_string(msg, &(msg->status.name)); + name = msg->status.name; + } + + iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager); + while (iterator->iterate(iterator, (void**)&ike_sa)) + { + bool ike_sa_printed = FALSE; + child_sa_t *child_sa; + iterator_t *children = ike_sa->create_child_sa_iterator(ike_sa); + + /* print IKE_SA */ + if (name == NULL || strncmp(name, ike_sa->get_name(ike_sa), strlen(name)) == 0) + { + fprintf(out, "%K\n", ike_sa); + ike_sa_printed = TRUE; + } + + while (children->iterate(children, (void**)&child_sa)) + { + bool child_sa_match = name == NULL || + strncmp(name, child_sa->get_name(child_sa), strlen(name)) == 0; + + /* print IKE_SA if its name differs from the CHILD_SA's name */ + if (!ike_sa_printed && child_sa_match) + { + fprintf(out, "%K\n", ike_sa); + ike_sa_printed = TRUE; + } + + /* print CHILD_SA */ + if (child_sa_match) + { + fprintf(out, "%P\n", child_sa); + } + } + children->destroy(children); + } + iterator->destroy(iterator); +} + +/** + * list all authority certificates matching a specified flag + */ +static void list_auth_certificates(u_int flag, const char *label, bool utc, FILE *out) +{ + bool first = TRUE; + x509_t *cert; + + iterator_t *iterator = charon->credentials->create_auth_cert_iterator(charon->credentials); + + while (iterator->iterate(iterator, (void**)&cert)) + { + if (cert->has_authority_flag(cert, flag)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 %s Certificates:\n", label); + fprintf(out, "\n"); + first = FALSE; + } + fprintf(out, "%#Q\n", cert, utc); + } + } + iterator->destroy(iterator); +} + +/** + * list various information + */ +static void stroke_list(stroke_msg_t *msg, FILE *out) +{ + iterator_t *iterator; + + if (msg->list.flags & LIST_CERTS) + { + x509_t *cert; + + iterator = charon->credentials->create_cert_iterator(charon->credentials); + if (iterator->get_count(iterator)) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 End Entity Certificates:\n"); + fprintf(out, "\n"); + } + while (iterator->iterate(iterator, (void**)&cert)) + { + fprintf(out, "%#Q", cert, msg->list.utc); + if (charon->credentials->has_rsa_private_key( + charon->credentials, cert->get_public_key(cert))) + { + fprintf(out, ", has private key"); + } + fprintf(out, "\n"); + + } + iterator->destroy(iterator); + } + if (msg->list.flags & LIST_CACERTS) + { + list_auth_certificates(AUTH_CA, "CA", msg->list.utc, out); + } + if (msg->list.flags & LIST_CAINFOS) + { + ca_info_t *ca_info; + + iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + if (iterator->get_count(iterator)) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 CA Information Records:\n"); + fprintf(out, "\n"); + } + while (iterator->iterate(iterator, (void**)&ca_info)) + { + fprintf(out, "%#W", ca_info, msg->list.utc); + } + iterator->destroy(iterator); + } + if (msg->list.flags & LIST_CRLS) + { + ca_info_t *ca_info; + bool first = TRUE; + + iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + if (ca_info->has_crl(ca_info)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 CRLs:\n"); + fprintf(out, "\n"); + first = FALSE; + } + ca_info->list_crl(ca_info, out, msg->list.utc); + } + } + iterator->destroy(iterator); + } + if (msg->list.flags & LIST_OCSPCERTS) + { + list_auth_certificates(AUTH_OCSP, "OCSP", msg->list.utc, out); + } + if (msg->list.flags & LIST_OCSP) + { + ca_info_t *ca_info; + bool first = TRUE; + + iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + + while (iterator->iterate(iterator, (void **)&ca_info)) + { + if (ca_info->has_certinfos(ca_info)) + { + if (first) + { + fprintf(out, "\n"); + fprintf(out, "List of OCSP responses:\n"); + first = FALSE; + } + fprintf(out, "\n"); + ca_info->list_certinfos(ca_info, out, msg->list.utc); + } + } + iterator->destroy(iterator); + } +} + +/** + * reread various information + */ +static void stroke_reread(stroke_msg_t *msg, FILE *out) +{ + if (msg->reread.flags & REREAD_CACERTS) + { + charon->credentials->load_ca_certificates(charon->credentials); + } + if (msg->reread.flags & REREAD_OCSPCERTS) + { + charon->credentials->load_ocsp_certificates(charon->credentials); + } + if (msg->reread.flags & REREAD_CRLS) + { + charon->credentials->load_crls(charon->credentials); + } +} + +/** + * purge various information + */ +static void stroke_purge(stroke_msg_t *msg, FILE *out) +{ + if (msg->purge.flags & PURGE_OCSP) + { + iterator_t *iterator = charon->credentials->create_cainfo_iterator(charon->credentials); + ca_info_t *ca_info; + + while (iterator->iterate(iterator, (void**)&ca_info)) + { + ca_info->purge_ocsp(ca_info); + } + iterator->destroy(iterator); + } +} + +signal_t get_signal_from_logtype(char *type) +{ + if (strcasecmp(type, "any") == 0) return SIG_ANY; + else if (strcasecmp(type, "mgr") == 0) return DBG_MGR; + else if (strcasecmp(type, "ike") == 0) return DBG_IKE; + else if (strcasecmp(type, "chd") == 0) return DBG_CHD; + else if (strcasecmp(type, "job") == 0) return DBG_JOB; + else if (strcasecmp(type, "cfg") == 0) return DBG_CFG; + else if (strcasecmp(type, "knl") == 0) return DBG_KNL; + else if (strcasecmp(type, "net") == 0) return DBG_NET; + else if (strcasecmp(type, "enc") == 0) return DBG_ENC; + else if (strcasecmp(type, "lib") == 0) return DBG_LIB; + else return -1; +} + +/** + * set the verbosity debug output + */ +static void stroke_loglevel(stroke_msg_t *msg, FILE *out) +{ + signal_t signal; + + pop_string(msg, &(msg->loglevel.type)); + DBG1(DBG_CFG, "received stroke: loglevel %d for %s", + msg->loglevel.level, msg->loglevel.type); + + signal = get_signal_from_logtype(msg->loglevel.type); + if (signal < 0) + { + fprintf(out, "invalid type (%s)!\n", msg->loglevel.type); + return; + } + + charon->outlog->set_level(charon->outlog, signal, msg->loglevel.level); + charon->syslog->set_level(charon->syslog, signal, msg->loglevel.level); +} + +/** + * process a stroke request from the socket pointed by "fd" + */ +static void stroke_process(int *fd) +{ + stroke_msg_t *msg; + u_int16_t msg_length; + ssize_t bytes_read; + FILE *out; + int strokefd = *fd; + + /* peek the length */ + bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK); + if (bytes_read != sizeof(msg_length)) + { + DBG1(DBG_CFG, "reading length of stroke message failed"); + close(strokefd); + return; + } + + /* read message */ + msg = malloc(msg_length); + bytes_read = recv(strokefd, msg, msg_length, 0); + if (bytes_read != msg_length) + { + DBG1(DBG_CFG, "reading stroke message failed: %m"); + close(strokefd); + return; + } + + out = fdopen(dup(strokefd), "w"); + if (out == NULL) + { + DBG1(DBG_CFG, "opening stroke output channel failed: %m"); + close(strokefd); + free(msg); + return; + } + + DBG3(DBG_CFG, "stroke message %b", (void*)msg, msg_length); + + switch (msg->type) + { + case STR_INITIATE: + stroke_initiate(msg, out); + break; + case STR_ROUTE: + stroke_route(msg, out, TRUE); + break; + case STR_UNROUTE: + stroke_route(msg, out, FALSE); + break; + case STR_TERMINATE: + stroke_terminate(msg, out); + break; + case STR_STATUS: + stroke_status(msg, out); + break; + case STR_STATUS_ALL: + stroke_statusall(msg, out); + break; + case STR_ADD_CONN: + stroke_add_conn(msg, out); + break; + case STR_DEL_CONN: + stroke_del_conn(msg, out); + break; + case STR_ADD_CA: + stroke_add_ca(msg, out); + break; + case STR_DEL_CA: + stroke_del_ca(msg, out); + break; + case STR_LOGLEVEL: + stroke_loglevel(msg, out); + break; + case STR_LIST: + stroke_list(msg, out); + break; + case STR_REREAD: + stroke_reread(msg, out); + break; + case STR_PURGE: + stroke_purge(msg, out); + break; + default: + DBG1(DBG_CFG, "received unknown stroke"); + } + fclose(out); + close(strokefd); + free(msg); +} + +/** + * Implementation of private_stroke_t.stroke_receive. + */ +static void stroke_receive(private_stroke_t *this) +{ + struct sockaddr_un strokeaddr; + int strokeaddrlen = sizeof(strokeaddr); + int strokefd; + int oldstate; + pthread_t thread; + + /* ignore sigpipe. writing over the pipe back to the console + * only fails if SIGPIPE is ignored. */ + signal(SIGPIPE, SIG_IGN); + + /* disable cancellation by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + while (TRUE) + { + /* wait for connections, but allow thread to terminate */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen); + pthread_setcancelstate(oldstate, NULL); + + if (strokefd < 0) + { + DBG1(DBG_CFG, "accepting stroke connection failed: %m"); + continue; + } + + /* handle request asynchronously */ + if (pthread_create(&thread, NULL, (void*(*)(void*))stroke_process, (void*)&strokefd) != 0) + { + DBG1(DBG_CFG, "failed to spawn stroke thread: %m"); + } + /* detach so the thread terminates cleanly */ + pthread_detach(thread); + } +} + +/** + * Implementation of stroke_t.destroy. + */ +static void destroy(private_stroke_t *this) +{ + pthread_cancel(this->assigned_thread); + pthread_join(this->assigned_thread, NULL); + + close(this->socket); + unlink(socket_addr.sun_path); + free(this); +} + +/* + * Described in header-file + */ +stroke_t *stroke_create() +{ + private_stroke_t *this = malloc_thing(private_stroke_t); + mode_t old; + + /* public functions */ + this->public.destroy = (void (*)(stroke_t*))destroy; + + /* set up unix socket */ + this->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (this->socket == -1) + { + DBG1(DBG_CFG, "could not create whack socket"); + free(this); + return NULL; + } + + old = umask(~S_IRWXU); + if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) + { + DBG1(DBG_CFG, "could not bind stroke socket: %m"); + close(this->socket); + free(this); + return NULL; + } + umask(old); + + if (listen(this->socket, 0) < 0) + { + DBG1(DBG_CFG, "could not listen on stroke socket: %m"); + close(this->socket); + unlink(socket_addr.sun_path); + free(this); + return NULL; + } + + /* start a thread reading from the socket */ + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))stroke_receive, this) != 0) + { + DBG1(DBG_CFG, "could not spawn stroke thread"); + close(this->socket); + unlink(socket_addr.sun_path); + free(this); + return NULL; + } + + return (&this->public); +} diff --git a/src/charon/threads/stroke_interface.h b/src/charon/threads/stroke_interface.h new file mode 100644 index 000000000..0def5167e --- /dev/null +++ b/src/charon/threads/stroke_interface.h @@ -0,0 +1,61 @@ +/** + * @file stroke.h + * + * @brief Interface of stroke_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef STROKE_INTERFACE_H_ +#define STROKE_INTERFACE_H_ + +typedef struct stroke_t stroke_t; + +/** + * @brief Stroke is a configuration and control interface which + * allows other processes to modify charons behavior. + * + * stroke_t allows config manipulation (as whack in pluto). + * Messages of type stroke_msg_t's are sent over a unix socket + * (/var/run/charon.ctl). + * + * @b Constructors: + * - stroke_create() + * + * @ingroup threads + */ +struct stroke_t { + + /** + * @brief Destroy a stroke_t instance. + * + * @param this stroke_t objec to destroy + */ + void (*destroy) (stroke_t *this); +}; + + +/** + * @brief Create the stroke interface and listen on the socket. + * + * @return stroke_t object + * + * @ingroup threads + */ +stroke_t *stroke_create(void); + +#endif /* STROKE_INTERFACE_H_ */ diff --git a/src/charon/threads/thread_pool.c b/src/charon/threads/thread_pool.c new file mode 100644 index 000000000..052b5aab9 --- /dev/null +++ b/src/charon/threads/thread_pool.c @@ -0,0 +1,181 @@ +/** + * @file thread_pool.c + * + * @brief Implementation of thread_pool_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include + +#include "thread_pool.h" + +#include +#include + + +typedef struct private_thread_pool_t private_thread_pool_t; + +/** + * @brief Private data of thread_pool_t class. + */ +struct private_thread_pool_t { + /** + * Public thread_pool_t interface. + */ + thread_pool_t public; + + /** + * Number of running threads. + */ + u_int pool_size; + + /** + * Number of threads waiting for work + */ + u_int idle_threads; + + /** + * Array of thread ids. + */ + pthread_t *threads; +} ; + +/** + * Implementation of private_thread_pool_t.process_jobs. + */ +static void process_jobs(private_thread_pool_t *this) +{ + job_t *job; + status_t status; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + DBG1(DBG_JOB, "worker thread running, thread_ID: %06u", + (int)pthread_self()); + + while (TRUE) + { + /* TODO: should be atomic, but is not mission critical */ + this->idle_threads++; + job = charon->job_queue->get(charon->job_queue); + this->idle_threads--; + + status = job->execute(job); + + if (status == DESTROY_ME) + { + job->destroy(job); + } + } +} + +/** + * Implementation of thread_pool_t.get_pool_size. + */ +static u_int get_pool_size(private_thread_pool_t *this) +{ + return this->pool_size; +} + +/** + * Implementation of thread_pool_t.get_idle_threads. + */ +static u_int get_idle_threads(private_thread_pool_t *this) +{ + return this->idle_threads; +} + +/** + * Implementation of thread_pool_t.destroy. + */ +static void destroy(private_thread_pool_t *this) +{ + int current; + /* flag thread for termination */ + for (current = 0; current < this->pool_size; current++) + { + DBG1(DBG_JOB, "cancelling worker thread #%d", current+1); + pthread_cancel(this->threads[current]); + } + + /* wait for all threads */ + for (current = 0; current < this->pool_size; current++) { + if (pthread_join(this->threads[current], NULL) == 0) + { + DBG1(DBG_JOB, "worker thread #%d terminated", current+1); + } + else + { + DBG1(DBG_JOB, "could not terminate worker thread #%d", current+1); + } + } + + /* free mem */ + free(this->threads); + free(this); +} + +/* + * Described in header. + */ +thread_pool_t *thread_pool_create(size_t pool_size) +{ + int current; + private_thread_pool_t *this = malloc_thing(private_thread_pool_t); + + /* fill in public fields */ + this->public.destroy = (void(*)(thread_pool_t*))destroy; + this->public.get_pool_size = (u_int(*)(thread_pool_t*))get_pool_size; + this->public.get_idle_threads = (u_int(*)(thread_pool_t*))get_idle_threads; + + /* initialize member */ + this->pool_size = pool_size; + this->idle_threads = 0; + this->threads = malloc(sizeof(pthread_t) * pool_size); + + /* try to create as many threads as possible, up to pool_size */ + for (current = 0; current < pool_size; current++) + { + if (pthread_create(&(this->threads[current]), NULL, + (void*(*)(void*))process_jobs, this) == 0) + { + DBG1(DBG_JOB, "created worker thread #%d", current+1); + } + else + { + /* creation failed, is it the first one? */ + if (current == 0) + { + free(this->threads); + free(this); + charon->kill(charon, "could not create any worker threads"); + } + /* not all threads could be created, but at least one :-/ */ + DBG1(DBG_JOB, "could only create %d from requested %d threads!", + current, pool_size); + this->pool_size = current; + break; + } + } + return (thread_pool_t*)this; +} diff --git a/src/charon/threads/thread_pool.h b/src/charon/threads/thread_pool.h new file mode 100644 index 000000000..8e1989bda --- /dev/null +++ b/src/charon/threads/thread_pool.h @@ -0,0 +1,87 @@ +/** + * @file thread_pool.h + * + * @brief Interface of thread_pool_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef THREAD_POOL_H_ +#define THREAD_POOL_H_ + +typedef struct thread_pool_t thread_pool_t; + +#include + +#include + +/** + * @brief A thread_pool consists of a pool of threads processing jobs from the job queue. + * + * Current implementation uses as many threads as specified in constructor. + * A more improved version would dynamically increase thread count if necessary. + * + * @b Constructors: + * - thread_pool_create() + * + * @todo Add support for dynamic thread handling + * + * @ingroup threads + */ +struct thread_pool_t { + + /** + * @brief Return currently instanciated thread count. + * + * @param thread_pool calling object + * @return size of thread pool + */ + u_int (*get_pool_size) (thread_pool_t *thread_pool); + + /** + * @brief Get the number of threads currently waiting for work. + * + * @param thread_pool calling object + * @return number of idle threads + */ + u_int (*get_idle_threads) (thread_pool_t *thread_pool); + + /** + * @brief Destroy a thread_pool_t object. + * + * Sends cancellation request to all threads and AWAITS their termination. + * + * @param thread_pool calling object + */ + void (*destroy) (thread_pool_t *thread_pool); +}; + +/** + * @brief Create the thread pool using using pool_size of threads. + * + * @param pool_size desired pool size + * @return + * - thread_pool_t object if one ore more threads could be started, or + * - NULL if no threads could be created + * + * @ingroup threads + */ +thread_pool_t *thread_pool_create(size_t pool_size); + + +#endif /*THREAD_POOL_H_*/ diff --git a/src/ipsec/Makefile.am b/src/ipsec/Makefile.am new file mode 100644 index 000000000..44964e041 --- /dev/null +++ b/src/ipsec/Makefile.am @@ -0,0 +1,16 @@ +sbin_SCRIPTS = ipsec +CLEANFILES = ipsec +dist_man8_MANS = ipsec.8 +EXTRA_DIST = ipsec.in + +ipsec : ipsec.in + sed \ + -e "s:@IPSEC_VERSION@:$(PACKAGE_VERSION):" \ + -e "s:@IPSEC_NAME@:$(PACKAGE_NAME):" \ + -e "s:@IPSEC_DISTRO@::" \ + -e "s:@IPSEC_DIR@:$(ipsecdir):" \ + -e "s:@IPSEC_SBINDIR@:$(sbindir):" \ + -e "s:@IPSEC_CONFDIR@:$(confdir):" \ + -e "s:@IPSEC_PIDDIR@:$(piddir):" \ + $< > $@ + chmod +x $@ diff --git a/src/ipsec/Makefile.in b/src/ipsec/Makefile.in new file mode 100644 index 000000000..eaf0e9d79 --- /dev/null +++ b/src/ipsec/Makefile.in @@ -0,0 +1,434 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/ipsec +DIST_COMMON = $(dist_man8_MANS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" +sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(sbin_SCRIPTS) +SOURCES = +DIST_SOURCES = +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man8_MANS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +sbin_SCRIPTS = ipsec +CLEANFILES = ipsec +dist_man8_MANS = ipsec.8 +EXTRA_DIST = ipsec.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/ipsec/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/ipsec/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-sbinSCRIPTS: $(sbin_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)" + @list='$(sbin_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ + $(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \ + else :; fi; \ + done + +uninstall-sbinSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ + rm -f "$(DESTDIR)$(sbindir)/$$f"; \ + done + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-man + +install-exec-am: install-sbinSCRIPTS + +install-info: install-info-am + +install-man: install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-man uninstall-sbinSCRIPTS + +uninstall-man: uninstall-man8 + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-man8 \ + install-sbinSCRIPTS install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am uninstall uninstall-am uninstall-info-am \ + uninstall-man uninstall-man8 uninstall-sbinSCRIPTS + + +ipsec : ipsec.in + sed \ + -e "s:@IPSEC_VERSION@:$(PACKAGE_VERSION):" \ + -e "s:@IPSEC_NAME@:$(PACKAGE_NAME):" \ + -e "s:@IPSEC_DISTRO@::" \ + -e "s:@IPSEC_DIR@:$(ipsecdir):" \ + -e "s:@IPSEC_SBINDIR@:$(sbindir):" \ + -e "s:@IPSEC_CONFDIR@:$(confdir):" \ + -e "s:@IPSEC_PIDDIR@:$(piddir):" \ + $< > $@ + chmod +x $@ +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/ipsec/ipsec.8 b/src/ipsec/ipsec.8 new file mode 100644 index 000000000..b37ac2c3a --- /dev/null +++ b/src/ipsec/ipsec.8 @@ -0,0 +1,342 @@ +.TH IPSEC 8 "9 February 2006" +.\" RCSID $Id: ipsec.8,v 1.3 2006/02/09 19:47:38 as Exp $ +.SH NAME +ipsec \- invoke IPsec utilities +.SH SYNOPSIS +.B ipsec +command [ argument ...] +.sp +.B ipsec start|update|reload|restart|stop +.sp +.B ipsec up|down|route|unroute +\fIconnectionname\fP +.sp +.B ipsec status|statusall +[ +\fIconnectionname\fP +] +.sp +.B ipsec listalgs|listpubkeys|listcerts +[ +.B \-\-utc +] +.br +.B ipsec listcacerts|listaacerts|listocspcerts +[ +.B \-\-utc +] +.br +.B ipsec listacerts|listgroups|listcainfos +[ +.B \-\-utc +] +.br +.B ipsec listcrls|listocsp|listcards|listall +[ +.B \-\-utc +] +.sp +.B ipsec rereadsecrets|rereadgroups +.br +.B ipsec rereadcacerts|rereadaacerts|rereadocspcerts +.br +.B ipsec rereadacerts|rereadcrls|rereadall +.sp +.B ipsec purgeocsp +.sp +.B ipsec +[ +.B \-\-help +] [ +.B \-\-version +] [ +.B \-\-versioncode +] [ +.B \-\-copyright +] +.br +.B ipsec +[ +.B \-\-directory +] [ +.B \-\-confdir +] +.SH DESCRIPTION +.I Ipsec +invokes any of several utilities involved in controlling the IPsec +encryption/authentication system, +running the specified +.I command +with the specified +.IR argument s +as if it had been invoked directly. +This largely eliminates possible name collisions with other software, +and also permits some centralized services. +.PP +The commands +.BR start , +.BR update , +.BR reload , +.BR restart , +and +.BR stop +are built-in and are used to control the +.BR "ipsec starter" +utility, an extremely fast replacement for the traditional +.BR ipsec +.BR setup +script. +.PP +The commands +.BR up, +.BR down, +.BR route, +.BR unroute, +.BR status, +.BR statusall, +.BR listalgs, +.BR listpubkeys, +.BR listcerts, +.BR listcacerts, +.BR listaacerts, +.BR listocspcerts, +.BR listacerts, +.BR listgroups, +.BR listcainfos, +.BR listcrls, +.BR listocsp, +.BR listcards, +.BR listall, +.BR rereadsecrets, +.BR rereadgroups, +.BR rereadcacerts, +.BR rereadaacerts, +.BR rereadocspcerts, +.BR rereadacerts, +.BR rereadcrls, +and +.BR rereadall +are also built-in and completely replace the corresponding +.BR "ipsec auto" +\-\-\fIoperation\fP" +commands. Communication with the pluto daemon happens via the +.BR "ipsec whack" +socket interface. +.PP +In particular, +.I ipsec +supplies the invoked +.I command +with a suitable PATH environment variable, +and also provides IPSEC_DIR, +IPSEC_CONFS, and IPSEC_VERSION environment variables, +containing respectively +the full pathname of the directory where the IPsec utilities are stored, +the full pathname of the directory where the configuration files live, +and the IPsec version number. +.PP +.B "ipsec start" +calls +.BR "ipsec starter" +which in turn starts \fIpluto\fR. +.PP +.B "ipsec update" +sends a \fIHUP\fR signal to +.BR "ipsec starter" +which in turn determines any changes in \fIipsec.conf\fR +and updates the configuration on the running \fIpluto\fR daemon, correspondingly. +.PP +.B "ipsec reload" +sends a \fIUSR1\fR signal to +.BR "ipsec starter" +which in turn reloads the whole configuration on the running \fIpluto\fR daemon +based on the actual \fIipsec.conf\fR. +.PP +.B "ipsec restart" +executes +.B "ipsec stop" +followed by +.BR "ipsec start". +.PP +.B "ipsec stop" +stops \fIipsec\fR by sending a \fITERM\fR signal to +.BR "ipsec starter". +.PP +.B "ipsec up" +\fIname\fP tells the \fIpluto\fP daemon to start up connection \fIname\fP. +.PP +.B "ipsec down" +\fIname\fP tells the \fIpluto\fP daemon to take down connection \fIname\fP. +.PP +.B "ipsec route" +\fIname\fP tells the \fIpluto\fP daemon to install a route for connection +\fIname\fP. +.PP +.B "ipsec unroute" +\fIname\fP tells the \fIpluto\fP daemon to take down the route for connection +\fIname\fP. +.PP +.B "ipsec status" +[ \fIname\fP ] gives concise status information either on connection +\fIname\fP or if the \fIname\fP argument is lacking, on all connections. +.PP +.B "ipsec statusall" +[ \fIname\fP ] gives detailed status information either on connection +\fIname\fP or if the \fIname\fP argument is lacking, on all connections. +.PP +.B "ipsec listalgs" +returns a list all supported IKE encryption and hash algorithms, the available +Diffie-Hellman groups, as well as all supported ESP encryption and authentication +algorithms. +.PP +.B "ipsec listpubkeys" +returns a list of RSA public keys that were either loaded in raw key format +or extracted from X.509 and|or OpenPGP certificates. +.PP +.B "ipsec listcerts" +returns a list of X.509 and|or OpenPGP certificates that were loaded locally +by the \fIpluto\fP daemon. +.PP +.B "ipsec listcacerts" +returns a list of X.509 Certification Authority (CA) certificates that were +loaded locally by the \fIpluto\fP daemon from the \fI/etc/ipsec.d/cacerts/\fP +directory or received in PKCS#7-wrapped certificate payloads via the IKE +protocol. +.PP +.B "ipsec listaacerts" +returns a list of X.509 Authorization Authority (AA) certificates that were +loaded locally by the \fIpluto\fP daemon from the \fI/etc/ipsec.d/aacerts/\fP +directory. +.PP +.B "ipsec listocspcerts" +returns a list of X.509 OCSP Signer certificates that were either loaded +locally by the \fIpluto\fP daemon from the \fI/etc/ipsec.d/ocspcerts/\fP +directory or were sent by an OCSP server. +.PP +.B "ipsec listacerts" +returns a list of X.509 Attribute certificates that were loaded locally by +the \fIpluto\fP daemon from the \fI/etc/ipsec.d/acerts/\fP directory. +.PP +.B "ipsec listgroups" +returns a list of groups that are used to define user authorization profiles. +.PP +.B "ipsec listcainfos" +returns certification authority information (CRL distribution points, OCSP URIs, +LDAP servers) that were defined by +.BR ca +sections in \fIipsec.conf\fP. +.PP +.B "ipsec listcrls" +returns a list of Certificate Revocation Lists (CRLs). +.PP +.B "ipsec listocsp" +returns revocation information fetched from OCSP servers. +.PP +.B "ipsec listcards" +returns a list of certificates residing on smartcards. +.PP +.B "ipsec listall" +returns all information generated by the list commands above. Each list command +can be called with the +\-\-url +option which displays all dates in UTC instead of local time. +.PP +.B "ipsec rereadsecrets" +flushes and rereads all secrets defined in \fIipsec.conf\fP. +.PP +.B "ipsec rereadcacerts" +reads all certificate files contained in the \fI/etc/ipsec.d/cacerts\fP +directory and adds them to \fIpluto\fP's list of Certification Authority (CA) certificates. +.PP +.B "ipsec rereadaacerts" +reads all certificate files contained in the \fI/etc/ipsec.d/aacerts\fP +directory and adds them to \fIpluto\fP's list of Authorization Authority (AA) certificates. +.PP +.B "ipsec rereadocspcerts" +reads all certificate files contained in the \fI/etc/ipsec.d/ocspcerts/\fP +directory and adds them to \fIpluto\fP's list of OCSP signer certificates. +.PP +.B "ipsec rereadacerts" +operation reads all certificate files contained in the \fI/etc/ipsec.d/acerts/\fP +directory and adds them to \fIpluto\fP's list of attribute certificates. +.PP +.B "ipsec rereadcrls" +reads all Certificate Revocation Lists (CRLs) contained in the +\fI/etc/ipsec.d/crls/\fP directory and adds them to \fIpluto\fP's list of CRLs. +.PP +.B "ipsec rereadall" +is equivalent to the execution of \fBrereadsecrets\fP, +\fBrereadcacerts\fP, \fBrereadaacerts\fP, \fBrereadocspcerts\fP, +\fBrereadacerts\fP, and \fBrereadcrls\fP. +.PP +.B "ipsec \-\-help" +lists the available commands. +Most have their own manual pages, e.g. +.IR ipsec_auto (8) +for +.IR auto . +.PP +.B "ipsec \-\-version" +outputs version information about Linux strongSwan. +A version code of the form ``U\fIxxx\fR/K\fIyyy\fR'' +indicates that the user-level utilities are version \fIxxx\fR +but the kernel portion appears to be version \fIyyy\fR +(this form is used only if the two disagree). +.PP +.B "ipsec \-\-versioncode" +outputs \fIjust\fR the version code, +with none of +.BR \-\-version 's +supporting information, +for use by scripts. +.PP +.B "ipsec \-\-copyright" +supplies boring copyright details. +.PP +.B "ipsec \-\-directory" +reports where +.I ipsec +thinks the IPsec utilities are stored. +.PP +.B "ipsec \-\-confdir" +reports where +.I ipsec +thinks the IPsec configuration files are stored. +.SH FILES +/usr/local/lib/ipsec usual utilities directory +.SH ENVIRONMENT +.PP +The following environment variables control where strongSwan finds its +components. +The +.B ipsec +command sets them if they are not already set. +.nf +.na + +IPSEC_DIR directory containing ipsec programs and utilities +IPSEC_SBINDIR directory containing \fBipsec\fP command +IPSEC_CONFDIR directory containing configuration files +IPSEC_PIDDIR directory containing PID files +IPSEC_NAME name of ipsec distribution +IPSEC_VERSION version numer of ipsec userland and kernel +IPSEC_STARTER_PID PID file for ipsec starter +IPSEC_PLUTO_PID PID file for IKEv1 keying daemon +IPSEC_CHARON_PID PID file for IKEv2 keying daemon +.ad +.fi +.SH SEE ALSO +.hy 0 +.na +ipsec.conf(5), ipsec.secrets(5), +ipsec_barf(8), +.ad +.hy +.PP +.SH HISTORY +Written for Linux FreeS/WAN + +by Henry Spencer. +Updated and extended for Linux strongSwan + +by Andreas Steffen. diff --git a/src/ipsec/ipsec.in b/src/ipsec/ipsec.in new file mode 100755 index 000000000..bd74b6f16 --- /dev/null +++ b/src/ipsec/ipsec.in @@ -0,0 +1,294 @@ +#! /bin/sh +# prefix command to run stuff from our programs directory +# Copyright (C) 1998-2002 Henry Spencer. +# Copyright (C) 2006 Andreas Steffen +# Copyright (C) 2006 Martin Willi +# +# This program 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. See . +# +# 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 General Public License +# for more details. +# +# RCSID $Id: ipsec.in,v 1.13 2006/03/09 20:09:33 as Exp $ + +# name and version of the ipsec implementation +IPSEC_NAME="@IPSEC_NAME@" +IPSEC_VERSION="U@IPSEC_VERSION@/K`uname -r`" + +# where the private directory and the config files are +IPSEC_DIR="@IPSEC_DIR@" +IPSEC_SBINDIR="@IPSEC_SBINDIR@" +IPSEC_CONFDIR="@IPSEC_CONFDIR@" +IPSEC_PIDDIR="@IPSEC_PIDDIR@" + +IPSEC_STARTER_PID="${IPSEC_PIDDIR}/starter.pid" +IPSEC_PLUTO_PID="${IPSEC_PIDDIR}/pluto.pid" +IPSEC_CHARON_PID="${IPSEC_PIDDIR}/charon.pid" + +IPSEC_WHACK="${IPSEC_DIR}/whack" +IPSEC_STROKE="${IPSEC_DIR}/stroke" +IPSEC_STARTER="${IPSEC_DIR}/starter" + +export IPSEC_DIR IPSEC_SBINDIR IPSEC_CONFDIR IPSEC_PIDDIR IPSEC_VERSION IPSEC_NAME IPSEC_STARTER_PID IPSEC_PLUTO_PID IPSEC_CHARON_PID + +IPSEC_DISTRO="Institute for Internet Technologies and Applications\n + University of Applied Sciences Rapperswil, Switzerland" + +case "$1" in +'') + echo "Usage: ipsec command argument ..." + echo "Use --help for list of commands, or see ipsec(8) manual page" + echo "or the $IPSEC_NAME documentation for names of the common ones." + echo "Most have their own manual pages, e.g. ipsec_auto(8)." + echo "See for more general info." + exit 0 + ;; +--help) + echo "Usage: ipsec command argument ..." + echo "where command is one of:" + echo " start|restart arguments..." + echo " update|reload|stop" + echo " up|down|route|unroute " + echo " status|statusall []" + echo " ready" + echo " listalgs|listpubkeys|listcerts [--utc]" + echo " listcacerts|listaacerts|listocspcerts [--utc]" + echo " listacerts|listgroups|listcainfos [--utc]" + echo " listcrls|listocsp|listcards|listall [--utc]" + echo " rereadsecrets|rereadgroups" + echo " rereadcacerts|rereadaacerts|rereadocspcerts" + echo " rereadacerts|rereadcrls|rereadall" + echo " purgeocsp" + echo " scencrypt|scdecrypt [--inbase ] [--outbase ] [--keyid ]" + echo " barf" + echo " openac" + echo " pluto" + echo " scepclient" + echo " secrets" + echo " starter" + echo " version" + echo " whack" + echo " stoke" + echo + echo "Some of these functions have their own manual pages, e.g. ipsec_scepclient(8)." + exit 0 + ;; +--versioncode) + echo "$IPSEC_VERSION" + exit 0 + ;; +--copyright) + set _copyright + # and fall through, invoking "ipsec _copyright" + ;; +--directory) + echo "$IPSEC_DIR" + exit 0 + ;; +--confdir) + echo "$IPSEC_CONFDIR" + exit 0 + ;; +down) + shift + if [ "$#" -ne 1 ] + then + echo "Usage: ipsec down " + exit 1 + fi + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK --name "$1" --terminate + fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE down "$1" + fi + exit 0 + ;; +listalgs|listpubkeys|listaacerts|\ +listacerts|listgroups|\listcards|\ +rereadsecrets|rereadgroups|\ +rereadaacerts|rereadacerts) + op="$1" + shift + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK "$@" "--$op" + fi + exit 0 + ;; +listcerts|listcacerts|listocspcerts|\ +listcainfos|listcrls|listocsp|listall|\ +rereadcacerts|rereadocspcerts|rereadcrls|\ +rereadall|purgeocsp) + op="$1" + shift + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK "$@" "--$op" + fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE "$op" "$@" + fi + exit 0 + ;; +ready) + shift + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK --listen + fi + exit 0 + ;; +reload) + if test -e $IPSEC_STARTER_PID + then + echo "Reloading strongSwan IPsec configuration..." >&2 + kill -s USR1 `cat $IPSEC_STARTER_PID` + else + echo "ipsec starter is not running" >&2 + fi + exit 0 + ;; +restart) + $IPSEC_SBINDIR/ipsec stop + sleep 2 + shift + $IPSEC_SBINDIR/ipsec start "$@" + exit 0 + ;; +route|unroute) + op="$1" + shift + if [ "$#" -ne 1 ] + then + echo "Usage: ipsec $op " + exit 1 + fi + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK --name "$1" "--$op" + fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE "$op" "$1" + fi + exit 0 + ;; +scencrypt|scdecrypt) + op="$1" + shift + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK "--$op" "$@" + fi + exit 0 + ;; +secrets) + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK --rereadsecrets + fi + exit 0 + ;; +start) + shift + exec $IPSEC_STARTER "$@" + ;; +status|statusall) + op="$1" + shift + if test $# -eq 0 + then + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK "--$op" + fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE "$op" + fi + else + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK --name "$1" "--$op" + fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE "$op" "$1" + fi + fi + exit 0 + ;; +stop) + if test -e $IPSEC_STARTER_PID + then + echo "Stopping strongSwan IPsec..." >&2 + kill `cat $IPSEC_STARTER_PID` + else + echo "ipsec starter is not running" >&2 + fi + exit 0 + ;; +up) + shift + if [ "$#" -ne 1 ] + then + echo "Usage: ipsec up " + exit 1 + fi + if test -e $IPSEC_PLUTO_PID + then + $IPSEC_WHACK --name "$1" --initiate + fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE up "$1" + fi + exit 0 + ;; +update) + if test -e $IPSEC_STARTER_PID + then + echo "Updating strongSwan IPsec configuration..." >&2 + kill -s HUP `cat $IPSEC_STARTER_PID` + else + echo "ipsec starter is not running" >&2 + fi + exit 0 + ;; +version|--version) + echo "Linux $IPSEC_NAME $IPSEC_VERSION" + echo "See \`ipsec --copyright' for copyright information." + echo $IPSEC_DISTRO + exit 0 + ;; +--*) + echo "$0: unknown option \`$1' (perhaps command name was omitted?)" >&2 + exit 1 + ;; +esac + +cmd="$1" +shift + +path="$IPSEC_DIR/$cmd" + +if test ! -x "$path" +then + path="$IPSEC_DIR/$cmd" + if test ! -x "$path" + then + echo "$0: unknown IPsec command \`$cmd' (\`ipsec --help' for list)" >&2 + exit 1 + fi +fi + +exec $path "$@" diff --git a/src/libcrypto/Makefile.am b/src/libcrypto/Makefile.am new file mode 100644 index 000000000..23066033d --- /dev/null +++ b/src/libcrypto/Makefile.am @@ -0,0 +1,11 @@ +noinst_LIBRARIES = libcrypto.a +libcrypto_a_SOURCES = \ +libaes/aes_xcbc_mac.c libaes/aes_cbc.c libaes/aes_xcbc_mac.h libaes/aes_cbc.h libaes/aes.c libaes/aes.h \ +include/md32_common.h include/cbc_generic.h include/hmac_generic.h libblowfish/bf_skey.c libblowfish/blowfish.h \ +libblowfish/bf_pi.h libblowfish/bf_locl.h libblowfish/bf_enc.c libsha2/hmac_sha2.c libsha2/sha2.h libsha2/hmac_sha2.h \ +libsha2/sha2.c libserpent/serpent_cbc.c libserpent/serpent_cbc.h libserpent/serpent.c libserpent/serpent.h \ +libtwofish/twofish_cbc.h libtwofish/twofish_cbc.c libtwofish/twofish.c libtwofish/twofish.h libdes/des_enc.c \ +libdes/podd.h libdes/sk.h libdes/set_key.c libdes/speed.c libdes/fcrypt_b.c libdes/fcrypt.c libdes/destest.c \ +libdes/spr.h libdes/cbc_enc.c libdes/ecb_enc.c libdes/des_opts.c libdes/des_locl.h libdes/des_ver.h libdes/des.h + +INCLUDES = -I$(top_srcdir)/src/libcrypto/include diff --git a/src/libcrypto/Makefile.in b/src/libcrypto/Makefile.in new file mode 100644 index 000000000..63b7d4907 --- /dev/null +++ b/src/libcrypto/Makefile.in @@ -0,0 +1,761 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/libcrypto +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +libcrypto_a_AR = $(AR) $(ARFLAGS) +libcrypto_a_LIBADD = +am_libcrypto_a_OBJECTS = aes_xcbc_mac.$(OBJEXT) aes_cbc.$(OBJEXT) \ + aes.$(OBJEXT) bf_skey.$(OBJEXT) bf_enc.$(OBJEXT) \ + hmac_sha2.$(OBJEXT) sha2.$(OBJEXT) serpent_cbc.$(OBJEXT) \ + serpent.$(OBJEXT) twofish_cbc.$(OBJEXT) twofish.$(OBJEXT) \ + des_enc.$(OBJEXT) set_key.$(OBJEXT) speed.$(OBJEXT) \ + fcrypt_b.$(OBJEXT) fcrypt.$(OBJEXT) destest.$(OBJEXT) \ + cbc_enc.$(OBJEXT) ecb_enc.$(OBJEXT) des_opts.$(OBJEXT) +libcrypto_a_OBJECTS = $(am_libcrypto_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libcrypto_a_SOURCES) +DIST_SOURCES = $(libcrypto_a_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +noinst_LIBRARIES = libcrypto.a +libcrypto_a_SOURCES = \ +libaes/aes_xcbc_mac.c libaes/aes_cbc.c libaes/aes_xcbc_mac.h libaes/aes_cbc.h libaes/aes.c libaes/aes.h \ +include/md32_common.h include/cbc_generic.h include/hmac_generic.h libblowfish/bf_skey.c libblowfish/blowfish.h \ +libblowfish/bf_pi.h libblowfish/bf_locl.h libblowfish/bf_enc.c libsha2/hmac_sha2.c libsha2/sha2.h libsha2/hmac_sha2.h \ +libsha2/sha2.c libserpent/serpent_cbc.c libserpent/serpent_cbc.h libserpent/serpent.c libserpent/serpent.h \ +libtwofish/twofish_cbc.h libtwofish/twofish_cbc.c libtwofish/twofish.c libtwofish/twofish.h libdes/des_enc.c \ +libdes/podd.h libdes/sk.h libdes/set_key.c libdes/speed.c libdes/fcrypt_b.c libdes/fcrypt.c libdes/destest.c \ +libdes/spr.h libdes/cbc_enc.c libdes/ecb_enc.c libdes/des_opts.c libdes/des_locl.h libdes/des_ver.h libdes/des.h + +INCLUDES = -I$(top_srcdir)/src/libcrypto/include +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libcrypto/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libcrypto/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libcrypto.a: $(libcrypto_a_OBJECTS) $(libcrypto_a_DEPENDENCIES) + -rm -f libcrypto.a + $(libcrypto_a_AR) libcrypto.a $(libcrypto_a_OBJECTS) $(libcrypto_a_LIBADD) + $(RANLIB) libcrypto.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aes_cbc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aes_xcbc_mac.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bf_enc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bf_skey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cbc_enc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/des_enc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/des_opts.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/destest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecb_enc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fcrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fcrypt_b.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac_sha2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serpent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serpent_cbc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_key.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/speed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/twofish.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/twofish_cbc.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +aes_xcbc_mac.o: libaes/aes_xcbc_mac.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes_xcbc_mac.o -MD -MP -MF "$(DEPDIR)/aes_xcbc_mac.Tpo" -c -o aes_xcbc_mac.o `test -f 'libaes/aes_xcbc_mac.c' || echo '$(srcdir)/'`libaes/aes_xcbc_mac.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes_xcbc_mac.Tpo" "$(DEPDIR)/aes_xcbc_mac.Po"; else rm -f "$(DEPDIR)/aes_xcbc_mac.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libaes/aes_xcbc_mac.c' object='aes_xcbc_mac.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes_xcbc_mac.o `test -f 'libaes/aes_xcbc_mac.c' || echo '$(srcdir)/'`libaes/aes_xcbc_mac.c + +aes_xcbc_mac.obj: libaes/aes_xcbc_mac.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes_xcbc_mac.obj -MD -MP -MF "$(DEPDIR)/aes_xcbc_mac.Tpo" -c -o aes_xcbc_mac.obj `if test -f 'libaes/aes_xcbc_mac.c'; then $(CYGPATH_W) 'libaes/aes_xcbc_mac.c'; else $(CYGPATH_W) '$(srcdir)/libaes/aes_xcbc_mac.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes_xcbc_mac.Tpo" "$(DEPDIR)/aes_xcbc_mac.Po"; else rm -f "$(DEPDIR)/aes_xcbc_mac.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libaes/aes_xcbc_mac.c' object='aes_xcbc_mac.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes_xcbc_mac.obj `if test -f 'libaes/aes_xcbc_mac.c'; then $(CYGPATH_W) 'libaes/aes_xcbc_mac.c'; else $(CYGPATH_W) '$(srcdir)/libaes/aes_xcbc_mac.c'; fi` + +aes_cbc.o: libaes/aes_cbc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes_cbc.o -MD -MP -MF "$(DEPDIR)/aes_cbc.Tpo" -c -o aes_cbc.o `test -f 'libaes/aes_cbc.c' || echo '$(srcdir)/'`libaes/aes_cbc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes_cbc.Tpo" "$(DEPDIR)/aes_cbc.Po"; else rm -f "$(DEPDIR)/aes_cbc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libaes/aes_cbc.c' object='aes_cbc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes_cbc.o `test -f 'libaes/aes_cbc.c' || echo '$(srcdir)/'`libaes/aes_cbc.c + +aes_cbc.obj: libaes/aes_cbc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes_cbc.obj -MD -MP -MF "$(DEPDIR)/aes_cbc.Tpo" -c -o aes_cbc.obj `if test -f 'libaes/aes_cbc.c'; then $(CYGPATH_W) 'libaes/aes_cbc.c'; else $(CYGPATH_W) '$(srcdir)/libaes/aes_cbc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes_cbc.Tpo" "$(DEPDIR)/aes_cbc.Po"; else rm -f "$(DEPDIR)/aes_cbc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libaes/aes_cbc.c' object='aes_cbc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes_cbc.obj `if test -f 'libaes/aes_cbc.c'; then $(CYGPATH_W) 'libaes/aes_cbc.c'; else $(CYGPATH_W) '$(srcdir)/libaes/aes_cbc.c'; fi` + +aes.o: libaes/aes.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes.o -MD -MP -MF "$(DEPDIR)/aes.Tpo" -c -o aes.o `test -f 'libaes/aes.c' || echo '$(srcdir)/'`libaes/aes.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes.Tpo" "$(DEPDIR)/aes.Po"; else rm -f "$(DEPDIR)/aes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libaes/aes.c' object='aes.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes.o `test -f 'libaes/aes.c' || echo '$(srcdir)/'`libaes/aes.c + +aes.obj: libaes/aes.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes.obj -MD -MP -MF "$(DEPDIR)/aes.Tpo" -c -o aes.obj `if test -f 'libaes/aes.c'; then $(CYGPATH_W) 'libaes/aes.c'; else $(CYGPATH_W) '$(srcdir)/libaes/aes.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes.Tpo" "$(DEPDIR)/aes.Po"; else rm -f "$(DEPDIR)/aes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libaes/aes.c' object='aes.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes.obj `if test -f 'libaes/aes.c'; then $(CYGPATH_W) 'libaes/aes.c'; else $(CYGPATH_W) '$(srcdir)/libaes/aes.c'; fi` + +bf_skey.o: libblowfish/bf_skey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bf_skey.o -MD -MP -MF "$(DEPDIR)/bf_skey.Tpo" -c -o bf_skey.o `test -f 'libblowfish/bf_skey.c' || echo '$(srcdir)/'`libblowfish/bf_skey.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bf_skey.Tpo" "$(DEPDIR)/bf_skey.Po"; else rm -f "$(DEPDIR)/bf_skey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libblowfish/bf_skey.c' object='bf_skey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bf_skey.o `test -f 'libblowfish/bf_skey.c' || echo '$(srcdir)/'`libblowfish/bf_skey.c + +bf_skey.obj: libblowfish/bf_skey.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bf_skey.obj -MD -MP -MF "$(DEPDIR)/bf_skey.Tpo" -c -o bf_skey.obj `if test -f 'libblowfish/bf_skey.c'; then $(CYGPATH_W) 'libblowfish/bf_skey.c'; else $(CYGPATH_W) '$(srcdir)/libblowfish/bf_skey.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bf_skey.Tpo" "$(DEPDIR)/bf_skey.Po"; else rm -f "$(DEPDIR)/bf_skey.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libblowfish/bf_skey.c' object='bf_skey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bf_skey.obj `if test -f 'libblowfish/bf_skey.c'; then $(CYGPATH_W) 'libblowfish/bf_skey.c'; else $(CYGPATH_W) '$(srcdir)/libblowfish/bf_skey.c'; fi` + +bf_enc.o: libblowfish/bf_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bf_enc.o -MD -MP -MF "$(DEPDIR)/bf_enc.Tpo" -c -o bf_enc.o `test -f 'libblowfish/bf_enc.c' || echo '$(srcdir)/'`libblowfish/bf_enc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bf_enc.Tpo" "$(DEPDIR)/bf_enc.Po"; else rm -f "$(DEPDIR)/bf_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libblowfish/bf_enc.c' object='bf_enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bf_enc.o `test -f 'libblowfish/bf_enc.c' || echo '$(srcdir)/'`libblowfish/bf_enc.c + +bf_enc.obj: libblowfish/bf_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bf_enc.obj -MD -MP -MF "$(DEPDIR)/bf_enc.Tpo" -c -o bf_enc.obj `if test -f 'libblowfish/bf_enc.c'; then $(CYGPATH_W) 'libblowfish/bf_enc.c'; else $(CYGPATH_W) '$(srcdir)/libblowfish/bf_enc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/bf_enc.Tpo" "$(DEPDIR)/bf_enc.Po"; else rm -f "$(DEPDIR)/bf_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libblowfish/bf_enc.c' object='bf_enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bf_enc.obj `if test -f 'libblowfish/bf_enc.c'; then $(CYGPATH_W) 'libblowfish/bf_enc.c'; else $(CYGPATH_W) '$(srcdir)/libblowfish/bf_enc.c'; fi` + +hmac_sha2.o: libsha2/hmac_sha2.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hmac_sha2.o -MD -MP -MF "$(DEPDIR)/hmac_sha2.Tpo" -c -o hmac_sha2.o `test -f 'libsha2/hmac_sha2.c' || echo '$(srcdir)/'`libsha2/hmac_sha2.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hmac_sha2.Tpo" "$(DEPDIR)/hmac_sha2.Po"; else rm -f "$(DEPDIR)/hmac_sha2.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libsha2/hmac_sha2.c' object='hmac_sha2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hmac_sha2.o `test -f 'libsha2/hmac_sha2.c' || echo '$(srcdir)/'`libsha2/hmac_sha2.c + +hmac_sha2.obj: libsha2/hmac_sha2.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hmac_sha2.obj -MD -MP -MF "$(DEPDIR)/hmac_sha2.Tpo" -c -o hmac_sha2.obj `if test -f 'libsha2/hmac_sha2.c'; then $(CYGPATH_W) 'libsha2/hmac_sha2.c'; else $(CYGPATH_W) '$(srcdir)/libsha2/hmac_sha2.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hmac_sha2.Tpo" "$(DEPDIR)/hmac_sha2.Po"; else rm -f "$(DEPDIR)/hmac_sha2.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libsha2/hmac_sha2.c' object='hmac_sha2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hmac_sha2.obj `if test -f 'libsha2/hmac_sha2.c'; then $(CYGPATH_W) 'libsha2/hmac_sha2.c'; else $(CYGPATH_W) '$(srcdir)/libsha2/hmac_sha2.c'; fi` + +sha2.o: libsha2/sha2.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha2.o -MD -MP -MF "$(DEPDIR)/sha2.Tpo" -c -o sha2.o `test -f 'libsha2/sha2.c' || echo '$(srcdir)/'`libsha2/sha2.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sha2.Tpo" "$(DEPDIR)/sha2.Po"; else rm -f "$(DEPDIR)/sha2.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libsha2/sha2.c' object='sha2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha2.o `test -f 'libsha2/sha2.c' || echo '$(srcdir)/'`libsha2/sha2.c + +sha2.obj: libsha2/sha2.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha2.obj -MD -MP -MF "$(DEPDIR)/sha2.Tpo" -c -o sha2.obj `if test -f 'libsha2/sha2.c'; then $(CYGPATH_W) 'libsha2/sha2.c'; else $(CYGPATH_W) '$(srcdir)/libsha2/sha2.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sha2.Tpo" "$(DEPDIR)/sha2.Po"; else rm -f "$(DEPDIR)/sha2.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libsha2/sha2.c' object='sha2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha2.obj `if test -f 'libsha2/sha2.c'; then $(CYGPATH_W) 'libsha2/sha2.c'; else $(CYGPATH_W) '$(srcdir)/libsha2/sha2.c'; fi` + +serpent_cbc.o: libserpent/serpent_cbc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT serpent_cbc.o -MD -MP -MF "$(DEPDIR)/serpent_cbc.Tpo" -c -o serpent_cbc.o `test -f 'libserpent/serpent_cbc.c' || echo '$(srcdir)/'`libserpent/serpent_cbc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/serpent_cbc.Tpo" "$(DEPDIR)/serpent_cbc.Po"; else rm -f "$(DEPDIR)/serpent_cbc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libserpent/serpent_cbc.c' object='serpent_cbc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o serpent_cbc.o `test -f 'libserpent/serpent_cbc.c' || echo '$(srcdir)/'`libserpent/serpent_cbc.c + +serpent_cbc.obj: libserpent/serpent_cbc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT serpent_cbc.obj -MD -MP -MF "$(DEPDIR)/serpent_cbc.Tpo" -c -o serpent_cbc.obj `if test -f 'libserpent/serpent_cbc.c'; then $(CYGPATH_W) 'libserpent/serpent_cbc.c'; else $(CYGPATH_W) '$(srcdir)/libserpent/serpent_cbc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/serpent_cbc.Tpo" "$(DEPDIR)/serpent_cbc.Po"; else rm -f "$(DEPDIR)/serpent_cbc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libserpent/serpent_cbc.c' object='serpent_cbc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o serpent_cbc.obj `if test -f 'libserpent/serpent_cbc.c'; then $(CYGPATH_W) 'libserpent/serpent_cbc.c'; else $(CYGPATH_W) '$(srcdir)/libserpent/serpent_cbc.c'; fi` + +serpent.o: libserpent/serpent.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT serpent.o -MD -MP -MF "$(DEPDIR)/serpent.Tpo" -c -o serpent.o `test -f 'libserpent/serpent.c' || echo '$(srcdir)/'`libserpent/serpent.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/serpent.Tpo" "$(DEPDIR)/serpent.Po"; else rm -f "$(DEPDIR)/serpent.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libserpent/serpent.c' object='serpent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o serpent.o `test -f 'libserpent/serpent.c' || echo '$(srcdir)/'`libserpent/serpent.c + +serpent.obj: libserpent/serpent.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT serpent.obj -MD -MP -MF "$(DEPDIR)/serpent.Tpo" -c -o serpent.obj `if test -f 'libserpent/serpent.c'; then $(CYGPATH_W) 'libserpent/serpent.c'; else $(CYGPATH_W) '$(srcdir)/libserpent/serpent.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/serpent.Tpo" "$(DEPDIR)/serpent.Po"; else rm -f "$(DEPDIR)/serpent.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libserpent/serpent.c' object='serpent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o serpent.obj `if test -f 'libserpent/serpent.c'; then $(CYGPATH_W) 'libserpent/serpent.c'; else $(CYGPATH_W) '$(srcdir)/libserpent/serpent.c'; fi` + +twofish_cbc.o: libtwofish/twofish_cbc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT twofish_cbc.o -MD -MP -MF "$(DEPDIR)/twofish_cbc.Tpo" -c -o twofish_cbc.o `test -f 'libtwofish/twofish_cbc.c' || echo '$(srcdir)/'`libtwofish/twofish_cbc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/twofish_cbc.Tpo" "$(DEPDIR)/twofish_cbc.Po"; else rm -f "$(DEPDIR)/twofish_cbc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtwofish/twofish_cbc.c' object='twofish_cbc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o twofish_cbc.o `test -f 'libtwofish/twofish_cbc.c' || echo '$(srcdir)/'`libtwofish/twofish_cbc.c + +twofish_cbc.obj: libtwofish/twofish_cbc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT twofish_cbc.obj -MD -MP -MF "$(DEPDIR)/twofish_cbc.Tpo" -c -o twofish_cbc.obj `if test -f 'libtwofish/twofish_cbc.c'; then $(CYGPATH_W) 'libtwofish/twofish_cbc.c'; else $(CYGPATH_W) '$(srcdir)/libtwofish/twofish_cbc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/twofish_cbc.Tpo" "$(DEPDIR)/twofish_cbc.Po"; else rm -f "$(DEPDIR)/twofish_cbc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtwofish/twofish_cbc.c' object='twofish_cbc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o twofish_cbc.obj `if test -f 'libtwofish/twofish_cbc.c'; then $(CYGPATH_W) 'libtwofish/twofish_cbc.c'; else $(CYGPATH_W) '$(srcdir)/libtwofish/twofish_cbc.c'; fi` + +twofish.o: libtwofish/twofish.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT twofish.o -MD -MP -MF "$(DEPDIR)/twofish.Tpo" -c -o twofish.o `test -f 'libtwofish/twofish.c' || echo '$(srcdir)/'`libtwofish/twofish.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/twofish.Tpo" "$(DEPDIR)/twofish.Po"; else rm -f "$(DEPDIR)/twofish.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtwofish/twofish.c' object='twofish.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o twofish.o `test -f 'libtwofish/twofish.c' || echo '$(srcdir)/'`libtwofish/twofish.c + +twofish.obj: libtwofish/twofish.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT twofish.obj -MD -MP -MF "$(DEPDIR)/twofish.Tpo" -c -o twofish.obj `if test -f 'libtwofish/twofish.c'; then $(CYGPATH_W) 'libtwofish/twofish.c'; else $(CYGPATH_W) '$(srcdir)/libtwofish/twofish.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/twofish.Tpo" "$(DEPDIR)/twofish.Po"; else rm -f "$(DEPDIR)/twofish.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtwofish/twofish.c' object='twofish.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o twofish.obj `if test -f 'libtwofish/twofish.c'; then $(CYGPATH_W) 'libtwofish/twofish.c'; else $(CYGPATH_W) '$(srcdir)/libtwofish/twofish.c'; fi` + +des_enc.o: libdes/des_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT des_enc.o -MD -MP -MF "$(DEPDIR)/des_enc.Tpo" -c -o des_enc.o `test -f 'libdes/des_enc.c' || echo '$(srcdir)/'`libdes/des_enc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/des_enc.Tpo" "$(DEPDIR)/des_enc.Po"; else rm -f "$(DEPDIR)/des_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/des_enc.c' object='des_enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o des_enc.o `test -f 'libdes/des_enc.c' || echo '$(srcdir)/'`libdes/des_enc.c + +des_enc.obj: libdes/des_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT des_enc.obj -MD -MP -MF "$(DEPDIR)/des_enc.Tpo" -c -o des_enc.obj `if test -f 'libdes/des_enc.c'; then $(CYGPATH_W) 'libdes/des_enc.c'; else $(CYGPATH_W) '$(srcdir)/libdes/des_enc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/des_enc.Tpo" "$(DEPDIR)/des_enc.Po"; else rm -f "$(DEPDIR)/des_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/des_enc.c' object='des_enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o des_enc.obj `if test -f 'libdes/des_enc.c'; then $(CYGPATH_W) 'libdes/des_enc.c'; else $(CYGPATH_W) '$(srcdir)/libdes/des_enc.c'; fi` + +set_key.o: libdes/set_key.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT set_key.o -MD -MP -MF "$(DEPDIR)/set_key.Tpo" -c -o set_key.o `test -f 'libdes/set_key.c' || echo '$(srcdir)/'`libdes/set_key.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/set_key.Tpo" "$(DEPDIR)/set_key.Po"; else rm -f "$(DEPDIR)/set_key.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/set_key.c' object='set_key.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o set_key.o `test -f 'libdes/set_key.c' || echo '$(srcdir)/'`libdes/set_key.c + +set_key.obj: libdes/set_key.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT set_key.obj -MD -MP -MF "$(DEPDIR)/set_key.Tpo" -c -o set_key.obj `if test -f 'libdes/set_key.c'; then $(CYGPATH_W) 'libdes/set_key.c'; else $(CYGPATH_W) '$(srcdir)/libdes/set_key.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/set_key.Tpo" "$(DEPDIR)/set_key.Po"; else rm -f "$(DEPDIR)/set_key.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/set_key.c' object='set_key.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o set_key.obj `if test -f 'libdes/set_key.c'; then $(CYGPATH_W) 'libdes/set_key.c'; else $(CYGPATH_W) '$(srcdir)/libdes/set_key.c'; fi` + +speed.o: libdes/speed.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT speed.o -MD -MP -MF "$(DEPDIR)/speed.Tpo" -c -o speed.o `test -f 'libdes/speed.c' || echo '$(srcdir)/'`libdes/speed.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/speed.Tpo" "$(DEPDIR)/speed.Po"; else rm -f "$(DEPDIR)/speed.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/speed.c' object='speed.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o speed.o `test -f 'libdes/speed.c' || echo '$(srcdir)/'`libdes/speed.c + +speed.obj: libdes/speed.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT speed.obj -MD -MP -MF "$(DEPDIR)/speed.Tpo" -c -o speed.obj `if test -f 'libdes/speed.c'; then $(CYGPATH_W) 'libdes/speed.c'; else $(CYGPATH_W) '$(srcdir)/libdes/speed.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/speed.Tpo" "$(DEPDIR)/speed.Po"; else rm -f "$(DEPDIR)/speed.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/speed.c' object='speed.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o speed.obj `if test -f 'libdes/speed.c'; then $(CYGPATH_W) 'libdes/speed.c'; else $(CYGPATH_W) '$(srcdir)/libdes/speed.c'; fi` + +fcrypt_b.o: libdes/fcrypt_b.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fcrypt_b.o -MD -MP -MF "$(DEPDIR)/fcrypt_b.Tpo" -c -o fcrypt_b.o `test -f 'libdes/fcrypt_b.c' || echo '$(srcdir)/'`libdes/fcrypt_b.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/fcrypt_b.Tpo" "$(DEPDIR)/fcrypt_b.Po"; else rm -f "$(DEPDIR)/fcrypt_b.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/fcrypt_b.c' object='fcrypt_b.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fcrypt_b.o `test -f 'libdes/fcrypt_b.c' || echo '$(srcdir)/'`libdes/fcrypt_b.c + +fcrypt_b.obj: libdes/fcrypt_b.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fcrypt_b.obj -MD -MP -MF "$(DEPDIR)/fcrypt_b.Tpo" -c -o fcrypt_b.obj `if test -f 'libdes/fcrypt_b.c'; then $(CYGPATH_W) 'libdes/fcrypt_b.c'; else $(CYGPATH_W) '$(srcdir)/libdes/fcrypt_b.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/fcrypt_b.Tpo" "$(DEPDIR)/fcrypt_b.Po"; else rm -f "$(DEPDIR)/fcrypt_b.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/fcrypt_b.c' object='fcrypt_b.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fcrypt_b.obj `if test -f 'libdes/fcrypt_b.c'; then $(CYGPATH_W) 'libdes/fcrypt_b.c'; else $(CYGPATH_W) '$(srcdir)/libdes/fcrypt_b.c'; fi` + +fcrypt.o: libdes/fcrypt.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fcrypt.o -MD -MP -MF "$(DEPDIR)/fcrypt.Tpo" -c -o fcrypt.o `test -f 'libdes/fcrypt.c' || echo '$(srcdir)/'`libdes/fcrypt.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/fcrypt.Tpo" "$(DEPDIR)/fcrypt.Po"; else rm -f "$(DEPDIR)/fcrypt.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/fcrypt.c' object='fcrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fcrypt.o `test -f 'libdes/fcrypt.c' || echo '$(srcdir)/'`libdes/fcrypt.c + +fcrypt.obj: libdes/fcrypt.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fcrypt.obj -MD -MP -MF "$(DEPDIR)/fcrypt.Tpo" -c -o fcrypt.obj `if test -f 'libdes/fcrypt.c'; then $(CYGPATH_W) 'libdes/fcrypt.c'; else $(CYGPATH_W) '$(srcdir)/libdes/fcrypt.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/fcrypt.Tpo" "$(DEPDIR)/fcrypt.Po"; else rm -f "$(DEPDIR)/fcrypt.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/fcrypt.c' object='fcrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fcrypt.obj `if test -f 'libdes/fcrypt.c'; then $(CYGPATH_W) 'libdes/fcrypt.c'; else $(CYGPATH_W) '$(srcdir)/libdes/fcrypt.c'; fi` + +destest.o: libdes/destest.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT destest.o -MD -MP -MF "$(DEPDIR)/destest.Tpo" -c -o destest.o `test -f 'libdes/destest.c' || echo '$(srcdir)/'`libdes/destest.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/destest.Tpo" "$(DEPDIR)/destest.Po"; else rm -f "$(DEPDIR)/destest.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/destest.c' object='destest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o destest.o `test -f 'libdes/destest.c' || echo '$(srcdir)/'`libdes/destest.c + +destest.obj: libdes/destest.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT destest.obj -MD -MP -MF "$(DEPDIR)/destest.Tpo" -c -o destest.obj `if test -f 'libdes/destest.c'; then $(CYGPATH_W) 'libdes/destest.c'; else $(CYGPATH_W) '$(srcdir)/libdes/destest.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/destest.Tpo" "$(DEPDIR)/destest.Po"; else rm -f "$(DEPDIR)/destest.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/destest.c' object='destest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o destest.obj `if test -f 'libdes/destest.c'; then $(CYGPATH_W) 'libdes/destest.c'; else $(CYGPATH_W) '$(srcdir)/libdes/destest.c'; fi` + +cbc_enc.o: libdes/cbc_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cbc_enc.o -MD -MP -MF "$(DEPDIR)/cbc_enc.Tpo" -c -o cbc_enc.o `test -f 'libdes/cbc_enc.c' || echo '$(srcdir)/'`libdes/cbc_enc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cbc_enc.Tpo" "$(DEPDIR)/cbc_enc.Po"; else rm -f "$(DEPDIR)/cbc_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/cbc_enc.c' object='cbc_enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cbc_enc.o `test -f 'libdes/cbc_enc.c' || echo '$(srcdir)/'`libdes/cbc_enc.c + +cbc_enc.obj: libdes/cbc_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cbc_enc.obj -MD -MP -MF "$(DEPDIR)/cbc_enc.Tpo" -c -o cbc_enc.obj `if test -f 'libdes/cbc_enc.c'; then $(CYGPATH_W) 'libdes/cbc_enc.c'; else $(CYGPATH_W) '$(srcdir)/libdes/cbc_enc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/cbc_enc.Tpo" "$(DEPDIR)/cbc_enc.Po"; else rm -f "$(DEPDIR)/cbc_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/cbc_enc.c' object='cbc_enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cbc_enc.obj `if test -f 'libdes/cbc_enc.c'; then $(CYGPATH_W) 'libdes/cbc_enc.c'; else $(CYGPATH_W) '$(srcdir)/libdes/cbc_enc.c'; fi` + +ecb_enc.o: libdes/ecb_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ecb_enc.o -MD -MP -MF "$(DEPDIR)/ecb_enc.Tpo" -c -o ecb_enc.o `test -f 'libdes/ecb_enc.c' || echo '$(srcdir)/'`libdes/ecb_enc.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ecb_enc.Tpo" "$(DEPDIR)/ecb_enc.Po"; else rm -f "$(DEPDIR)/ecb_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/ecb_enc.c' object='ecb_enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ecb_enc.o `test -f 'libdes/ecb_enc.c' || echo '$(srcdir)/'`libdes/ecb_enc.c + +ecb_enc.obj: libdes/ecb_enc.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ecb_enc.obj -MD -MP -MF "$(DEPDIR)/ecb_enc.Tpo" -c -o ecb_enc.obj `if test -f 'libdes/ecb_enc.c'; then $(CYGPATH_W) 'libdes/ecb_enc.c'; else $(CYGPATH_W) '$(srcdir)/libdes/ecb_enc.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ecb_enc.Tpo" "$(DEPDIR)/ecb_enc.Po"; else rm -f "$(DEPDIR)/ecb_enc.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/ecb_enc.c' object='ecb_enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ecb_enc.obj `if test -f 'libdes/ecb_enc.c'; then $(CYGPATH_W) 'libdes/ecb_enc.c'; else $(CYGPATH_W) '$(srcdir)/libdes/ecb_enc.c'; fi` + +des_opts.o: libdes/des_opts.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT des_opts.o -MD -MP -MF "$(DEPDIR)/des_opts.Tpo" -c -o des_opts.o `test -f 'libdes/des_opts.c' || echo '$(srcdir)/'`libdes/des_opts.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/des_opts.Tpo" "$(DEPDIR)/des_opts.Po"; else rm -f "$(DEPDIR)/des_opts.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/des_opts.c' object='des_opts.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o des_opts.o `test -f 'libdes/des_opts.c' || echo '$(srcdir)/'`libdes/des_opts.c + +des_opts.obj: libdes/des_opts.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT des_opts.obj -MD -MP -MF "$(DEPDIR)/des_opts.Tpo" -c -o des_opts.obj `if test -f 'libdes/des_opts.c'; then $(CYGPATH_W) 'libdes/des_opts.c'; else $(CYGPATH_W) '$(srcdir)/libdes/des_opts.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/des_opts.Tpo" "$(DEPDIR)/des_opts.Po"; else rm -f "$(DEPDIR)/des_opts.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libdes/des_opts.c' object='des_opts.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o des_opts.obj `if test -f 'libdes/des_opts.c'; then $(CYGPATH_W) 'libdes/des_opts.c'; else $(CYGPATH_W) '$(srcdir)/libdes/des_opts.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/libcrypto/include/cbc_generic.h b/src/libcrypto/include/cbc_generic.h new file mode 100644 index 000000000..0dd3a77d6 --- /dev/null +++ b/src/libcrypto/include/cbc_generic.h @@ -0,0 +1,110 @@ +#ifndef _CBC_GENERIC_H +#define _CBC_GENERIC_H +/* + * CBC macro helpers + * + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ + +/* + * Heavily inspired in loop_AES + */ +#define CBC_IMPL_BLK16(name, ctx_type, addr_type, enc_func, dec_func) \ +int name(ctx_type *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \ + int ret=ilen, pos; \ + const u_int32_t *iv_i; \ + if ((ilen) % 16) return 0; \ + if (encrypt) { \ + pos=0; \ + while(pos=0) { \ + dec_func(ctx, (const addr_type) in, (addr_type) out); \ + if (pos==0) \ + iv_i=(const u_int32_t*) (iv); \ + else \ + iv_i=(const u_int32_t*) (in-16); \ + *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \ + *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \ + *((u_int32_t *)(&out[ 8])) ^= iv_i[2]; \ + *((u_int32_t *)(&out[12])) ^= iv_i[3]; \ + in-=16; \ + out-=16; \ + pos-=16; \ + } \ + } \ + return ret; \ +} +#define CBC_IMPL_BLK8(name, ctx_type, addr_type, enc_func, dec_func) \ +int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \ + int ret=ilen, pos; \ + const u_int32_t *iv_i; \ + if ((ilen) % 8) return 0; \ + if (encrypt) { \ + pos=0; \ + while(pos=0) { \ + dec_func(ctx, (const addr_type)in, (addr_type)out); \ + if (pos==0) \ + iv_i=(const u_int32_t*) (iv); \ + else \ + iv_i=(const u_int32_t*) (in-8); \ + *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \ + *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \ + in-=8; \ + out-=8; \ + pos-=8; \ + } \ + } \ + return ret; \ +} +#define CBC_DECL(name, ctx_type) \ +int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) +/* +Eg.: +CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt); +CBC_DECL(AES_cbc_encrypt, aes_context); +*/ +#endif /* _CBC_GENERIC_H */ diff --git a/src/libcrypto/include/hmac_generic.h b/src/libcrypto/include/hmac_generic.h new file mode 100644 index 000000000..a749228e3 --- /dev/null +++ b/src/libcrypto/include/hmac_generic.h @@ -0,0 +1,60 @@ +#ifndef _HMAC_GENERIC_H +#define _HMAC_GENERIC_H +/* + * HMAC macro helpers + * + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ + +#ifndef DIVUP +#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */ +#endif +#ifndef HMAC_IPAD +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5C +#endif +#define HMAC_SET_KEY_IMPL(func_name, hctx_t, blocksize, func_init, func_update) \ +void func_name(hctx_t *hctx, const u_int8_t * key, int keylen) { \ + int i;\ + u_int8_t kb[blocksize]; \ + for (i = 0; i < DIVUP(keylen*8, 8); i++) { \ + kb[i] = key[i] ^ HMAC_IPAD; \ + } \ + for (; i < blocksize; i++) { \ + kb[i] = HMAC_IPAD; \ + } \ + func_init(&hctx->ictx); \ + func_update(&hctx->ictx, kb, blocksize); \ + for (i = 0; i < blocksize; i++) { \ + kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD); \ + } \ + func_init(&hctx->octx); \ + func_update(&hctx->octx, kb, blocksize); \ +} +#define HMAC_HASH_IMPL(func_name, hctx_t, ctx_t, ahlen, func_update, func_result ) \ +void func_name(hctx_t *hctx, const u_int8_t * dat, int len, u_int8_t * hash, int hashlen) { \ + ctx_t ctx; \ + ctx=hctx->ictx; \ + if (dat) func_update(&ctx, dat, len); \ + if (hash) { \ + u_int8_t hash_buf[ahlen]; \ + func_result(&ctx, hash_buf, ahlen); \ + ctx=hctx->octx; \ + func_update(&ctx, hash_buf, ahlen); \ + func_result(&ctx, hash, hashlen); \ + memset(&ctx, 0, sizeof (ctx)); \ + memset(&hash_buf, 0, sizeof (hash_buf));\ + } \ +} +#endif /* _HMAC_GENERIC_H */ diff --git a/src/libcrypto/include/md32_common.h b/src/libcrypto/include/md32_common.h new file mode 100644 index 000000000..1a404a458 --- /dev/null +++ b/src/libcrypto/include/md32_common.h @@ -0,0 +1,607 @@ +/* crypto/md32_common.h */ +/* ==================================================================== + * Copyright (c) 1999 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED 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 OpenSSL PROJECT OR + * ITS 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. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +/* + * This is a generic 32 bit "collector" for message digest algorithms. + * Whenever needed it collects input character stream into chunks of + * 32 bit values and invokes a block function that performs actual hash + * calculations. + * + * Porting guide. + * + * Obligatory macros: + * + * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN + * this macro defines byte order of input stream. + * HASH_CBLOCK + * size of a unit chunk HASH_BLOCK operates on. + * HASH_LONG + * has to be at lest 32 bit wide, if it's wider, then + * HASH_LONG_LOG2 *has to* be defined along + * HASH_CTX + * context structure that at least contains following + * members: + * typedef struct { + * ... + * HASH_LONG Nl,Nh; + * HASH_LONG data[HASH_LBLOCK]; + * int num; + * ... + * } HASH_CTX; + * HASH_UPDATE + * name of "Update" function, implemented here. + * HASH_TRANSFORM + * name of "Transform" function, implemented here. + * HASH_FINAL + * name of "Final" function, implemented here. + * HASH_BLOCK_HOST_ORDER + * name of "block" function treating *aligned* input message + * in host byte order, implemented externally. + * HASH_BLOCK_DATA_ORDER + * name of "block" function treating *unaligned* input message + * in original (data) byte order, implemented externally (it + * actually is optional if data and host are of the same + * "endianess"). + * HASH_MAKE_STRING + * macro convering context variables to an ASCII hash string. + * + * Optional macros: + * + * B_ENDIAN or L_ENDIAN + * defines host byte-order. + * HASH_LONG_LOG2 + * defaults to 2 if not states otherwise. + * HASH_LBLOCK + * assumed to be HASH_CBLOCK/4 if not stated otherwise. + * HASH_BLOCK_DATA_ORDER_ALIGNED + * alternative "block" function capable of treating + * aligned input message in original (data) order, + * implemented externally. + * + * MD5 example: + * + * #define DATA_ORDER_IS_LITTLE_ENDIAN + * + * #define HASH_LONG MD5_LONG + * #define HASH_LONG_LOG2 MD5_LONG_LOG2 + * #define HASH_CTX MD5_CTX + * #define HASH_CBLOCK MD5_CBLOCK + * #define HASH_LBLOCK MD5_LBLOCK + * #define HASH_UPDATE MD5_Update + * #define HASH_TRANSFORM MD5_Transform + * #define HASH_FINAL MD5_Final + * #define HASH_BLOCK_HOST_ORDER md5_block_host_order + * #define HASH_BLOCK_DATA_ORDER md5_block_data_order + * + * + */ + +#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) +#error "DATA_ORDER must be defined!" +#endif + +#ifndef HASH_CBLOCK +#error "HASH_CBLOCK must be defined!" +#endif +#ifndef HASH_LONG +#error "HASH_LONG must be defined!" +#endif +#ifndef HASH_CTX +#error "HASH_CTX must be defined!" +#endif + +#ifndef HASH_UPDATE +#error "HASH_UPDATE must be defined!" +#endif +#ifndef HASH_TRANSFORM +#error "HASH_TRANSFORM must be defined!" +#endif +#ifndef HASH_FINAL +#error "HASH_FINAL must be defined!" +#endif + +#ifndef HASH_BLOCK_HOST_ORDER +#error "HASH_BLOCK_HOST_ORDER must be defined!" +#endif + +#if 0 +/* + * Moved below as it's required only if HASH_BLOCK_DATA_ORDER_ALIGNED + * isn't defined. + */ +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif +#endif + +#ifndef HASH_LBLOCK +#define HASH_LBLOCK (HASH_CBLOCK/4) +#endif + +#ifndef HASH_LONG_LOG2 +#define HASH_LONG_LOG2 2 +#endif + +/* + * Engage compiler specific rotate intrinsic function if available. + */ +#undef ROTATE +#ifndef PEDANTIC +# if defined(_MSC_VER) +# define ROTATE(a,n) _lrotl(a,n) +# elif defined(__MWERKS__) +# if defined(__POWERPC__) +# define ROTATE(a,n) __rlwinm(a,n,0,31) +# elif defined(__MC68K__) + /* Motorola specific tweak. */ +# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) +# else +# define ROTATE(a,n) __rol(a,n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 && !defined(NO_ASM) && !defined(NO_INLINE_ASM) + /* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + * + */ +# if defined(__i386) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r"(ret) \ + : "I"(n), "0"(a) \ + : "cc"); \ + ret; \ + }) +# elif defined(__powerpc) || defined(__ppc) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# endif +# endif + +/* + * Engage compiler specific "fetch in reverse byte order" + * intrinsic function if available. + */ +# if defined(__GNUC__) && __GNUC__>=2 && !defined(NO_ASM) && !defined(NO_INLINE_ASM) + /* some GNU C inline assembler templates by */ +# if defined(__i386) && !defined(I386_ONLY) +# define BE_FETCH32(a) ({ register unsigned int l=(a);\ + asm ( \ + "bswapl %0" \ + : "=r"(l) : "0"(l)); \ + l; \ + }) +# elif defined(__powerpc) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lwbrx %0,0,%1" \ + : "=r"(l) \ + : "r"(a)); \ + l; \ + }) + +# elif defined(__sparc) && defined(ULTRASPARC) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lda [%1]#ASI_PRIMARY_LITTLE,%0"\ + : "=r"(l) \ + : "r"(a)); \ + l; \ + }) +# endif +# endif +#endif /* PEDANTIC */ + +#if HASH_LONG_LOG2==2 /* Engage only if sizeof(HASH_LONG)== 4 */ +/* A nice byte order reversal from Wei Dai */ +#ifdef ROTATE +/* 5 instructions with rotate instruction, else 9 */ +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + ((ROTATE(l,8)&0x00FF00FF)|(ROTATE((l&0x00FF00FF),24))) \ + ) +#else +/* 6 instructions with rotate instruction, else 8 */ +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + l=(((l>>8)&0x00FF00FF)|((l&0x00FF00FF)<<8)), \ + ROTATE(l,16) \ + ) +/* + * Originally the middle line started with l=(((l&0xFF00FF00)>>8)|... + * It's rewritten as above for two reasons: + * - RISCs aren't good at long constants and have to explicitely + * compose 'em with several (well, usually 2) instructions in a + * register before performing the actual operation and (as you + * already realized:-) having same constant should inspire the + * compiler to permanently allocate the only register for it; + * - most modern CPUs have two ALUs, but usually only one has + * circuitry for shifts:-( this minor tweak inspires compiler + * to schedule shift instructions in a better way... + * + * + */ +#endif +#endif + +#ifndef ROTATE +#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +/* + * Make some obvious choices. E.g., HASH_BLOCK_DATA_ORDER_ALIGNED + * and HASH_BLOCK_HOST_ORDER ought to be the same if input data + * and host are of the same "endianess". It's possible to mask + * this with blank #define HASH_BLOCK_DATA_ORDER though... + * + * + */ +#if defined(B_ENDIAN) +# if defined(DATA_ORDER_IS_BIG_ENDIAN) +# if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# endif +# elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# ifndef HOST_FETCH32 +# ifdef LE_FETCH32 +# define HOST_FETCH32(p,l) LE_FETCH32(p) +# elif defined(REVERSE_FETCH32) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# endif +# endif +# endif +#elif defined(L_ENDIAN) +# if defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# endif +# elif defined(DATA_ORDER_IS_BIG_ENDIAN) +# ifndef HOST_FETCH32 +# ifdef BE_FETCH32 +# define HOST_FETCH32(p,l) BE_FETCH32(p) +# elif defined(REVERSE_FETCH32) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# endif +# endif +# endif +#endif + +#if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif +#endif + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + case 3: l|=((unsigned long)(*((c)++))); \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + } } +/* NOTE the pointer is not incremented at the end of this */ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<< 8; \ + case 2: l|=((unsigned long)(*(--(c))))<<16; \ + case 1: l|=((unsigned long)(*(--(c))))<<24; \ + } } +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) + +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++))); \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + case 3: l|=((unsigned long)(*((c)++)))<<24; \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++))); \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + } } +/* NOTE the pointer is not incremented at the end of this */ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<<16; \ + case 2: l|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l|=((unsigned long)(*(--(c)))); \ + } } +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) + +#endif + +/* + * Time for some action:-) + */ + +void HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) + { + const unsigned char *data=data_; + register HASH_LONG * p; + register unsigned long l; + int sw,sc,ew,ec; + + if (len==0) return; + + l=(c->Nl+(len<<3))&0xffffffffL; + /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to + * Wei Dai for pointing it out. */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + p=c->data; + sw=c->num>>2; + sc=c->num&0x03; + + if ((c->num+len) >= HASH_CBLOCK) + { + l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; + for (; swnum); + c->num=0; + /* drop through and do the rest */ + } + else + { + c->num+=len; + if ((sc+len) < 4) /* ugly, add char's to a word */ + { + l=p[sw]; HOST_p_c2l_p(data,l,sc,len); p[sw]=l; + } + else + { + ew=(c->num>>2); + ec=(c->num&0x03); + l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; + for (; sw < ew; sw++) + { + HOST_c2l(data,l); p[sw]=l; + } + if (ec) + { + HOST_c2l_p(data,l,ec); p[sw]=l; + } + } + return; + } + } + + sw=len/HASH_CBLOCK; + if (sw > 0) + { +#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) + /* + * Note that HASH_BLOCK_DATA_ORDER_ALIGNED gets defined + * only if sizeof(HASH_LONG)==4. + */ + if ((((unsigned long)data)%4) == 0) + { + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } + else +#if !defined(HASH_BLOCK_DATA_ORDER) + while (sw--) + { + memcpy (p=c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1); + data+=HASH_CBLOCK; + len-=HASH_CBLOCK; + } +#endif +#endif +#if defined(HASH_BLOCK_DATA_ORDER) + { + HASH_BLOCK_DATA_ORDER(c,data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } +#endif + } + + if (len!=0) + { + p = c->data; + c->num = len; + ew=len>>2; /* words to copy */ + ec=len&0x03; + for (; ew; ew--,p++) + { + HOST_c2l(data,l); *p=l; + } + HOST_c2l_p(data,l,ec); + *p=l; + } + } + + +void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data) + { +#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) + if ((((unsigned long)data)%4) == 0) + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,1); + else +#if !defined(HASH_BLOCK_DATA_ORDER) + { + memcpy (c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1); + } +#endif +#endif +#if defined(HASH_BLOCK_DATA_ORDER) + HASH_BLOCK_DATA_ORDER (c,data,1); +#endif + } + + +void HASH_FINAL (unsigned char *md, HASH_CTX *c) + { + register HASH_LONG *p; + register unsigned long l; + register int i,j; + static const unsigned char end[4]={0x80,0x00,0x00,0x00}; + const unsigned char *cp=end; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + i=c->num>>2; + j=c->num&0x03; + +#if 0 + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + * Many thanks to Alex Tang for pickup this + * 'potential bug' */ +#ifdef PURIFY + if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */ +#endif + l=p[i]; +#else + l = (j==0) ? 0 : p[i]; +#endif + HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */ + + if (i>(HASH_LBLOCK-2)) /* save room for Nl and Nh */ + { + if (iNh; + p[HASH_LBLOCK-1]=c->Nl; +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + p[HASH_LBLOCK-2]=c->Nl; + p[HASH_LBLOCK-1]=c->Nh; +#endif + HASH_BLOCK_HOST_ORDER (c,p,1); + +#ifndef HASH_MAKE_STRING +#error "HASH_MAKE_STRING must be defined!" +#else + HASH_MAKE_STRING(c,md); +#endif + + c->num=0; + /* clear stuff, HASH_BLOCK may be leaving some stuff on the stack + * but I'm not worried :-) + memset((void *)c,0,sizeof(HASH_CTX)); + */ + } diff --git a/src/libcrypto/libaes/aes.c b/src/libcrypto/libaes/aes.c new file mode 100644 index 000000000..1748119ac --- /dev/null +++ b/src/libcrypto/libaes/aes.c @@ -0,0 +1,1415 @@ +// I retain copyright in this code but I encourage its free use provided +// that I don't carry any responsibility for the results. I am especially +// happy to see it used in free and open source software. If you do use +// it I would appreciate an acknowledgement of its origin in the code or +// the product that results and I would also appreciate knowing a little +// about the use to which it is being put. I am grateful to Frank Yellin +// for some ideas that are used in this implementation. +// +// Dr B. R. Gladman 6th April 2001. +// +// This is an implementation of the AES encryption algorithm (Rijndael) +// designed by Joan Daemen and Vincent Rijmen. This version is designed +// to provide both fixed and dynamic block and key lengths and can also +// run with either big or little endian internal byte order (see aes.h). +// It inputs block and key lengths in bytes with the legal values being +// 16, 24 and 32. + +/* + * Modified by Jari Ruusu, May 1 2001 + * - Fixed some compile warnings, code was ok but gcc warned anyway. + * - Changed basic types: byte -> unsigned char, word -> u_int32_t + * - Major name space cleanup: Names visible to outside now begin + * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c + * - Removed C++ and DLL support as part of name space cleanup. + * - Eliminated unnecessary recomputation of tables. (actual bug fix) + * - Merged precomputed constant tables to aes.c file. + * - Removed data alignment restrictions for portability reasons. + * - Made block and key lengths accept bit count (128/192/256) + * as well byte count (16/24/32). + * - Removed all error checks. This change also eliminated the need + * to preinitialize the context struct to zero. + * - Removed some totally unused constants. + */ + +#include "aes.h" + +// CONFIGURATION OPTIONS (see also aes.h) +// +// 1. Define UNROLL for full loop unrolling in encryption and decryption. +// 2. Define PARTIAL_UNROLL to unroll two loops in encryption and decryption. +// 3. Define FIXED_TABLES for compiled rather than dynamic tables. +// 4. Define FF_TABLES to use tables for field multiplies and inverses. +// Do not enable this without understanding stack space requirements. +// 5. Define ARRAYS to use arrays to hold the local state block. If this +// is not defined, individually declared 32-bit words are used. +// 6. Define FAST_VARIABLE if a high speed variable block implementation +// is needed (essentially three separate fixed block size code sequences) +// 7. Define either ONE_TABLE or FOUR_TABLES for a fast table driven +// version using 1 table (2 kbytes of table space) or 4 tables (8 +// kbytes of table space) for higher speed. +// 8. Define either ONE_LR_TABLE or FOUR_LR_TABLES for a further speed +// increase by using tables for the last rounds but with more table +// space (2 or 8 kbytes extra). +// 9. If neither ONE_TABLE nor FOUR_TABLES is defined, a compact but +// slower version is provided. +// 10. If fast decryption key scheduling is needed define ONE_IM_TABLE +// or FOUR_IM_TABLES for higher speed (2 or 8 kbytes extra). + +#define UNROLL +//#define PARTIAL_UNROLL + +#define FIXED_TABLES +//#define FF_TABLES +//#define ARRAYS +#define FAST_VARIABLE + +//#define ONE_TABLE +#define FOUR_TABLES + +//#define ONE_LR_TABLE +#define FOUR_LR_TABLES + +//#define ONE_IM_TABLE +#define FOUR_IM_TABLES + +#if defined(UNROLL) && defined (PARTIAL_UNROLL) +#error both UNROLL and PARTIAL_UNROLL are defined +#endif + +#if defined(ONE_TABLE) && defined (FOUR_TABLES) +#error both ONE_TABLE and FOUR_TABLES are defined +#endif + +#if defined(ONE_LR_TABLE) && defined (FOUR_LR_TABLES) +#error both ONE_LR_TABLE and FOUR_LR_TABLES are defined +#endif + +#if defined(ONE_IM_TABLE) && defined (FOUR_IM_TABLES) +#error both ONE_IM_TABLE and FOUR_IM_TABLES are defined +#endif + +#if defined(AES_BLOCK_SIZE) && AES_BLOCK_SIZE != 16 && AES_BLOCK_SIZE != 24 && AES_BLOCK_SIZE != 32 +#error an illegal block size has been specified +#endif + +// upr(x,n): rotates bytes within words by n positions, moving bytes +// to higher index positions with wrap around into low positions +// ups(x,n): moves bytes by n positions to higher index positions in +// words but without wrap around +// bval(x,n): extracts a byte from a word + +#define upr(x,n) (((x) << 8 * (n)) | ((x) >> (32 - 8 * (n)))) +#define ups(x,n) ((x) << 8 * (n)) +#define bval(x,n) ((unsigned char)((x) >> 8 * (n))) +#define bytes2word(b0, b1, b2, b3) \ + ((u_int32_t)(b3) << 24 | (u_int32_t)(b2) << 16 | (u_int32_t)(b1) << 8 | (b0)) + + +/* little endian processor without data alignment restrictions: AES_LE_OK */ +/* original code: i386 */ +#if defined(i386) || defined(_I386) || defined(__i386__) || defined(__i386) +#define AES_LE_OK 1 +/* added (tested): alpha --jjo */ +#elif defined(__alpha__)|| defined (__alpha) +#define AES_LE_OK 1 +/* added (tested): ia64 --jjo */ +#elif defined(__ia64__)|| defined (__ia64) +#define AES_LE_OK 1 +#endif + +#ifdef AES_LE_OK +/* little endian processor without data alignment restrictions */ +#define word_in(x) *(u_int32_t*)(x) +#define const_word_in(x) *(const u_int32_t*)(x) +#define word_out(x,v) *(u_int32_t*)(x) = (v) +#define const_word_out(x,v) *(const u_int32_t*)(x) = (v) +#else +/* slower but generic big endian or with data alignment restrictions */ +/* some additional "const" touches to stop "gcc -Wcast-qual" complains --jjo */ +#define word_in(x) ((u_int32_t)(((unsigned char *)(x))[0])|((u_int32_t)(((unsigned char *)(x))[1])<<8)|((u_int32_t)(((unsigned char *)(x))[2])<<16)|((u_int32_t)(((unsigned char *)(x))[3])<<24)) +#define const_word_in(x) ((const u_int32_t)(((const unsigned char *)(x))[0])|((const u_int32_t)(((const unsigned char *)(x))[1])<<8)|((const u_int32_t)(((const unsigned char *)(x))[2])<<16)|((const u_int32_t)(((const unsigned char *)(x))[3])<<24)) +#define word_out(x,v) ((unsigned char *)(x))[0]=(v),((unsigned char *)(x))[1]=((v)>>8),((unsigned char *)(x))[2]=((v)>>16),((unsigned char *)(x))[3]=((v)>>24) +#define const_word_out(x,v) ((const unsigned char *)(x))[0]=(v),((const unsigned char *)(x))[1]=((v)>>8),((const unsigned char *)(x))[2]=((v)>>16),((const unsigned char *)(x))[3]=((v)>>24) +#endif + +// Disable at least some poor combinations of options + +#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) +#define FIXED_TABLES +#undef UNROLL +#undef ONE_LR_TABLE +#undef FOUR_LR_TABLES +#undef ONE_IM_TABLE +#undef FOUR_IM_TABLES +#elif !defined(FOUR_TABLES) +#ifdef FOUR_LR_TABLES +#undef FOUR_LR_TABLES +#define ONE_LR_TABLE +#endif +#ifdef FOUR_IM_TABLES +#undef FOUR_IM_TABLES +#define ONE_IM_TABLE +#endif +#elif !defined(AES_BLOCK_SIZE) +#if defined(UNROLL) +#define PARTIAL_UNROLL +#undef UNROLL +#endif +#endif + +// the finite field modular polynomial and elements + +#define ff_poly 0x011b +#define ff_hi 0x80 + +// multiply four bytes in GF(2^8) by 'x' {02} in parallel + +#define m1 0x80808080 +#define m2 0x7f7f7f7f +#define m3 0x0000001b +#define FFmulX(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * m3)) + +// The following defines provide alternative definitions of FFmulX that might +// give improved performance if a fast 32-bit multiply is not available. Note +// that a temporary variable u needs to be defined where FFmulX is used. + +// #define FFmulX(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6)) +// #define m4 0x1b1b1b1b +// #define FFmulX(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4) + +// perform column mix operation on four bytes in parallel + +#define fwd_mcol(x) (f2 = FFmulX(x), f2 ^ upr(x ^ f2,3) ^ upr(x,2) ^ upr(x,1)) + +#if defined(FIXED_TABLES) + +// the S-Box table + +static const unsigned char s_box[256] = +{ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +// the inverse S-Box table + +static const unsigned char inv_s_box[256] = +{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, + 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, + 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, + 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, + 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, + 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, + 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, + 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, + 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, + 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, + 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, + 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +}; + +#define w0(p) 0x000000##p + +// Number of elements required in this table for different +// block and key lengths is: +// +// Nk = 4 6 8 +// ---------- +// Nb = 4 | 10 8 7 +// 6 | 19 12 11 +// 8 | 29 19 14 +// +// this table can be a table of bytes if the key schedule +// code is adjusted accordingly + +static const u_int32_t rcon_tab[29] = +{ + w0(01), w0(02), w0(04), w0(08), + w0(10), w0(20), w0(40), w0(80), + w0(1b), w0(36), w0(6c), w0(d8), + w0(ab), w0(4d), w0(9a), w0(2f), + w0(5e), w0(bc), w0(63), w0(c6), + w0(97), w0(35), w0(6a), w0(d4), + w0(b3), w0(7d), w0(fa), w0(ef), + w0(c5) +}; + +#undef w0 + +#define r0(p,q,r,s) 0x##p##q##r##s +#define r1(p,q,r,s) 0x##q##r##s##p +#define r2(p,q,r,s) 0x##r##s##p##q +#define r3(p,q,r,s) 0x##s##p##q##r +#define w0(p) 0x000000##p +#define w1(p) 0x0000##p##00 +#define w2(p) 0x00##p##0000 +#define w3(p) 0x##p##000000 + +#if defined(FIXED_TABLES) && (defined(ONE_TABLE) || defined(FOUR_TABLES)) + +// data for forward tables (other than last round) + +#define f_table \ + r(a5,63,63,c6), r(84,7c,7c,f8), r(99,77,77,ee), r(8d,7b,7b,f6),\ + r(0d,f2,f2,ff), r(bd,6b,6b,d6), r(b1,6f,6f,de), r(54,c5,c5,91),\ + r(50,30,30,60), r(03,01,01,02), r(a9,67,67,ce), r(7d,2b,2b,56),\ + r(19,fe,fe,e7), r(62,d7,d7,b5), r(e6,ab,ab,4d), r(9a,76,76,ec),\ + r(45,ca,ca,8f), r(9d,82,82,1f), r(40,c9,c9,89), r(87,7d,7d,fa),\ + r(15,fa,fa,ef), r(eb,59,59,b2), r(c9,47,47,8e), r(0b,f0,f0,fb),\ + r(ec,ad,ad,41), r(67,d4,d4,b3), r(fd,a2,a2,5f), r(ea,af,af,45),\ + r(bf,9c,9c,23), r(f7,a4,a4,53), r(96,72,72,e4), r(5b,c0,c0,9b),\ + r(c2,b7,b7,75), r(1c,fd,fd,e1), r(ae,93,93,3d), r(6a,26,26,4c),\ + r(5a,36,36,6c), r(41,3f,3f,7e), r(02,f7,f7,f5), r(4f,cc,cc,83),\ + r(5c,34,34,68), r(f4,a5,a5,51), r(34,e5,e5,d1), r(08,f1,f1,f9),\ + r(93,71,71,e2), r(73,d8,d8,ab), r(53,31,31,62), r(3f,15,15,2a),\ + r(0c,04,04,08), r(52,c7,c7,95), r(65,23,23,46), r(5e,c3,c3,9d),\ + r(28,18,18,30), r(a1,96,96,37), r(0f,05,05,0a), r(b5,9a,9a,2f),\ + r(09,07,07,0e), r(36,12,12,24), r(9b,80,80,1b), r(3d,e2,e2,df),\ + r(26,eb,eb,cd), r(69,27,27,4e), r(cd,b2,b2,7f), r(9f,75,75,ea),\ + r(1b,09,09,12), r(9e,83,83,1d), r(74,2c,2c,58), r(2e,1a,1a,34),\ + r(2d,1b,1b,36), r(b2,6e,6e,dc), r(ee,5a,5a,b4), r(fb,a0,a0,5b),\ + r(f6,52,52,a4), r(4d,3b,3b,76), r(61,d6,d6,b7), r(ce,b3,b3,7d),\ + r(7b,29,29,52), r(3e,e3,e3,dd), r(71,2f,2f,5e), r(97,84,84,13),\ + r(f5,53,53,a6), r(68,d1,d1,b9), r(00,00,00,00), r(2c,ed,ed,c1),\ + r(60,20,20,40), r(1f,fc,fc,e3), r(c8,b1,b1,79), r(ed,5b,5b,b6),\ + r(be,6a,6a,d4), r(46,cb,cb,8d), r(d9,be,be,67), r(4b,39,39,72),\ + r(de,4a,4a,94), r(d4,4c,4c,98), r(e8,58,58,b0), r(4a,cf,cf,85),\ + r(6b,d0,d0,bb), r(2a,ef,ef,c5), r(e5,aa,aa,4f), r(16,fb,fb,ed),\ + r(c5,43,43,86), r(d7,4d,4d,9a), r(55,33,33,66), r(94,85,85,11),\ + r(cf,45,45,8a), r(10,f9,f9,e9), r(06,02,02,04), r(81,7f,7f,fe),\ + r(f0,50,50,a0), r(44,3c,3c,78), r(ba,9f,9f,25), r(e3,a8,a8,4b),\ + r(f3,51,51,a2), r(fe,a3,a3,5d), r(c0,40,40,80), r(8a,8f,8f,05),\ + r(ad,92,92,3f), r(bc,9d,9d,21), r(48,38,38,70), r(04,f5,f5,f1),\ + r(df,bc,bc,63), r(c1,b6,b6,77), r(75,da,da,af), r(63,21,21,42),\ + r(30,10,10,20), r(1a,ff,ff,e5), r(0e,f3,f3,fd), r(6d,d2,d2,bf),\ + r(4c,cd,cd,81), r(14,0c,0c,18), r(35,13,13,26), r(2f,ec,ec,c3),\ + r(e1,5f,5f,be), r(a2,97,97,35), r(cc,44,44,88), r(39,17,17,2e),\ + r(57,c4,c4,93), r(f2,a7,a7,55), r(82,7e,7e,fc), r(47,3d,3d,7a),\ + r(ac,64,64,c8), r(e7,5d,5d,ba), r(2b,19,19,32), r(95,73,73,e6),\ + r(a0,60,60,c0), r(98,81,81,19), r(d1,4f,4f,9e), r(7f,dc,dc,a3),\ + r(66,22,22,44), r(7e,2a,2a,54), r(ab,90,90,3b), r(83,88,88,0b),\ + r(ca,46,46,8c), r(29,ee,ee,c7), r(d3,b8,b8,6b), r(3c,14,14,28),\ + r(79,de,de,a7), r(e2,5e,5e,bc), r(1d,0b,0b,16), r(76,db,db,ad),\ + r(3b,e0,e0,db), r(56,32,32,64), r(4e,3a,3a,74), r(1e,0a,0a,14),\ + r(db,49,49,92), r(0a,06,06,0c), r(6c,24,24,48), r(e4,5c,5c,b8),\ + r(5d,c2,c2,9f), r(6e,d3,d3,bd), r(ef,ac,ac,43), r(a6,62,62,c4),\ + r(a8,91,91,39), r(a4,95,95,31), r(37,e4,e4,d3), r(8b,79,79,f2),\ + r(32,e7,e7,d5), r(43,c8,c8,8b), r(59,37,37,6e), r(b7,6d,6d,da),\ + r(8c,8d,8d,01), r(64,d5,d5,b1), r(d2,4e,4e,9c), r(e0,a9,a9,49),\ + r(b4,6c,6c,d8), r(fa,56,56,ac), r(07,f4,f4,f3), r(25,ea,ea,cf),\ + r(af,65,65,ca), r(8e,7a,7a,f4), r(e9,ae,ae,47), r(18,08,08,10),\ + r(d5,ba,ba,6f), r(88,78,78,f0), r(6f,25,25,4a), r(72,2e,2e,5c),\ + r(24,1c,1c,38), r(f1,a6,a6,57), r(c7,b4,b4,73), r(51,c6,c6,97),\ + r(23,e8,e8,cb), r(7c,dd,dd,a1), r(9c,74,74,e8), r(21,1f,1f,3e),\ + r(dd,4b,4b,96), r(dc,bd,bd,61), r(86,8b,8b,0d), r(85,8a,8a,0f),\ + r(90,70,70,e0), r(42,3e,3e,7c), r(c4,b5,b5,71), r(aa,66,66,cc),\ + r(d8,48,48,90), r(05,03,03,06), r(01,f6,f6,f7), r(12,0e,0e,1c),\ + r(a3,61,61,c2), r(5f,35,35,6a), r(f9,57,57,ae), r(d0,b9,b9,69),\ + r(91,86,86,17), r(58,c1,c1,99), r(27,1d,1d,3a), r(b9,9e,9e,27),\ + r(38,e1,e1,d9), r(13,f8,f8,eb), r(b3,98,98,2b), r(33,11,11,22),\ + r(bb,69,69,d2), r(70,d9,d9,a9), r(89,8e,8e,07), r(a7,94,94,33),\ + r(b6,9b,9b,2d), r(22,1e,1e,3c), r(92,87,87,15), r(20,e9,e9,c9),\ + r(49,ce,ce,87), r(ff,55,55,aa), r(78,28,28,50), r(7a,df,df,a5),\ + r(8f,8c,8c,03), r(f8,a1,a1,59), r(80,89,89,09), r(17,0d,0d,1a),\ + r(da,bf,bf,65), r(31,e6,e6,d7), r(c6,42,42,84), r(b8,68,68,d0),\ + r(c3,41,41,82), r(b0,99,99,29), r(77,2d,2d,5a), r(11,0f,0f,1e),\ + r(cb,b0,b0,7b), r(fc,54,54,a8), r(d6,bb,bb,6d), r(3a,16,16,2c) + +// data for inverse tables (other than last round) + +#define i_table \ + r(50,a7,f4,51), r(53,65,41,7e), r(c3,a4,17,1a), r(96,5e,27,3a),\ + r(cb,6b,ab,3b), r(f1,45,9d,1f), r(ab,58,fa,ac), r(93,03,e3,4b),\ + r(55,fa,30,20), r(f6,6d,76,ad), r(91,76,cc,88), r(25,4c,02,f5),\ + r(fc,d7,e5,4f), r(d7,cb,2a,c5), r(80,44,35,26), r(8f,a3,62,b5),\ + r(49,5a,b1,de), r(67,1b,ba,25), r(98,0e,ea,45), r(e1,c0,fe,5d),\ + r(02,75,2f,c3), r(12,f0,4c,81), r(a3,97,46,8d), r(c6,f9,d3,6b),\ + r(e7,5f,8f,03), r(95,9c,92,15), r(eb,7a,6d,bf), r(da,59,52,95),\ + r(2d,83,be,d4), r(d3,21,74,58), r(29,69,e0,49), r(44,c8,c9,8e),\ + r(6a,89,c2,75), r(78,79,8e,f4), r(6b,3e,58,99), r(dd,71,b9,27),\ + r(b6,4f,e1,be), r(17,ad,88,f0), r(66,ac,20,c9), r(b4,3a,ce,7d),\ + r(18,4a,df,63), r(82,31,1a,e5), r(60,33,51,97), r(45,7f,53,62),\ + r(e0,77,64,b1), r(84,ae,6b,bb), r(1c,a0,81,fe), r(94,2b,08,f9),\ + r(58,68,48,70), r(19,fd,45,8f), r(87,6c,de,94), r(b7,f8,7b,52),\ + r(23,d3,73,ab), r(e2,02,4b,72), r(57,8f,1f,e3), r(2a,ab,55,66),\ + r(07,28,eb,b2), r(03,c2,b5,2f), r(9a,7b,c5,86), r(a5,08,37,d3),\ + r(f2,87,28,30), r(b2,a5,bf,23), r(ba,6a,03,02), r(5c,82,16,ed),\ + r(2b,1c,cf,8a), r(92,b4,79,a7), r(f0,f2,07,f3), r(a1,e2,69,4e),\ + r(cd,f4,da,65), r(d5,be,05,06), r(1f,62,34,d1), r(8a,fe,a6,c4),\ + r(9d,53,2e,34), r(a0,55,f3,a2), r(32,e1,8a,05), r(75,eb,f6,a4),\ + r(39,ec,83,0b), r(aa,ef,60,40), r(06,9f,71,5e), r(51,10,6e,bd),\ + r(f9,8a,21,3e), r(3d,06,dd,96), r(ae,05,3e,dd), r(46,bd,e6,4d),\ + r(b5,8d,54,91), r(05,5d,c4,71), r(6f,d4,06,04), r(ff,15,50,60),\ + r(24,fb,98,19), r(97,e9,bd,d6), r(cc,43,40,89), r(77,9e,d9,67),\ + r(bd,42,e8,b0), r(88,8b,89,07), r(38,5b,19,e7), r(db,ee,c8,79),\ + r(47,0a,7c,a1), r(e9,0f,42,7c), r(c9,1e,84,f8), r(00,00,00,00),\ + r(83,86,80,09), r(48,ed,2b,32), r(ac,70,11,1e), r(4e,72,5a,6c),\ + r(fb,ff,0e,fd), r(56,38,85,0f), r(1e,d5,ae,3d), r(27,39,2d,36),\ + r(64,d9,0f,0a), r(21,a6,5c,68), r(d1,54,5b,9b), r(3a,2e,36,24),\ + r(b1,67,0a,0c), r(0f,e7,57,93), r(d2,96,ee,b4), r(9e,91,9b,1b),\ + r(4f,c5,c0,80), r(a2,20,dc,61), r(69,4b,77,5a), r(16,1a,12,1c),\ + r(0a,ba,93,e2), r(e5,2a,a0,c0), r(43,e0,22,3c), r(1d,17,1b,12),\ + r(0b,0d,09,0e), r(ad,c7,8b,f2), r(b9,a8,b6,2d), r(c8,a9,1e,14),\ + r(85,19,f1,57), r(4c,07,75,af), r(bb,dd,99,ee), r(fd,60,7f,a3),\ + r(9f,26,01,f7), r(bc,f5,72,5c), r(c5,3b,66,44), r(34,7e,fb,5b),\ + r(76,29,43,8b), r(dc,c6,23,cb), r(68,fc,ed,b6), r(63,f1,e4,b8),\ + r(ca,dc,31,d7), r(10,85,63,42), r(40,22,97,13), r(20,11,c6,84),\ + r(7d,24,4a,85), r(f8,3d,bb,d2), r(11,32,f9,ae), r(6d,a1,29,c7),\ + r(4b,2f,9e,1d), r(f3,30,b2,dc), r(ec,52,86,0d), r(d0,e3,c1,77),\ + r(6c,16,b3,2b), r(99,b9,70,a9), r(fa,48,94,11), r(22,64,e9,47),\ + r(c4,8c,fc,a8), r(1a,3f,f0,a0), r(d8,2c,7d,56), r(ef,90,33,22),\ + r(c7,4e,49,87), r(c1,d1,38,d9), r(fe,a2,ca,8c), r(36,0b,d4,98),\ + r(cf,81,f5,a6), r(28,de,7a,a5), r(26,8e,b7,da), r(a4,bf,ad,3f),\ + r(e4,9d,3a,2c), r(0d,92,78,50), r(9b,cc,5f,6a), r(62,46,7e,54),\ + r(c2,13,8d,f6), r(e8,b8,d8,90), r(5e,f7,39,2e), r(f5,af,c3,82),\ + r(be,80,5d,9f), r(7c,93,d0,69), r(a9,2d,d5,6f), r(b3,12,25,cf),\ + r(3b,99,ac,c8), r(a7,7d,18,10), r(6e,63,9c,e8), r(7b,bb,3b,db),\ + r(09,78,26,cd), r(f4,18,59,6e), r(01,b7,9a,ec), r(a8,9a,4f,83),\ + r(65,6e,95,e6), r(7e,e6,ff,aa), r(08,cf,bc,21), r(e6,e8,15,ef),\ + r(d9,9b,e7,ba), r(ce,36,6f,4a), r(d4,09,9f,ea), r(d6,7c,b0,29),\ + r(af,b2,a4,31), r(31,23,3f,2a), r(30,94,a5,c6), r(c0,66,a2,35),\ + r(37,bc,4e,74), r(a6,ca,82,fc), r(b0,d0,90,e0), r(15,d8,a7,33),\ + r(4a,98,04,f1), r(f7,da,ec,41), r(0e,50,cd,7f), r(2f,f6,91,17),\ + r(8d,d6,4d,76), r(4d,b0,ef,43), r(54,4d,aa,cc), r(df,04,96,e4),\ + r(e3,b5,d1,9e), r(1b,88,6a,4c), r(b8,1f,2c,c1), r(7f,51,65,46),\ + r(04,ea,5e,9d), r(5d,35,8c,01), r(73,74,87,fa), r(2e,41,0b,fb),\ + r(5a,1d,67,b3), r(52,d2,db,92), r(33,56,10,e9), r(13,47,d6,6d),\ + r(8c,61,d7,9a), r(7a,0c,a1,37), r(8e,14,f8,59), r(89,3c,13,eb),\ + r(ee,27,a9,ce), r(35,c9,61,b7), r(ed,e5,1c,e1), r(3c,b1,47,7a),\ + r(59,df,d2,9c), r(3f,73,f2,55), r(79,ce,14,18), r(bf,37,c7,73),\ + r(ea,cd,f7,53), r(5b,aa,fd,5f), r(14,6f,3d,df), r(86,db,44,78),\ + r(81,f3,af,ca), r(3e,c4,68,b9), r(2c,34,24,38), r(5f,40,a3,c2),\ + r(72,c3,1d,16), r(0c,25,e2,bc), r(8b,49,3c,28), r(41,95,0d,ff),\ + r(71,01,a8,39), r(de,b3,0c,08), r(9c,e4,b4,d8), r(90,c1,56,64),\ + r(61,84,cb,7b), r(70,b6,32,d5), r(74,5c,6c,48), r(42,57,b8,d0) + +// generate the required tables in the desired endian format + +#undef r +#define r r0 + +#if defined(ONE_TABLE) +static const u_int32_t ft_tab[256] = + { f_table }; +#elif defined(FOUR_TABLES) +static const u_int32_t ft_tab[4][256] = +{ { f_table }, +#undef r +#define r r1 + { f_table }, +#undef r +#define r r2 + { f_table }, +#undef r +#define r r3 + { f_table } +}; +#endif + +#undef r +#define r r0 +#if defined(ONE_TABLE) +static const u_int32_t it_tab[256] = + { i_table }; +#elif defined(FOUR_TABLES) +static const u_int32_t it_tab[4][256] = +{ { i_table }, +#undef r +#define r r1 + { i_table }, +#undef r +#define r r2 + { i_table }, +#undef r +#define r r3 + { i_table } +}; +#endif + +#endif + +#if defined(FIXED_TABLES) && (defined(ONE_LR_TABLE) || defined(FOUR_LR_TABLES)) + +// data for inverse tables (last round) + +#define li_table \ + w(52), w(09), w(6a), w(d5), w(30), w(36), w(a5), w(38),\ + w(bf), w(40), w(a3), w(9e), w(81), w(f3), w(d7), w(fb),\ + w(7c), w(e3), w(39), w(82), w(9b), w(2f), w(ff), w(87),\ + w(34), w(8e), w(43), w(44), w(c4), w(de), w(e9), w(cb),\ + w(54), w(7b), w(94), w(32), w(a6), w(c2), w(23), w(3d),\ + w(ee), w(4c), w(95), w(0b), w(42), w(fa), w(c3), w(4e),\ + w(08), w(2e), w(a1), w(66), w(28), w(d9), w(24), w(b2),\ + w(76), w(5b), w(a2), w(49), w(6d), w(8b), w(d1), w(25),\ + w(72), w(f8), w(f6), w(64), w(86), w(68), w(98), w(16),\ + w(d4), w(a4), w(5c), w(cc), w(5d), w(65), w(b6), w(92),\ + w(6c), w(70), w(48), w(50), w(fd), w(ed), w(b9), w(da),\ + w(5e), w(15), w(46), w(57), w(a7), w(8d), w(9d), w(84),\ + w(90), w(d8), w(ab), w(00), w(8c), w(bc), w(d3), w(0a),\ + w(f7), w(e4), w(58), w(05), w(b8), w(b3), w(45), w(06),\ + w(d0), w(2c), w(1e), w(8f), w(ca), w(3f), w(0f), w(02),\ + w(c1), w(af), w(bd), w(03), w(01), w(13), w(8a), w(6b),\ + w(3a), w(91), w(11), w(41), w(4f), w(67), w(dc), w(ea),\ + w(97), w(f2), w(cf), w(ce), w(f0), w(b4), w(e6), w(73),\ + w(96), w(ac), w(74), w(22), w(e7), w(ad), w(35), w(85),\ + w(e2), w(f9), w(37), w(e8), w(1c), w(75), w(df), w(6e),\ + w(47), w(f1), w(1a), w(71), w(1d), w(29), w(c5), w(89),\ + w(6f), w(b7), w(62), w(0e), w(aa), w(18), w(be), w(1b),\ + w(fc), w(56), w(3e), w(4b), w(c6), w(d2), w(79), w(20),\ + w(9a), w(db), w(c0), w(fe), w(78), w(cd), w(5a), w(f4),\ + w(1f), w(dd), w(a8), w(33), w(88), w(07), w(c7), w(31),\ + w(b1), w(12), w(10), w(59), w(27), w(80), w(ec), w(5f),\ + w(60), w(51), w(7f), w(a9), w(19), w(b5), w(4a), w(0d),\ + w(2d), w(e5), w(7a), w(9f), w(93), w(c9), w(9c), w(ef),\ + w(a0), w(e0), w(3b), w(4d), w(ae), w(2a), w(f5), w(b0),\ + w(c8), w(eb), w(bb), w(3c), w(83), w(53), w(99), w(61),\ + w(17), w(2b), w(04), w(7e), w(ba), w(77), w(d6), w(26),\ + w(e1), w(69), w(14), w(63), w(55), w(21), w(0c), w(7d), + +// generate the required tables in the desired endian format + +#undef r +#define r(p,q,r,s) w0(q) +#if defined(ONE_LR_TABLE) +static const u_int32_t fl_tab[256] = + { f_table }; +#elif defined(FOUR_LR_TABLES) +static const u_int32_t fl_tab[4][256] = +{ { f_table }, +#undef r +#define r(p,q,r,s) w1(q) + { f_table }, +#undef r +#define r(p,q,r,s) w2(q) + { f_table }, +#undef r +#define r(p,q,r,s) w3(q) + { f_table } +}; +#endif + +#undef w +#define w w0 +#if defined(ONE_LR_TABLE) +static const u_int32_t il_tab[256] = + { li_table }; +#elif defined(FOUR_LR_TABLES) +static const u_int32_t il_tab[4][256] = +{ { li_table }, +#undef w +#define w w1 + { li_table }, +#undef w +#define w w2 + { li_table }, +#undef w +#define w w3 + { li_table } +}; +#endif + +#endif + +#if defined(FIXED_TABLES) && (defined(ONE_IM_TABLE) || defined(FOUR_IM_TABLES)) + +#define m_table \ + r(00,00,00,00), r(0b,0d,09,0e), r(16,1a,12,1c), r(1d,17,1b,12),\ + r(2c,34,24,38), r(27,39,2d,36), r(3a,2e,36,24), r(31,23,3f,2a),\ + r(58,68,48,70), r(53,65,41,7e), r(4e,72,5a,6c), r(45,7f,53,62),\ + r(74,5c,6c,48), r(7f,51,65,46), r(62,46,7e,54), r(69,4b,77,5a),\ + r(b0,d0,90,e0), r(bb,dd,99,ee), r(a6,ca,82,fc), r(ad,c7,8b,f2),\ + r(9c,e4,b4,d8), r(97,e9,bd,d6), r(8a,fe,a6,c4), r(81,f3,af,ca),\ + r(e8,b8,d8,90), r(e3,b5,d1,9e), r(fe,a2,ca,8c), r(f5,af,c3,82),\ + r(c4,8c,fc,a8), r(cf,81,f5,a6), r(d2,96,ee,b4), r(d9,9b,e7,ba),\ + r(7b,bb,3b,db), r(70,b6,32,d5), r(6d,a1,29,c7), r(66,ac,20,c9),\ + r(57,8f,1f,e3), r(5c,82,16,ed), r(41,95,0d,ff), r(4a,98,04,f1),\ + r(23,d3,73,ab), r(28,de,7a,a5), r(35,c9,61,b7), r(3e,c4,68,b9),\ + r(0f,e7,57,93), r(04,ea,5e,9d), r(19,fd,45,8f), r(12,f0,4c,81),\ + r(cb,6b,ab,3b), r(c0,66,a2,35), r(dd,71,b9,27), r(d6,7c,b0,29),\ + r(e7,5f,8f,03), r(ec,52,86,0d), r(f1,45,9d,1f), r(fa,48,94,11),\ + r(93,03,e3,4b), r(98,0e,ea,45), r(85,19,f1,57), r(8e,14,f8,59),\ + r(bf,37,c7,73), r(b4,3a,ce,7d), r(a9,2d,d5,6f), r(a2,20,dc,61),\ + r(f6,6d,76,ad), r(fd,60,7f,a3), r(e0,77,64,b1), r(eb,7a,6d,bf),\ + r(da,59,52,95), r(d1,54,5b,9b), r(cc,43,40,89), r(c7,4e,49,87),\ + r(ae,05,3e,dd), r(a5,08,37,d3), r(b8,1f,2c,c1), r(b3,12,25,cf),\ + r(82,31,1a,e5), r(89,3c,13,eb), r(94,2b,08,f9), r(9f,26,01,f7),\ + r(46,bd,e6,4d), r(4d,b0,ef,43), r(50,a7,f4,51), r(5b,aa,fd,5f),\ + r(6a,89,c2,75), r(61,84,cb,7b), r(7c,93,d0,69), r(77,9e,d9,67),\ + r(1e,d5,ae,3d), r(15,d8,a7,33), r(08,cf,bc,21), r(03,c2,b5,2f),\ + r(32,e1,8a,05), r(39,ec,83,0b), r(24,fb,98,19), r(2f,f6,91,17),\ + r(8d,d6,4d,76), r(86,db,44,78), r(9b,cc,5f,6a), r(90,c1,56,64),\ + r(a1,e2,69,4e), r(aa,ef,60,40), r(b7,f8,7b,52), r(bc,f5,72,5c),\ + r(d5,be,05,06), r(de,b3,0c,08), r(c3,a4,17,1a), r(c8,a9,1e,14),\ + r(f9,8a,21,3e), r(f2,87,28,30), r(ef,90,33,22), r(e4,9d,3a,2c),\ + r(3d,06,dd,96), r(36,0b,d4,98), r(2b,1c,cf,8a), r(20,11,c6,84),\ + r(11,32,f9,ae), r(1a,3f,f0,a0), r(07,28,eb,b2), r(0c,25,e2,bc),\ + r(65,6e,95,e6), r(6e,63,9c,e8), r(73,74,87,fa), r(78,79,8e,f4),\ + r(49,5a,b1,de), r(42,57,b8,d0), r(5f,40,a3,c2), r(54,4d,aa,cc),\ + r(f7,da,ec,41), r(fc,d7,e5,4f), r(e1,c0,fe,5d), r(ea,cd,f7,53),\ + r(db,ee,c8,79), r(d0,e3,c1,77), r(cd,f4,da,65), r(c6,f9,d3,6b),\ + r(af,b2,a4,31), r(a4,bf,ad,3f), r(b9,a8,b6,2d), r(b2,a5,bf,23),\ + r(83,86,80,09), r(88,8b,89,07), r(95,9c,92,15), r(9e,91,9b,1b),\ + r(47,0a,7c,a1), r(4c,07,75,af), r(51,10,6e,bd), r(5a,1d,67,b3),\ + r(6b,3e,58,99), r(60,33,51,97), r(7d,24,4a,85), r(76,29,43,8b),\ + r(1f,62,34,d1), r(14,6f,3d,df), r(09,78,26,cd), r(02,75,2f,c3),\ + r(33,56,10,e9), r(38,5b,19,e7), r(25,4c,02,f5), r(2e,41,0b,fb),\ + r(8c,61,d7,9a), r(87,6c,de,94), r(9a,7b,c5,86), r(91,76,cc,88),\ + r(a0,55,f3,a2), r(ab,58,fa,ac), r(b6,4f,e1,be), r(bd,42,e8,b0),\ + r(d4,09,9f,ea), r(df,04,96,e4), r(c2,13,8d,f6), r(c9,1e,84,f8),\ + r(f8,3d,bb,d2), r(f3,30,b2,dc), r(ee,27,a9,ce), r(e5,2a,a0,c0),\ + r(3c,b1,47,7a), r(37,bc,4e,74), r(2a,ab,55,66), r(21,a6,5c,68),\ + r(10,85,63,42), r(1b,88,6a,4c), r(06,9f,71,5e), r(0d,92,78,50),\ + r(64,d9,0f,0a), r(6f,d4,06,04), r(72,c3,1d,16), r(79,ce,14,18),\ + r(48,ed,2b,32), r(43,e0,22,3c), r(5e,f7,39,2e), r(55,fa,30,20),\ + r(01,b7,9a,ec), r(0a,ba,93,e2), r(17,ad,88,f0), r(1c,a0,81,fe),\ + r(2d,83,be,d4), r(26,8e,b7,da), r(3b,99,ac,c8), r(30,94,a5,c6),\ + r(59,df,d2,9c), r(52,d2,db,92), r(4f,c5,c0,80), r(44,c8,c9,8e),\ + r(75,eb,f6,a4), r(7e,e6,ff,aa), r(63,f1,e4,b8), r(68,fc,ed,b6),\ + r(b1,67,0a,0c), r(ba,6a,03,02), r(a7,7d,18,10), r(ac,70,11,1e),\ + r(9d,53,2e,34), r(96,5e,27,3a), r(8b,49,3c,28), r(80,44,35,26),\ + r(e9,0f,42,7c), r(e2,02,4b,72), r(ff,15,50,60), r(f4,18,59,6e),\ + r(c5,3b,66,44), r(ce,36,6f,4a), r(d3,21,74,58), r(d8,2c,7d,56),\ + r(7a,0c,a1,37), r(71,01,a8,39), r(6c,16,b3,2b), r(67,1b,ba,25),\ + r(56,38,85,0f), r(5d,35,8c,01), r(40,22,97,13), r(4b,2f,9e,1d),\ + r(22,64,e9,47), r(29,69,e0,49), r(34,7e,fb,5b), r(3f,73,f2,55),\ + r(0e,50,cd,7f), r(05,5d,c4,71), r(18,4a,df,63), r(13,47,d6,6d),\ + r(ca,dc,31,d7), r(c1,d1,38,d9), r(dc,c6,23,cb), r(d7,cb,2a,c5),\ + r(e6,e8,15,ef), r(ed,e5,1c,e1), r(f0,f2,07,f3), r(fb,ff,0e,fd),\ + r(92,b4,79,a7), r(99,b9,70,a9), r(84,ae,6b,bb), r(8f,a3,62,b5),\ + r(be,80,5d,9f), r(b5,8d,54,91), r(a8,9a,4f,83), r(a3,97,46,8d) + +#undef r +#define r r0 + +#if defined(ONE_IM_TABLE) +static const u_int32_t im_tab[256] = + { m_table }; +#elif defined(FOUR_IM_TABLES) +static const u_int32_t im_tab[4][256] = +{ { m_table }, +#undef r +#define r r1 + { m_table }, +#undef r +#define r r2 + { m_table }, +#undef r +#define r r3 + { m_table } +}; +#endif + +#endif + +#else + +static int tab_gen = 0; + +static unsigned char s_box[256]; // the S box +static unsigned char inv_s_box[256]; // the inverse S box +static u_int32_t rcon_tab[AES_RC_LENGTH]; // table of round constants + +#if defined(ONE_TABLE) +static u_int32_t ft_tab[256]; +static u_int32_t it_tab[256]; +#elif defined(FOUR_TABLES) +static u_int32_t ft_tab[4][256]; +static u_int32_t it_tab[4][256]; +#endif + +#if defined(ONE_LR_TABLE) +static u_int32_t fl_tab[256]; +static u_int32_t il_tab[256]; +#elif defined(FOUR_LR_TABLES) +static u_int32_t fl_tab[4][256]; +static u_int32_t il_tab[4][256]; +#endif + +#if defined(ONE_IM_TABLE) +static u_int32_t im_tab[256]; +#elif defined(FOUR_IM_TABLES) +static u_int32_t im_tab[4][256]; +#endif + +// Generate the tables for the dynamic table option + +#if !defined(FF_TABLES) + +// It will generally be sensible to use tables to compute finite +// field multiplies and inverses but where memory is scarse this +// code might sometimes be better. + +// return 2 ^ (n - 1) where n is the bit number of the highest bit +// set in x with x in the range 1 < x < 0x00000200. This form is +// used so that locals within FFinv can be bytes rather than words + +static unsigned char hibit(const u_int32_t x) +{ unsigned char r = (unsigned char)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +// return the inverse of the finite field element x + +static unsigned char FFinv(const unsigned char x) +{ unsigned char p1 = x, p2 = 0x1b, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) return x; + + for(;;) + { + if(!n1) return v1; + + while(n2 >= n1) + { + n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2); + } + + if(!n2) return v2; + + while(n1 >= n2) + { + n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1); + } + } +} + +// define the finite field multiplies required for Rijndael + +#define FFmul02(x) ((((x) & 0x7f) << 1) ^ ((x) & 0x80 ? 0x1b : 0)) +#define FFmul03(x) ((x) ^ FFmul02(x)) +#define FFmul09(x) ((x) ^ FFmul02(FFmul02(FFmul02(x)))) +#define FFmul0b(x) ((x) ^ FFmul02((x) ^ FFmul02(FFmul02(x)))) +#define FFmul0d(x) ((x) ^ FFmul02(FFmul02((x) ^ FFmul02(x)))) +#define FFmul0e(x) FFmul02((x) ^ FFmul02((x) ^ FFmul02(x))) + +#else + +#define FFinv(x) ((x) ? pow[255 - log[x]]: 0) + +#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0) +#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0) +#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0) +#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0) +#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0) +#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0) + +#endif + +// The forward and inverse affine transformations used in the S-box + +#define fwd_affine(x) \ + (w = (u_int32_t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(unsigned char)(w^(w>>8))) + +#define inv_affine(x) \ + (w = (u_int32_t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(unsigned char)(w^(w>>8))) + +static void gen_tabs(void) +{ u_int32_t i, w; + +#if defined(FF_TABLES) + + unsigned char pow[512], log[256]; + + // log and power tables for GF(2^8) finite field with + // 0x011b as modular polynomial - the simplest primitive + // root is 0x03, used here to generate the tables + + i = 0; w = 1; + do + { + pow[i] = (unsigned char)w; + pow[i + 255] = (unsigned char)w; + log[w] = (unsigned char)i++; + w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0); + } + while (w != 1); + +#endif + + for(i = 0, w = 1; i < AES_RC_LENGTH; ++i) + { + rcon_tab[i] = bytes2word(w, 0, 0, 0); + w = (w << 1) ^ (w & ff_hi ? ff_poly : 0); + } + + for(i = 0; i < 256; ++i) + { unsigned char b; + + s_box[i] = b = fwd_affine(FFinv((unsigned char)i)); + + w = bytes2word(b, 0, 0, 0); +#if defined(ONE_LR_TABLE) + fl_tab[i] = w; +#elif defined(FOUR_LR_TABLES) + fl_tab[0][i] = w; + fl_tab[1][i] = upr(w,1); + fl_tab[2][i] = upr(w,2); + fl_tab[3][i] = upr(w,3); +#endif + w = bytes2word(FFmul02(b), b, b, FFmul03(b)); +#if defined(ONE_TABLE) + ft_tab[i] = w; +#elif defined(FOUR_TABLES) + ft_tab[0][i] = w; + ft_tab[1][i] = upr(w,1); + ft_tab[2][i] = upr(w,2); + ft_tab[3][i] = upr(w,3); +#endif + inv_s_box[i] = b = FFinv(inv_affine((unsigned char)i)); + + w = bytes2word(b, 0, 0, 0); +#if defined(ONE_LR_TABLE) + il_tab[i] = w; +#elif defined(FOUR_LR_TABLES) + il_tab[0][i] = w; + il_tab[1][i] = upr(w,1); + il_tab[2][i] = upr(w,2); + il_tab[3][i] = upr(w,3); +#endif + w = bytes2word(FFmul0e(b), FFmul09(b), FFmul0d(b), FFmul0b(b)); +#if defined(ONE_TABLE) + it_tab[i] = w; +#elif defined(FOUR_TABLES) + it_tab[0][i] = w; + it_tab[1][i] = upr(w,1); + it_tab[2][i] = upr(w,2); + it_tab[3][i] = upr(w,3); +#endif +#if defined(ONE_IM_TABLE) + im_tab[b] = w; +#elif defined(FOUR_IM_TABLES) + im_tab[0][b] = w; + im_tab[1][b] = upr(w,1); + im_tab[2][b] = upr(w,2); + im_tab[3][b] = upr(w,3); +#endif + + } +} + +#endif + +#define no_table(x,box,vf,rf,c) bytes2word( \ + box[bval(vf(x,0,c),rf(0,c))], \ + box[bval(vf(x,1,c),rf(1,c))], \ + box[bval(vf(x,2,c),rf(2,c))], \ + box[bval(vf(x,3,c),rf(3,c))]) + +#define one_table(x,op,tab,vf,rf,c) \ + ( tab[bval(vf(x,0,c),rf(0,c))] \ + ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \ + ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \ + ^ op(tab[bval(vf(x,3,c),rf(3,c))],3)) + +#define four_tables(x,tab,vf,rf,c) \ + ( tab[0][bval(vf(x,0,c),rf(0,c))] \ + ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ + ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ + ^ tab[3][bval(vf(x,3,c),rf(3,c))]) + +#define vf1(x,r,c) (x) +#define rf1(r,c) (r) +#define rf2(r,c) ((r-c)&3) + +#if defined(FOUR_LR_TABLES) +#define ls_box(x,c) four_tables(x,fl_tab,vf1,rf2,c) +#elif defined(ONE_LR_TABLE) +#define ls_box(x,c) one_table(x,upr,fl_tab,vf1,rf2,c) +#else +#define ls_box(x,c) no_table(x,s_box,vf1,rf2,c) +#endif + +#if defined(FOUR_IM_TABLES) +#define inv_mcol(x) four_tables(x,im_tab,vf1,rf1,0) +#elif defined(ONE_IM_TABLE) +#define inv_mcol(x) one_table(x,upr,im_tab,vf1,rf1,0) +#else +#define inv_mcol(x) \ + (f9 = (x),f2 = FFmulX(f9), f4 = FFmulX(f2), f8 = FFmulX(f4), f9 ^= f8, \ + f2 ^= f4 ^ f8 ^ upr(f2 ^ f9,3) ^ upr(f4 ^ f9,2) ^ upr(f9,1)) +#endif + +// Subroutine to set the block size (if variable) in bytes, legal +// values being 16, 24 and 32. + +#if defined(AES_BLOCK_SIZE) +#define nc (AES_BLOCK_SIZE / 4) +#else +#define nc (cx->aes_Ncol) + +void aes_set_blk(aes_context *cx, int n_bytes) +{ +#if !defined(FIXED_TABLES) + if(!tab_gen) { gen_tabs(); tab_gen = 1; } +#endif + + switch(n_bytes) { + case 32: /* bytes */ + case 256: /* bits */ + nc = 8; + break; + case 24: /* bytes */ + case 192: /* bits */ + nc = 6; + break; + case 16: /* bytes */ + case 128: /* bits */ + default: + nc = 4; + break; + } +} + +#endif + +// Initialise the key schedule from the user supplied key. The key +// length is now specified in bytes - 16, 24 or 32 as appropriate. +// This corresponds to bit lengths of 128, 192 and 256 bits, and +// to Nk values of 4, 6 and 8 respectively. + +#define mx(t,f) (*t++ = inv_mcol(*f),f++) +#define cp(t,f) *t++ = *f++ + +#if AES_BLOCK_SIZE == 16 +#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s) +#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s) +#elif AES_BLOCK_SIZE == 24 +#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \ + cp(d,s); cp(d,s) +#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \ + mx(d,s); mx(d,s) +#elif AES_BLOCK_SIZE == 32 +#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \ + cp(d,s); cp(d,s); cp(d,s); cp(d,s) +#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \ + mx(d,s); mx(d,s); mx(d,s); mx(d,s) +#else + +#define cpy(d,s) \ +switch(nc) \ +{ case 8: cp(d,s); cp(d,s); \ + case 6: cp(d,s); cp(d,s); \ + case 4: cp(d,s); cp(d,s); \ + cp(d,s); cp(d,s); \ +} + +#define mix(d,s) \ +switch(nc) \ +{ case 8: mx(d,s); mx(d,s); \ + case 6: mx(d,s); mx(d,s); \ + case 4: mx(d,s); mx(d,s); \ + mx(d,s); mx(d,s); \ +} + +#endif + +void aes_set_key(aes_context *cx, const unsigned char in_key[], int n_bytes, const int f) +{ u_int32_t *kf, *kt, rci; + +#if !defined(FIXED_TABLES) + if(!tab_gen) { gen_tabs(); tab_gen = 1; } +#endif + + switch(n_bytes) { + case 32: /* bytes */ + case 256: /* bits */ + cx->aes_Nkey = 8; + break; + case 24: /* bytes */ + case 192: /* bits */ + cx->aes_Nkey = 6; + break; + case 16: /* bytes */ + case 128: /* bits */ + default: + cx->aes_Nkey = 4; + break; + } + + cx->aes_Nrnd = (cx->aes_Nkey > nc ? cx->aes_Nkey : nc) + 6; + + cx->aes_e_key[0] = const_word_in(in_key ); + cx->aes_e_key[1] = const_word_in(in_key + 4); + cx->aes_e_key[2] = const_word_in(in_key + 8); + cx->aes_e_key[3] = const_word_in(in_key + 12); + + kf = cx->aes_e_key; + kt = kf + nc * (cx->aes_Nrnd + 1) - cx->aes_Nkey; + rci = 0; + + switch(cx->aes_Nkey) + { + case 4: do + { kf[4] = kf[0] ^ ls_box(kf[3],3) ^ rcon_tab[rci++]; + kf[5] = kf[1] ^ kf[4]; + kf[6] = kf[2] ^ kf[5]; + kf[7] = kf[3] ^ kf[6]; + kf += 4; + } + while(kf < kt); + break; + + case 6: cx->aes_e_key[4] = const_word_in(in_key + 16); + cx->aes_e_key[5] = const_word_in(in_key + 20); + do + { kf[ 6] = kf[0] ^ ls_box(kf[5],3) ^ rcon_tab[rci++]; + kf[ 7] = kf[1] ^ kf[ 6]; + kf[ 8] = kf[2] ^ kf[ 7]; + kf[ 9] = kf[3] ^ kf[ 8]; + kf[10] = kf[4] ^ kf[ 9]; + kf[11] = kf[5] ^ kf[10]; + kf += 6; + } + while(kf < kt); + break; + + case 8: cx->aes_e_key[4] = const_word_in(in_key + 16); + cx->aes_e_key[5] = const_word_in(in_key + 20); + cx->aes_e_key[6] = const_word_in(in_key + 24); + cx->aes_e_key[7] = const_word_in(in_key + 28); + do + { kf[ 8] = kf[0] ^ ls_box(kf[7],3) ^ rcon_tab[rci++]; + kf[ 9] = kf[1] ^ kf[ 8]; + kf[10] = kf[2] ^ kf[ 9]; + kf[11] = kf[3] ^ kf[10]; + kf[12] = kf[4] ^ ls_box(kf[11],0); + kf[13] = kf[5] ^ kf[12]; + kf[14] = kf[6] ^ kf[13]; + kf[15] = kf[7] ^ kf[14]; + kf += 8; + } + while (kf < kt); + break; + } + + if(!f) + { u_int32_t i; + + kt = cx->aes_d_key + nc * cx->aes_Nrnd; + kf = cx->aes_e_key; + + cpy(kt, kf); kt -= 2 * nc; + + for(i = 1; i < cx->aes_Nrnd; ++i) + { +#if defined(ONE_TABLE) || defined(FOUR_TABLES) +#if !defined(ONE_IM_TABLE) && !defined(FOUR_IM_TABLES) + u_int32_t f2, f4, f8, f9; +#endif + mix(kt, kf); +#else + cpy(kt, kf); +#endif + kt -= 2 * nc; + } + + cpy(kt, kf); + } +} + +// y = output word, x = input word, r = row, c = column +// for r = 0, 1, 2 and 3 = column accessed for row r + +#if defined(ARRAYS) +#define s(x,c) x[c] +#else +#define s(x,c) x##c +#endif + +// I am grateful to Frank Yellin for the following constructions +// which, given the column (c) of the output state variable that +// is being computed, return the input state variables which are +// needed for each row (r) of the state + +// For the fixed block size options, compilers reduce these two +// expressions to fixed variable references. For variable block +// size code conditional clauses will sometimes be returned + +#define unused 77 // Sunset Strip + +#define fwd_var(x,r,c) \ + ( r==0 ? \ + ( c==0 ? s(x,0) \ + : c==1 ? s(x,1) \ + : c==2 ? s(x,2) \ + : c==3 ? s(x,3) \ + : c==4 ? s(x,4) \ + : c==5 ? s(x,5) \ + : c==6 ? s(x,6) \ + : s(x,7)) \ + : r==1 ? \ + ( c==0 ? s(x,1) \ + : c==1 ? s(x,2) \ + : c==2 ? s(x,3) \ + : c==3 ? nc==4 ? s(x,0) : s(x,4) \ + : c==4 ? s(x,5) \ + : c==5 ? nc==8 ? s(x,6) : s(x,0) \ + : c==6 ? s(x,7) \ + : s(x,0)) \ + : r==2 ? \ + ( c==0 ? nc==8 ? s(x,3) : s(x,2) \ + : c==1 ? nc==8 ? s(x,4) : s(x,3) \ + : c==2 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \ + : c==3 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \ + : c==4 ? nc==8 ? s(x,7) : s(x,0) \ + : c==5 ? nc==8 ? s(x,0) : s(x,1) \ + : c==6 ? s(x,1) \ + : s(x,2)) \ + : \ + ( c==0 ? nc==8 ? s(x,4) : s(x,3) \ + : c==1 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \ + : c==2 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \ + : c==3 ? nc==4 ? s(x,2) : nc==8 ? s(x,7) : s(x,0) \ + : c==4 ? nc==8 ? s(x,0) : s(x,1) \ + : c==5 ? nc==8 ? s(x,1) : s(x,2) \ + : c==6 ? s(x,2) \ + : s(x,3))) + +#define inv_var(x,r,c) \ + ( r==0 ? \ + ( c==0 ? s(x,0) \ + : c==1 ? s(x,1) \ + : c==2 ? s(x,2) \ + : c==3 ? s(x,3) \ + : c==4 ? s(x,4) \ + : c==5 ? s(x,5) \ + : c==6 ? s(x,6) \ + : s(x,7)) \ + : r==1 ? \ + ( c==0 ? nc==4 ? s(x,3) : nc==8 ? s(x,7) : s(x,5) \ + : c==1 ? s(x,0) \ + : c==2 ? s(x,1) \ + : c==3 ? s(x,2) \ + : c==4 ? s(x,3) \ + : c==5 ? s(x,4) \ + : c==6 ? s(x,5) \ + : s(x,6)) \ + : r==2 ? \ + ( c==0 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \ + : c==1 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \ + : c==2 ? nc==8 ? s(x,7) : s(x,0) \ + : c==3 ? nc==8 ? s(x,0) : s(x,1) \ + : c==4 ? nc==8 ? s(x,1) : s(x,2) \ + : c==5 ? nc==8 ? s(x,2) : s(x,3) \ + : c==6 ? s(x,3) \ + : s(x,4)) \ + : \ + ( c==0 ? nc==4 ? s(x,1) : nc==8 ? s(x,4) : s(x,3) \ + : c==1 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \ + : c==2 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \ + : c==3 ? nc==8 ? s(x,7) : s(x,0) \ + : c==4 ? nc==8 ? s(x,0) : s(x,1) \ + : c==5 ? nc==8 ? s(x,1) : s(x,2) \ + : c==6 ? s(x,2) \ + : s(x,3))) + +#define si(y,x,k,c) s(y,c) = const_word_in(x + 4 * c) ^ k[c] +#define so(y,x,c) word_out(y + 4 * c, s(x,c)) + +#if defined(FOUR_TABLES) +#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,ft_tab,fwd_var,rf1,c) +#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,it_tab,inv_var,rf1,c) +#elif defined(ONE_TABLE) +#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,ft_tab,fwd_var,rf1,c) +#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,it_tab,inv_var,rf1,c) +#else +#define fwd_rnd(y,x,k,c) s(y,c) = fwd_mcol(no_table(x,s_box,fwd_var,rf1,c)) ^ (k)[c] +#define inv_rnd(y,x,k,c) s(y,c) = inv_mcol(no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c]) +#endif + +#if defined(FOUR_LR_TABLES) +#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,fl_tab,fwd_var,rf1,c) +#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,il_tab,inv_var,rf1,c) +#elif defined(ONE_LR_TABLE) +#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,fl_tab,fwd_var,rf1,c) +#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,il_tab,inv_var,rf1,c) +#else +#define fwd_lrnd(y,x,k,c) s(y,c) = no_table(x,s_box,fwd_var,rf1,c) ^ (k)[c] +#define inv_lrnd(y,x,k,c) s(y,c) = no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c] +#endif + +#if AES_BLOCK_SIZE == 16 + +#if defined(ARRAYS) +#define locals(y,x) x[4],y[4] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3 +// the following defines prevent the compiler requiring the declaration +// of generated but unused variables in the fwd_var and inv_var macros +#define b04 unused +#define b05 unused +#define b06 unused +#define b07 unused +#define b14 unused +#define b15 unused +#define b16 unused +#define b17 unused +#endif +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) + +#elif AES_BLOCK_SIZE == 24 + +#if defined(ARRAYS) +#define locals(y,x) x[6],y[6] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5, \ + y##0,y##1,y##2,y##3,y##4,y##5 +#define b06 unused +#define b07 unused +#define b16 unused +#define b17 unused +#endif +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); \ + s(y,4) = s(x,4); s(y,5) = s(x,5); +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); \ + si(y,x,k,3); si(y,x,k,4); si(y,x,k,5) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); \ + so(y,x,3); so(y,x,4); so(y,x,5) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); \ + rm(y,x,k,3); rm(y,x,k,4); rm(y,x,k,5) +#else + +#if defined(ARRAYS) +#define locals(y,x) x[8],y[8] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5,x##6,x##7, \ + y##0,y##1,y##2,y##3,y##4,y##5,y##6,y##7 +#endif +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); \ + s(y,4) = s(x,4); s(y,5) = s(x,5); \ + s(y,6) = s(x,6); s(y,7) = s(x,7); + +#if AES_BLOCK_SIZE == 32 + +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3); \ + si(y,x,k,4); si(y,x,k,5); si(y,x,k,6); si(y,x,k,7) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3); \ + so(y,x,4); so(y,x,5); so(y,x,6); so(y,x,7) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3); \ + rm(y,x,k,4); rm(y,x,k,5); rm(y,x,k,6); rm(y,x,k,7) +#else + +#define state_in(y,x,k) \ +switch(nc) \ +{ case 8: si(y,x,k,7); si(y,x,k,6); \ + case 6: si(y,x,k,5); si(y,x,k,4); \ + case 4: si(y,x,k,3); si(y,x,k,2); \ + si(y,x,k,1); si(y,x,k,0); \ +} + +#define state_out(y,x) \ +switch(nc) \ +{ case 8: so(y,x,7); so(y,x,6); \ + case 6: so(y,x,5); so(y,x,4); \ + case 4: so(y,x,3); so(y,x,2); \ + so(y,x,1); so(y,x,0); \ +} + +#if defined(FAST_VARIABLE) + +#define round(rm,y,x,k) \ +switch(nc) \ +{ case 8: rm(y,x,k,7); rm(y,x,k,6); \ + rm(y,x,k,5); rm(y,x,k,4); \ + rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ + break; \ + case 6: rm(y,x,k,5); rm(y,x,k,4); \ + rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ + break; \ + case 4: rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ + break; \ +} +#else + +#define round(rm,y,x,k) \ +switch(nc) \ +{ case 8: rm(y,x,k,7); rm(y,x,k,6); \ + case 6: rm(y,x,k,5); rm(y,x,k,4); \ + case 4: rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ +} + +#endif + +#endif +#endif + +void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[]) +{ u_int32_t locals(b0, b1); + const u_int32_t *kp = cx->aes_e_key; + +#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) + u_int32_t f2; +#endif + + state_in(b0, in_blk, kp); kp += nc; + +#if defined(UNROLL) + + switch(cx->aes_Nrnd) + { + case 14: round(fwd_rnd, b1, b0, kp ); + round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 12: round(fwd_rnd, b1, b0, kp ); + round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 10: round(fwd_rnd, b1, b0, kp ); + round(fwd_rnd, b0, b1, kp + nc); + round(fwd_rnd, b1, b0, kp + 2 * nc); + round(fwd_rnd, b0, b1, kp + 3 * nc); + round(fwd_rnd, b1, b0, kp + 4 * nc); + round(fwd_rnd, b0, b1, kp + 5 * nc); + round(fwd_rnd, b1, b0, kp + 6 * nc); + round(fwd_rnd, b0, b1, kp + 7 * nc); + round(fwd_rnd, b1, b0, kp + 8 * nc); + round(fwd_lrnd, b0, b1, kp + 9 * nc); + } + +#elif defined(PARTIAL_UNROLL) + { u_int32_t rnd; + + for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd) + { + round(fwd_rnd, b1, b0, kp); + round(fwd_rnd, b0, b1, kp + nc); kp += 2 * nc; + } + + round(fwd_rnd, b1, b0, kp); + round(fwd_lrnd, b0, b1, kp + nc); + } +#else + { u_int32_t rnd; + + for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd) + { + round(fwd_rnd, b1, b0, kp); + l_copy(b0, b1); kp += nc; + } + + round(fwd_lrnd, b0, b1, kp); + } +#endif + + state_out(out_blk, b0); +} + +void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[]) +{ u_int32_t locals(b0, b1); + const u_int32_t *kp = cx->aes_d_key; + +#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) + u_int32_t f2, f4, f8, f9; +#endif + + state_in(b0, in_blk, kp); kp += nc; + +#if defined(UNROLL) + + switch(cx->aes_Nrnd) + { + case 14: round(inv_rnd, b1, b0, kp ); + round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 12: round(inv_rnd, b1, b0, kp ); + round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 10: round(inv_rnd, b1, b0, kp ); + round(inv_rnd, b0, b1, kp + nc); + round(inv_rnd, b1, b0, kp + 2 * nc); + round(inv_rnd, b0, b1, kp + 3 * nc); + round(inv_rnd, b1, b0, kp + 4 * nc); + round(inv_rnd, b0, b1, kp + 5 * nc); + round(inv_rnd, b1, b0, kp + 6 * nc); + round(inv_rnd, b0, b1, kp + 7 * nc); + round(inv_rnd, b1, b0, kp + 8 * nc); + round(inv_lrnd, b0, b1, kp + 9 * nc); + } + +#elif defined(PARTIAL_UNROLL) + { u_int32_t rnd; + + for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd) + { + round(inv_rnd, b1, b0, kp); + round(inv_rnd, b0, b1, kp + nc); kp += 2 * nc; + } + + round(inv_rnd, b1, b0, kp); + round(inv_lrnd, b0, b1, kp + nc); + } +#else + { u_int32_t rnd; + + for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd) + { + round(inv_rnd, b1, b0, kp); + l_copy(b0, b1); kp += nc; + } + + round(inv_lrnd, b0, b1, kp); + } +#endif + + state_out(out_blk, b0); +} diff --git a/src/libcrypto/libaes/aes.h b/src/libcrypto/libaes/aes.h new file mode 100644 index 000000000..4f1e3b335 --- /dev/null +++ b/src/libcrypto/libaes/aes.h @@ -0,0 +1,97 @@ +// I retain copyright in this code but I encourage its free use provided +// that I don't carry any responsibility for the results. I am especially +// happy to see it used in free and open source software. If you do use +// it I would appreciate an acknowledgement of its origin in the code or +// the product that results and I would also appreciate knowing a little +// about the use to which it is being put. I am grateful to Frank Yellin +// for some ideas that are used in this implementation. +// +// Dr B. R. Gladman 6th April 2001. +// +// This is an implementation of the AES encryption algorithm (Rijndael) +// designed by Joan Daemen and Vincent Rijmen. This version is designed +// to provide both fixed and dynamic block and key lengths and can also +// run with either big or little endian internal byte order (see aes.h). +// It inputs block and key lengths in bytes with the legal values being +// 16, 24 and 32. + +/* + * Modified by Jari Ruusu, May 1 2001 + * - Fixed some compile warnings, code was ok but gcc warned anyway. + * - Changed basic types: byte -> unsigned char, word -> u_int32_t + * - Major name space cleanup: Names visible to outside now begin + * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c + * - Removed C++ and DLL support as part of name space cleanup. + * - Eliminated unnecessary recomputation of tables. (actual bug fix) + * - Merged precomputed constant tables to aes.c file. + * - Removed data alignment restrictions for portability reasons. + * - Made block and key lengths accept bit count (128/192/256) + * as well byte count (16/24/32). + * - Removed all error checks. This change also eliminated the need + * to preinitialize the context struct to zero. + * - Removed some totally unused constants. + */ + +#ifndef _AES_H +#define _AES_H + +#if defined(__linux__) && defined(__KERNEL__) +# include +#else +# include +#endif + +// CONFIGURATION OPTIONS (see also aes.c) +// +// Define AES_BLOCK_SIZE to set the cipher block size (16, 24 or 32) or +// leave this undefined for dynamically variable block size (this will +// result in much slower code). +// IMPORTANT NOTE: AES_BLOCK_SIZE is in BYTES (16, 24, 32 or undefined). If +// left undefined a slower version providing variable block length is compiled + +#define AES_BLOCK_SIZE 16 + +// The number of key schedule words for different block and key lengths +// allowing for method of computation which requires the length to be a +// multiple of the key length +// +// Nk = 4 6 8 +// ------------- +// Nb = 4 | 60 60 64 +// 6 | 96 90 96 +// 8 | 120 120 120 + +#if !defined(AES_BLOCK_SIZE) || (AES_BLOCK_SIZE == 32) +#define AES_KS_LENGTH 120 +#define AES_RC_LENGTH 29 +#else +#define AES_KS_LENGTH 4 * AES_BLOCK_SIZE +#define AES_RC_LENGTH (9 * AES_BLOCK_SIZE) / 8 - 8 +#endif + +typedef struct +{ + u_int32_t aes_Nkey; // the number of words in the key input block + u_int32_t aes_Nrnd; // the number of cipher rounds + u_int32_t aes_e_key[AES_KS_LENGTH]; // the encryption key schedule + u_int32_t aes_d_key[AES_KS_LENGTH]; // the decryption key schedule +#if !defined(AES_BLOCK_SIZE) + u_int32_t aes_Ncol; // the number of columns in the cipher state +#endif +} aes_context; + +// THE CIPHER INTERFACE + +#if !defined(AES_BLOCK_SIZE) +extern void aes_set_blk(aes_context *, const int); +#endif +extern void aes_set_key(aes_context *, const unsigned char [], const int, const int); +extern void aes_encrypt(const aes_context *, const unsigned char [], unsigned char []); +extern void aes_decrypt(const aes_context *, const unsigned char [], unsigned char []); + +// The block length inputs to aes_set_block and aes_set_key are in numbers +// of bytes or bits. The calls to subroutines must be made in the above +// order but multiple calls can be made without repeating earlier calls +// if their parameters have not changed. + +#endif // _AES_H diff --git a/src/libcrypto/libaes/aes_cbc.c b/src/libcrypto/libaes/aes_cbc.c new file mode 100644 index 000000000..962dd1a35 --- /dev/null +++ b/src/libcrypto/libaes/aes_cbc.c @@ -0,0 +1,13 @@ +#ifdef __KERNEL__ +#include +#else +#include +#endif +#include "aes_cbc.h" +#include "cbc_generic.h" +/* returns bool success */ +int AES_set_key(aes_context *aes_ctx, const u_int8_t *key, int keysize) { + aes_set_key(aes_ctx, key, keysize, 0); + return 1; +} +CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt); diff --git a/src/libcrypto/libaes/aes_cbc.h b/src/libcrypto/libaes/aes_cbc.h new file mode 100644 index 000000000..92f5d77f5 --- /dev/null +++ b/src/libcrypto/libaes/aes_cbc.h @@ -0,0 +1,4 @@ +/* Glue header */ +#include "aes.h" +int AES_set_key(aes_context *aes_ctx, const u_int8_t * key, int keysize); +int AES_cbc_encrypt(aes_context *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt); diff --git a/src/libcrypto/libaes/aes_xcbc_mac.c b/src/libcrypto/libaes/aes_xcbc_mac.c new file mode 100644 index 000000000..89d7bc067 --- /dev/null +++ b/src/libcrypto/libaes/aes_xcbc_mac.c @@ -0,0 +1,67 @@ +#ifdef __KERNEL__ +#include +#include +#define DEBUG(x) +#else +#include +#include +#define DEBUG(x) x +#endif + +#include "aes.h" +#include "aes_xcbc_mac.h" + +int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen) +{ + int ret=1; + aes_block kn[3] = { + { 0x01010101, 0x01010101, 0x01010101, 0x01010101 }, + { 0x02020202, 0x02020202, 0x02020202, 0x02020202 }, + { 0x03030303, 0x03030303, 0x03030303, 0x03030303 }, + }; + aes_set_key(&ctxm->ctx_k1, key, keylen, 0); + aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[0], (u_int8_t *) kn[0]); + aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[1], (u_int8_t *) ctxm->k2); + aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[2], (u_int8_t *) ctxm->k3); + aes_set_key(&ctxm->ctx_k1, (u_int8_t *) kn[0], 16, 0); + return ret; +} +static void do_pad_xor(u_int8_t *out, const u_int8_t *in, int len) { + int pos=0; + for (pos=1; pos <= 16; pos++, in++, out++) { + if (pos <= len) + *out ^= *in; + if (pos > len) { + DEBUG(printf("put 0x80 at pos=%d\n", pos)); + *out ^= 0x80; + break; + } + } +} +static void xor_block(aes_block res, const aes_block op) { + res[0] ^= op[0]; + res[1] ^= op[1]; + res[2] ^= op[2]; + res[3] ^= op[3]; +} +int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]) { + int ret=ilen; + u_int32_t out[4] = { 0, 0, 0, 0 }; + for (; ilen > 16 ; ilen-=16) { + xor_block(out, (const u_int32_t*) &in[0]); + aes_encrypt(&ctxm->ctx_k1, in, (u_int8_t *)&out[0]); + in+=16; + } + do_pad_xor((u_int8_t *)&out, in, ilen); + if (ilen==16) { + DEBUG(printf("using k3\n")); + xor_block(out, ctxm->k3); + } + else + { + DEBUG(printf("using k2\n")); + xor_block(out, ctxm->k2); + } + aes_encrypt(&ctxm->ctx_k1, (u_int8_t *)out, hash); + return ret; +} diff --git a/src/libcrypto/libaes/aes_xcbc_mac.h b/src/libcrypto/libaes/aes_xcbc_mac.h new file mode 100644 index 000000000..baf438cd4 --- /dev/null +++ b/src/libcrypto/libaes/aes_xcbc_mac.h @@ -0,0 +1,12 @@ +#ifndef _AES_XCBC_MAC_H +#define _AES_XCBC_MAC_H + +typedef u_int32_t aes_block[4]; +typedef struct { + aes_context ctx_k1; + aes_block k2; + aes_block k3; +} aes_context_mac; +int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen); +int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]); +#endif /* _AES_XCBC_MAC_H */ diff --git a/src/libcrypto/libblowfish/bf_enc.c b/src/libcrypto/libblowfish/bf_enc.c new file mode 100644 index 000000000..aa6c79812 --- /dev/null +++ b/src/libcrypto/libblowfish/bf_enc.c @@ -0,0 +1,306 @@ +/* crypto/bf/bf_enc.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "blowfish.h" +#include "bf_locl.h" + +/* Blowfish as implemented from 'Blowfish: Springer-Verlag paper' + * (From LECTURE NOTES IN COMPUTER SCIENCE 809, FAST SOFTWARE ENCRYPTION, + * CAMBRIDGE SECURITY WORKSHOP, CAMBRIDGE, U.K., DECEMBER 9-11, 1993) + */ + +#if (BF_ROUNDS != 16) && (BF_ROUNDS != 20) +#error If you set BF_ROUNDS to some value other than 16 or 20, you will have \ +to modify the code. +#endif + +void BF_encrypt(BF_LONG *data, const BF_KEY *key) + { +#ifndef BF_PTR2 + const BF_LONG *p,*s; + BF_LONG l,r; + + p=key->P; + s= &(key->S[0]); + l=data[0]; + r=data[1]; + + l^=p[0]; + BF_ENC(r,l,s,p[ 1]); + BF_ENC(l,r,s,p[ 2]); + BF_ENC(r,l,s,p[ 3]); + BF_ENC(l,r,s,p[ 4]); + BF_ENC(r,l,s,p[ 5]); + BF_ENC(l,r,s,p[ 6]); + BF_ENC(r,l,s,p[ 7]); + BF_ENC(l,r,s,p[ 8]); + BF_ENC(r,l,s,p[ 9]); + BF_ENC(l,r,s,p[10]); + BF_ENC(r,l,s,p[11]); + BF_ENC(l,r,s,p[12]); + BF_ENC(r,l,s,p[13]); + BF_ENC(l,r,s,p[14]); + BF_ENC(r,l,s,p[15]); + BF_ENC(l,r,s,p[16]); +#if BF_ROUNDS == 20 + BF_ENC(r,l,s,p[17]); + BF_ENC(l,r,s,p[18]); + BF_ENC(r,l,s,p[19]); + BF_ENC(l,r,s,p[20]); +#endif + r^=p[BF_ROUNDS+1]; + + data[1]=l&0xffffffffL; + data[0]=r&0xffffffffL; +#else + BF_LONG l,r,t,*k; + + l=data[0]; + r=data[1]; + k=(BF_LONG*)key; + + l^=k[0]; + BF_ENC(r,l,k, 1); + BF_ENC(l,r,k, 2); + BF_ENC(r,l,k, 3); + BF_ENC(l,r,k, 4); + BF_ENC(r,l,k, 5); + BF_ENC(l,r,k, 6); + BF_ENC(r,l,k, 7); + BF_ENC(l,r,k, 8); + BF_ENC(r,l,k, 9); + BF_ENC(l,r,k,10); + BF_ENC(r,l,k,11); + BF_ENC(l,r,k,12); + BF_ENC(r,l,k,13); + BF_ENC(l,r,k,14); + BF_ENC(r,l,k,15); + BF_ENC(l,r,k,16); +#if BF_ROUNDS == 20 + BF_ENC(r,l,k,17); + BF_ENC(l,r,k,18); + BF_ENC(r,l,k,19); + BF_ENC(l,r,k,20); +#endif + r^=k[BF_ROUNDS+1]; + + data[1]=l&0xffffffffL; + data[0]=r&0xffffffffL; +#endif + } + +#ifndef BF_DEFAULT_OPTIONS + +void BF_decrypt(BF_LONG *data, const BF_KEY *key) + { +#ifndef BF_PTR2 + const BF_LONG *p,*s; + BF_LONG l,r; + + p=key->P; + s= &(key->S[0]); + l=data[0]; + r=data[1]; + + l^=p[BF_ROUNDS+1]; +#if BF_ROUNDS == 20 + BF_ENC(r,l,s,p[20]); + BF_ENC(l,r,s,p[19]); + BF_ENC(r,l,s,p[18]); + BF_ENC(l,r,s,p[17]); +#endif + BF_ENC(r,l,s,p[16]); + BF_ENC(l,r,s,p[15]); + BF_ENC(r,l,s,p[14]); + BF_ENC(l,r,s,p[13]); + BF_ENC(r,l,s,p[12]); + BF_ENC(l,r,s,p[11]); + BF_ENC(r,l,s,p[10]); + BF_ENC(l,r,s,p[ 9]); + BF_ENC(r,l,s,p[ 8]); + BF_ENC(l,r,s,p[ 7]); + BF_ENC(r,l,s,p[ 6]); + BF_ENC(l,r,s,p[ 5]); + BF_ENC(r,l,s,p[ 4]); + BF_ENC(l,r,s,p[ 3]); + BF_ENC(r,l,s,p[ 2]); + BF_ENC(l,r,s,p[ 1]); + r^=p[0]; + + data[1]=l&0xffffffffL; + data[0]=r&0xffffffffL; +#else + BF_LONG l,r,t,*k; + + l=data[0]; + r=data[1]; + k=(BF_LONG *)key; + + l^=k[BF_ROUNDS+1]; +#if BF_ROUNDS == 20 + BF_ENC(r,l,k,20); + BF_ENC(l,r,k,19); + BF_ENC(r,l,k,18); + BF_ENC(l,r,k,17); +#endif + BF_ENC(r,l,k,16); + BF_ENC(l,r,k,15); + BF_ENC(r,l,k,14); + BF_ENC(l,r,k,13); + BF_ENC(r,l,k,12); + BF_ENC(l,r,k,11); + BF_ENC(r,l,k,10); + BF_ENC(l,r,k, 9); + BF_ENC(r,l,k, 8); + BF_ENC(l,r,k, 7); + BF_ENC(r,l,k, 6); + BF_ENC(l,r,k, 5); + BF_ENC(r,l,k, 4); + BF_ENC(l,r,k, 3); + BF_ENC(r,l,k, 2); + BF_ENC(l,r,k, 1); + r^=k[0]; + + data[1]=l&0xffffffffL; + data[0]=r&0xffffffffL; +#endif + } + +void BF_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int encrypt) + { + BF_LONG tin0,tin1; + BF_LONG tout0,tout1,xor0,xor1; + long l=length; + BF_LONG tin[2]; + + if (encrypt) + { + n2l(ivec,tout0); + n2l(ivec,tout1); + ivec-=8; + for (l-=8; l>=0; l-=8) + { + n2l(in,tin0); + n2l(in,tin1); + tin0^=tout0; + tin1^=tout1; + tin[0]=tin0; + tin[1]=tin1; + BF_encrypt(tin,schedule); + tout0=tin[0]; + tout1=tin[1]; + l2n(tout0,out); + l2n(tout1,out); + } + if (l != -8) + { + n2ln(in,tin0,tin1,l+8); + tin0^=tout0; + tin1^=tout1; + tin[0]=tin0; + tin[1]=tin1; + BF_encrypt(tin,schedule); + tout0=tin[0]; + tout1=tin[1]; + l2n(tout0,out); + l2n(tout1,out); + } + l2n(tout0,ivec); + l2n(tout1,ivec); + } + else + { + n2l(ivec,xor0); + n2l(ivec,xor1); + ivec-=8; + for (l-=8; l>=0; l-=8) + { + n2l(in,tin0); + n2l(in,tin1); + tin[0]=tin0; + tin[1]=tin1; + BF_decrypt(tin,schedule); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2n(tout0,out); + l2n(tout1,out); + xor0=tin0; + xor1=tin1; + } + if (l != -8) + { + n2l(in,tin0); + n2l(in,tin1); + tin[0]=tin0; + tin[1]=tin1; + BF_decrypt(tin,schedule); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2nn(tout0,tout1,out,l+8); + xor0=tin0; + xor1=tin1; + } + l2n(xor0,ivec); + l2n(xor1,ivec); + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; + } + +#endif diff --git a/src/libcrypto/libblowfish/bf_locl.h b/src/libcrypto/libblowfish/bf_locl.h new file mode 100644 index 000000000..283bf4c43 --- /dev/null +++ b/src/libcrypto/libblowfish/bf_locl.h @@ -0,0 +1,218 @@ +/* crypto/bf/bf_locl.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_BF_LOCL_H +#define HEADER_BF_LOCL_H + +#undef c2l +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<<24L) + +/* NOTE - c is not incremented as per c2l */ +#undef c2ln +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c))))<<24L; \ + case 7: l2|=((unsigned long)(*(--(c))))<<16L; \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \ + case 5: l2|=((unsigned long)(*(--(c)))); \ + case 4: l1 =((unsigned long)(*(--(c))))<<24L; \ + case 3: l1|=((unsigned long)(*(--(c))))<<16L; \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \ + case 1: l1|=((unsigned long)(*(--(c)))); \ + } \ + } + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#undef l2cn +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +/* NOTE - c is not incremented as per n2l */ +#define n2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c)))) ; \ + case 7: l2|=((unsigned long)(*(--(c))))<< 8; \ + case 6: l2|=((unsigned long)(*(--(c))))<<16; \ + case 5: l2|=((unsigned long)(*(--(c))))<<24; \ + case 4: l1 =((unsigned long)(*(--(c)))) ; \ + case 3: l1|=((unsigned long)(*(--(c))))<< 8; \ + case 2: l1|=((unsigned long)(*(--(c))))<<16; \ + case 1: l1|=((unsigned long)(*(--(c))))<<24; \ + } \ + } + +/* NOTE - c is not incremented as per l2n */ +#define l2nn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1) )&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + } \ + } + +#undef n2l +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))) + +#undef l2n +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* This is actually a big endian algorithm, the most significant byte + * is used to lookup array 0 */ + +#if defined(BF_PTR2) + +/* + * This is basically a special Intel version. Point is that Intel + * doesn't have many registers, but offers a reach choice of addressing + * modes. So we spare some registers by directly traversing BF_KEY + * structure and hiring the most decorated addressing mode. The code + * generated by EGCS is *perfectly* competitive with assembler + * implementation! + */ +#define BF_ENC(LL,R,KEY,Pi) (\ + LL^=KEY[Pi], \ + t= KEY[BF_ROUNDS+2 + 0 + ((R>>24)&0xFF)], \ + t+= KEY[BF_ROUNDS+2 + 256 + ((R>>16)&0xFF)], \ + t^= KEY[BF_ROUNDS+2 + 512 + ((R>>8 )&0xFF)], \ + t+= KEY[BF_ROUNDS+2 + 768 + ((R )&0xFF)], \ + LL^=t \ + ) + +#elif defined(BF_PTR) + +#ifndef BF_LONG_LOG2 +#define BF_LONG_LOG2 2 /* default to BF_LONG being 32 bits */ +#endif +#define BF_M (0xFF<>BF_i)&BF_M gets folded into a single instruction, namely + * rlwinm. So let'em double-check if their compiler does it. + */ + +#define BF_ENC(LL,R,S,P) ( \ + LL^=P, \ + LL^= (((*(BF_LONG *)((unsigned char *)&(S[ 0])+((R>>BF_0)&BF_M))+ \ + *(BF_LONG *)((unsigned char *)&(S[256])+((R>>BF_1)&BF_M)))^ \ + *(BF_LONG *)((unsigned char *)&(S[512])+((R>>BF_2)&BF_M)))+ \ + *(BF_LONG *)((unsigned char *)&(S[768])+((R<>24)&0xff)] + \ + S[0x0100+((int)(R>>16)&0xff)])^ \ + S[0x0200+((int)(R>> 8)&0xff)])+ \ + S[0x0300+((int)(R )&0xff)])&0xffffffffL \ + ) +#endif + +#endif diff --git a/src/libcrypto/libblowfish/bf_pi.h b/src/libcrypto/libblowfish/bf_pi.h new file mode 100644 index 000000000..9949513c6 --- /dev/null +++ b/src/libcrypto/libblowfish/bf_pi.h @@ -0,0 +1,325 @@ +/* crypto/bf/bf_pi.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +static const BF_KEY bf_init= { + { + 0x243f6a88L, 0x85a308d3L, 0x13198a2eL, 0x03707344L, + 0xa4093822L, 0x299f31d0L, 0x082efa98L, 0xec4e6c89L, + 0x452821e6L, 0x38d01377L, 0xbe5466cfL, 0x34e90c6cL, + 0xc0ac29b7L, 0xc97c50ddL, 0x3f84d5b5L, 0xb5470917L, + 0x9216d5d9L, 0x8979fb1b + },{ + 0xd1310ba6L, 0x98dfb5acL, 0x2ffd72dbL, 0xd01adfb7L, + 0xb8e1afedL, 0x6a267e96L, 0xba7c9045L, 0xf12c7f99L, + 0x24a19947L, 0xb3916cf7L, 0x0801f2e2L, 0x858efc16L, + 0x636920d8L, 0x71574e69L, 0xa458fea3L, 0xf4933d7eL, + 0x0d95748fL, 0x728eb658L, 0x718bcd58L, 0x82154aeeL, + 0x7b54a41dL, 0xc25a59b5L, 0x9c30d539L, 0x2af26013L, + 0xc5d1b023L, 0x286085f0L, 0xca417918L, 0xb8db38efL, + 0x8e79dcb0L, 0x603a180eL, 0x6c9e0e8bL, 0xb01e8a3eL, + 0xd71577c1L, 0xbd314b27L, 0x78af2fdaL, 0x55605c60L, + 0xe65525f3L, 0xaa55ab94L, 0x57489862L, 0x63e81440L, + 0x55ca396aL, 0x2aab10b6L, 0xb4cc5c34L, 0x1141e8ceL, + 0xa15486afL, 0x7c72e993L, 0xb3ee1411L, 0x636fbc2aL, + 0x2ba9c55dL, 0x741831f6L, 0xce5c3e16L, 0x9b87931eL, + 0xafd6ba33L, 0x6c24cf5cL, 0x7a325381L, 0x28958677L, + 0x3b8f4898L, 0x6b4bb9afL, 0xc4bfe81bL, 0x66282193L, + 0x61d809ccL, 0xfb21a991L, 0x487cac60L, 0x5dec8032L, + 0xef845d5dL, 0xe98575b1L, 0xdc262302L, 0xeb651b88L, + 0x23893e81L, 0xd396acc5L, 0x0f6d6ff3L, 0x83f44239L, + 0x2e0b4482L, 0xa4842004L, 0x69c8f04aL, 0x9e1f9b5eL, + 0x21c66842L, 0xf6e96c9aL, 0x670c9c61L, 0xabd388f0L, + 0x6a51a0d2L, 0xd8542f68L, 0x960fa728L, 0xab5133a3L, + 0x6eef0b6cL, 0x137a3be4L, 0xba3bf050L, 0x7efb2a98L, + 0xa1f1651dL, 0x39af0176L, 0x66ca593eL, 0x82430e88L, + 0x8cee8619L, 0x456f9fb4L, 0x7d84a5c3L, 0x3b8b5ebeL, + 0xe06f75d8L, 0x85c12073L, 0x401a449fL, 0x56c16aa6L, + 0x4ed3aa62L, 0x363f7706L, 0x1bfedf72L, 0x429b023dL, + 0x37d0d724L, 0xd00a1248L, 0xdb0fead3L, 0x49f1c09bL, + 0x075372c9L, 0x80991b7bL, 0x25d479d8L, 0xf6e8def7L, + 0xe3fe501aL, 0xb6794c3bL, 0x976ce0bdL, 0x04c006baL, + 0xc1a94fb6L, 0x409f60c4L, 0x5e5c9ec2L, 0x196a2463L, + 0x68fb6fafL, 0x3e6c53b5L, 0x1339b2ebL, 0x3b52ec6fL, + 0x6dfc511fL, 0x9b30952cL, 0xcc814544L, 0xaf5ebd09L, + 0xbee3d004L, 0xde334afdL, 0x660f2807L, 0x192e4bb3L, + 0xc0cba857L, 0x45c8740fL, 0xd20b5f39L, 0xb9d3fbdbL, + 0x5579c0bdL, 0x1a60320aL, 0xd6a100c6L, 0x402c7279L, + 0x679f25feL, 0xfb1fa3ccL, 0x8ea5e9f8L, 0xdb3222f8L, + 0x3c7516dfL, 0xfd616b15L, 0x2f501ec8L, 0xad0552abL, + 0x323db5faL, 0xfd238760L, 0x53317b48L, 0x3e00df82L, + 0x9e5c57bbL, 0xca6f8ca0L, 0x1a87562eL, 0xdf1769dbL, + 0xd542a8f6L, 0x287effc3L, 0xac6732c6L, 0x8c4f5573L, + 0x695b27b0L, 0xbbca58c8L, 0xe1ffa35dL, 0xb8f011a0L, + 0x10fa3d98L, 0xfd2183b8L, 0x4afcb56cL, 0x2dd1d35bL, + 0x9a53e479L, 0xb6f84565L, 0xd28e49bcL, 0x4bfb9790L, + 0xe1ddf2daL, 0xa4cb7e33L, 0x62fb1341L, 0xcee4c6e8L, + 0xef20cadaL, 0x36774c01L, 0xd07e9efeL, 0x2bf11fb4L, + 0x95dbda4dL, 0xae909198L, 0xeaad8e71L, 0x6b93d5a0L, + 0xd08ed1d0L, 0xafc725e0L, 0x8e3c5b2fL, 0x8e7594b7L, + 0x8ff6e2fbL, 0xf2122b64L, 0x8888b812L, 0x900df01cL, + 0x4fad5ea0L, 0x688fc31cL, 0xd1cff191L, 0xb3a8c1adL, + 0x2f2f2218L, 0xbe0e1777L, 0xea752dfeL, 0x8b021fa1L, + 0xe5a0cc0fL, 0xb56f74e8L, 0x18acf3d6L, 0xce89e299L, + 0xb4a84fe0L, 0xfd13e0b7L, 0x7cc43b81L, 0xd2ada8d9L, + 0x165fa266L, 0x80957705L, 0x93cc7314L, 0x211a1477L, + 0xe6ad2065L, 0x77b5fa86L, 0xc75442f5L, 0xfb9d35cfL, + 0xebcdaf0cL, 0x7b3e89a0L, 0xd6411bd3L, 0xae1e7e49L, + 0x00250e2dL, 0x2071b35eL, 0x226800bbL, 0x57b8e0afL, + 0x2464369bL, 0xf009b91eL, 0x5563911dL, 0x59dfa6aaL, + 0x78c14389L, 0xd95a537fL, 0x207d5ba2L, 0x02e5b9c5L, + 0x83260376L, 0x6295cfa9L, 0x11c81968L, 0x4e734a41L, + 0xb3472dcaL, 0x7b14a94aL, 0x1b510052L, 0x9a532915L, + 0xd60f573fL, 0xbc9bc6e4L, 0x2b60a476L, 0x81e67400L, + 0x08ba6fb5L, 0x571be91fL, 0xf296ec6bL, 0x2a0dd915L, + 0xb6636521L, 0xe7b9f9b6L, 0xff34052eL, 0xc5855664L, + 0x53b02d5dL, 0xa99f8fa1L, 0x08ba4799L, 0x6e85076aL, + 0x4b7a70e9L, 0xb5b32944L, 0xdb75092eL, 0xc4192623L, + 0xad6ea6b0L, 0x49a7df7dL, 0x9cee60b8L, 0x8fedb266L, + 0xecaa8c71L, 0x699a17ffL, 0x5664526cL, 0xc2b19ee1L, + 0x193602a5L, 0x75094c29L, 0xa0591340L, 0xe4183a3eL, + 0x3f54989aL, 0x5b429d65L, 0x6b8fe4d6L, 0x99f73fd6L, + 0xa1d29c07L, 0xefe830f5L, 0x4d2d38e6L, 0xf0255dc1L, + 0x4cdd2086L, 0x8470eb26L, 0x6382e9c6L, 0x021ecc5eL, + 0x09686b3fL, 0x3ebaefc9L, 0x3c971814L, 0x6b6a70a1L, + 0x687f3584L, 0x52a0e286L, 0xb79c5305L, 0xaa500737L, + 0x3e07841cL, 0x7fdeae5cL, 0x8e7d44ecL, 0x5716f2b8L, + 0xb03ada37L, 0xf0500c0dL, 0xf01c1f04L, 0x0200b3ffL, + 0xae0cf51aL, 0x3cb574b2L, 0x25837a58L, 0xdc0921bdL, + 0xd19113f9L, 0x7ca92ff6L, 0x94324773L, 0x22f54701L, + 0x3ae5e581L, 0x37c2dadcL, 0xc8b57634L, 0x9af3dda7L, + 0xa9446146L, 0x0fd0030eL, 0xecc8c73eL, 0xa4751e41L, + 0xe238cd99L, 0x3bea0e2fL, 0x3280bba1L, 0x183eb331L, + 0x4e548b38L, 0x4f6db908L, 0x6f420d03L, 0xf60a04bfL, + 0x2cb81290L, 0x24977c79L, 0x5679b072L, 0xbcaf89afL, + 0xde9a771fL, 0xd9930810L, 0xb38bae12L, 0xdccf3f2eL, + 0x5512721fL, 0x2e6b7124L, 0x501adde6L, 0x9f84cd87L, + 0x7a584718L, 0x7408da17L, 0xbc9f9abcL, 0xe94b7d8cL, + 0xec7aec3aL, 0xdb851dfaL, 0x63094366L, 0xc464c3d2L, + 0xef1c1847L, 0x3215d908L, 0xdd433b37L, 0x24c2ba16L, + 0x12a14d43L, 0x2a65c451L, 0x50940002L, 0x133ae4ddL, + 0x71dff89eL, 0x10314e55L, 0x81ac77d6L, 0x5f11199bL, + 0x043556f1L, 0xd7a3c76bL, 0x3c11183bL, 0x5924a509L, + 0xf28fe6edL, 0x97f1fbfaL, 0x9ebabf2cL, 0x1e153c6eL, + 0x86e34570L, 0xeae96fb1L, 0x860e5e0aL, 0x5a3e2ab3L, + 0x771fe71cL, 0x4e3d06faL, 0x2965dcb9L, 0x99e71d0fL, + 0x803e89d6L, 0x5266c825L, 0x2e4cc978L, 0x9c10b36aL, + 0xc6150ebaL, 0x94e2ea78L, 0xa5fc3c53L, 0x1e0a2df4L, + 0xf2f74ea7L, 0x361d2b3dL, 0x1939260fL, 0x19c27960L, + 0x5223a708L, 0xf71312b6L, 0xebadfe6eL, 0xeac31f66L, + 0xe3bc4595L, 0xa67bc883L, 0xb17f37d1L, 0x018cff28L, + 0xc332ddefL, 0xbe6c5aa5L, 0x65582185L, 0x68ab9802L, + 0xeecea50fL, 0xdb2f953bL, 0x2aef7dadL, 0x5b6e2f84L, + 0x1521b628L, 0x29076170L, 0xecdd4775L, 0x619f1510L, + 0x13cca830L, 0xeb61bd96L, 0x0334fe1eL, 0xaa0363cfL, + 0xb5735c90L, 0x4c70a239L, 0xd59e9e0bL, 0xcbaade14L, + 0xeecc86bcL, 0x60622ca7L, 0x9cab5cabL, 0xb2f3846eL, + 0x648b1eafL, 0x19bdf0caL, 0xa02369b9L, 0x655abb50L, + 0x40685a32L, 0x3c2ab4b3L, 0x319ee9d5L, 0xc021b8f7L, + 0x9b540b19L, 0x875fa099L, 0x95f7997eL, 0x623d7da8L, + 0xf837889aL, 0x97e32d77L, 0x11ed935fL, 0x16681281L, + 0x0e358829L, 0xc7e61fd6L, 0x96dedfa1L, 0x7858ba99L, + 0x57f584a5L, 0x1b227263L, 0x9b83c3ffL, 0x1ac24696L, + 0xcdb30aebL, 0x532e3054L, 0x8fd948e4L, 0x6dbc3128L, + 0x58ebf2efL, 0x34c6ffeaL, 0xfe28ed61L, 0xee7c3c73L, + 0x5d4a14d9L, 0xe864b7e3L, 0x42105d14L, 0x203e13e0L, + 0x45eee2b6L, 0xa3aaabeaL, 0xdb6c4f15L, 0xfacb4fd0L, + 0xc742f442L, 0xef6abbb5L, 0x654f3b1dL, 0x41cd2105L, + 0xd81e799eL, 0x86854dc7L, 0xe44b476aL, 0x3d816250L, + 0xcf62a1f2L, 0x5b8d2646L, 0xfc8883a0L, 0xc1c7b6a3L, + 0x7f1524c3L, 0x69cb7492L, 0x47848a0bL, 0x5692b285L, + 0x095bbf00L, 0xad19489dL, 0x1462b174L, 0x23820e00L, + 0x58428d2aL, 0x0c55f5eaL, 0x1dadf43eL, 0x233f7061L, + 0x3372f092L, 0x8d937e41L, 0xd65fecf1L, 0x6c223bdbL, + 0x7cde3759L, 0xcbee7460L, 0x4085f2a7L, 0xce77326eL, + 0xa6078084L, 0x19f8509eL, 0xe8efd855L, 0x61d99735L, + 0xa969a7aaL, 0xc50c06c2L, 0x5a04abfcL, 0x800bcadcL, + 0x9e447a2eL, 0xc3453484L, 0xfdd56705L, 0x0e1e9ec9L, + 0xdb73dbd3L, 0x105588cdL, 0x675fda79L, 0xe3674340L, + 0xc5c43465L, 0x713e38d8L, 0x3d28f89eL, 0xf16dff20L, + 0x153e21e7L, 0x8fb03d4aL, 0xe6e39f2bL, 0xdb83adf7L, + 0xe93d5a68L, 0x948140f7L, 0xf64c261cL, 0x94692934L, + 0x411520f7L, 0x7602d4f7L, 0xbcf46b2eL, 0xd4a20068L, + 0xd4082471L, 0x3320f46aL, 0x43b7d4b7L, 0x500061afL, + 0x1e39f62eL, 0x97244546L, 0x14214f74L, 0xbf8b8840L, + 0x4d95fc1dL, 0x96b591afL, 0x70f4ddd3L, 0x66a02f45L, + 0xbfbc09ecL, 0x03bd9785L, 0x7fac6dd0L, 0x31cb8504L, + 0x96eb27b3L, 0x55fd3941L, 0xda2547e6L, 0xabca0a9aL, + 0x28507825L, 0x530429f4L, 0x0a2c86daL, 0xe9b66dfbL, + 0x68dc1462L, 0xd7486900L, 0x680ec0a4L, 0x27a18deeL, + 0x4f3ffea2L, 0xe887ad8cL, 0xb58ce006L, 0x7af4d6b6L, + 0xaace1e7cL, 0xd3375fecL, 0xce78a399L, 0x406b2a42L, + 0x20fe9e35L, 0xd9f385b9L, 0xee39d7abL, 0x3b124e8bL, + 0x1dc9faf7L, 0x4b6d1856L, 0x26a36631L, 0xeae397b2L, + 0x3a6efa74L, 0xdd5b4332L, 0x6841e7f7L, 0xca7820fbL, + 0xfb0af54eL, 0xd8feb397L, 0x454056acL, 0xba489527L, + 0x55533a3aL, 0x20838d87L, 0xfe6ba9b7L, 0xd096954bL, + 0x55a867bcL, 0xa1159a58L, 0xcca92963L, 0x99e1db33L, + 0xa62a4a56L, 0x3f3125f9L, 0x5ef47e1cL, 0x9029317cL, + 0xfdf8e802L, 0x04272f70L, 0x80bb155cL, 0x05282ce3L, + 0x95c11548L, 0xe4c66d22L, 0x48c1133fL, 0xc70f86dcL, + 0x07f9c9eeL, 0x41041f0fL, 0x404779a4L, 0x5d886e17L, + 0x325f51ebL, 0xd59bc0d1L, 0xf2bcc18fL, 0x41113564L, + 0x257b7834L, 0x602a9c60L, 0xdff8e8a3L, 0x1f636c1bL, + 0x0e12b4c2L, 0x02e1329eL, 0xaf664fd1L, 0xcad18115L, + 0x6b2395e0L, 0x333e92e1L, 0x3b240b62L, 0xeebeb922L, + 0x85b2a20eL, 0xe6ba0d99L, 0xde720c8cL, 0x2da2f728L, + 0xd0127845L, 0x95b794fdL, 0x647d0862L, 0xe7ccf5f0L, + 0x5449a36fL, 0x877d48faL, 0xc39dfd27L, 0xf33e8d1eL, + 0x0a476341L, 0x992eff74L, 0x3a6f6eabL, 0xf4f8fd37L, + 0xa812dc60L, 0xa1ebddf8L, 0x991be14cL, 0xdb6e6b0dL, + 0xc67b5510L, 0x6d672c37L, 0x2765d43bL, 0xdcd0e804L, + 0xf1290dc7L, 0xcc00ffa3L, 0xb5390f92L, 0x690fed0bL, + 0x667b9ffbL, 0xcedb7d9cL, 0xa091cf0bL, 0xd9155ea3L, + 0xbb132f88L, 0x515bad24L, 0x7b9479bfL, 0x763bd6ebL, + 0x37392eb3L, 0xcc115979L, 0x8026e297L, 0xf42e312dL, + 0x6842ada7L, 0xc66a2b3bL, 0x12754cccL, 0x782ef11cL, + 0x6a124237L, 0xb79251e7L, 0x06a1bbe6L, 0x4bfb6350L, + 0x1a6b1018L, 0x11caedfaL, 0x3d25bdd8L, 0xe2e1c3c9L, + 0x44421659L, 0x0a121386L, 0xd90cec6eL, 0xd5abea2aL, + 0x64af674eL, 0xda86a85fL, 0xbebfe988L, 0x64e4c3feL, + 0x9dbc8057L, 0xf0f7c086L, 0x60787bf8L, 0x6003604dL, + 0xd1fd8346L, 0xf6381fb0L, 0x7745ae04L, 0xd736fcccL, + 0x83426b33L, 0xf01eab71L, 0xb0804187L, 0x3c005e5fL, + 0x77a057beL, 0xbde8ae24L, 0x55464299L, 0xbf582e61L, + 0x4e58f48fL, 0xf2ddfda2L, 0xf474ef38L, 0x8789bdc2L, + 0x5366f9c3L, 0xc8b38e74L, 0xb475f255L, 0x46fcd9b9L, + 0x7aeb2661L, 0x8b1ddf84L, 0x846a0e79L, 0x915f95e2L, + 0x466e598eL, 0x20b45770L, 0x8cd55591L, 0xc902de4cL, + 0xb90bace1L, 0xbb8205d0L, 0x11a86248L, 0x7574a99eL, + 0xb77f19b6L, 0xe0a9dc09L, 0x662d09a1L, 0xc4324633L, + 0xe85a1f02L, 0x09f0be8cL, 0x4a99a025L, 0x1d6efe10L, + 0x1ab93d1dL, 0x0ba5a4dfL, 0xa186f20fL, 0x2868f169L, + 0xdcb7da83L, 0x573906feL, 0xa1e2ce9bL, 0x4fcd7f52L, + 0x50115e01L, 0xa70683faL, 0xa002b5c4L, 0x0de6d027L, + 0x9af88c27L, 0x773f8641L, 0xc3604c06L, 0x61a806b5L, + 0xf0177a28L, 0xc0f586e0L, 0x006058aaL, 0x30dc7d62L, + 0x11e69ed7L, 0x2338ea63L, 0x53c2dd94L, 0xc2c21634L, + 0xbbcbee56L, 0x90bcb6deL, 0xebfc7da1L, 0xce591d76L, + 0x6f05e409L, 0x4b7c0188L, 0x39720a3dL, 0x7c927c24L, + 0x86e3725fL, 0x724d9db9L, 0x1ac15bb4L, 0xd39eb8fcL, + 0xed545578L, 0x08fca5b5L, 0xd83d7cd3L, 0x4dad0fc4L, + 0x1e50ef5eL, 0xb161e6f8L, 0xa28514d9L, 0x6c51133cL, + 0x6fd5c7e7L, 0x56e14ec4L, 0x362abfceL, 0xddc6c837L, + 0xd79a3234L, 0x92638212L, 0x670efa8eL, 0x406000e0L, + 0x3a39ce37L, 0xd3faf5cfL, 0xabc27737L, 0x5ac52d1bL, + 0x5cb0679eL, 0x4fa33742L, 0xd3822740L, 0x99bc9bbeL, + 0xd5118e9dL, 0xbf0f7315L, 0xd62d1c7eL, 0xc700c47bL, + 0xb78c1b6bL, 0x21a19045L, 0xb26eb1beL, 0x6a366eb4L, + 0x5748ab2fL, 0xbc946e79L, 0xc6a376d2L, 0x6549c2c8L, + 0x530ff8eeL, 0x468dde7dL, 0xd5730a1dL, 0x4cd04dc6L, + 0x2939bbdbL, 0xa9ba4650L, 0xac9526e8L, 0xbe5ee304L, + 0xa1fad5f0L, 0x6a2d519aL, 0x63ef8ce2L, 0x9a86ee22L, + 0xc089c2b8L, 0x43242ef6L, 0xa51e03aaL, 0x9cf2d0a4L, + 0x83c061baL, 0x9be96a4dL, 0x8fe51550L, 0xba645bd6L, + 0x2826a2f9L, 0xa73a3ae1L, 0x4ba99586L, 0xef5562e9L, + 0xc72fefd3L, 0xf752f7daL, 0x3f046f69L, 0x77fa0a59L, + 0x80e4a915L, 0x87b08601L, 0x9b09e6adL, 0x3b3ee593L, + 0xe990fd5aL, 0x9e34d797L, 0x2cf0b7d9L, 0x022b8b51L, + 0x96d5ac3aL, 0x017da67dL, 0xd1cf3ed6L, 0x7c7d2d28L, + 0x1f9f25cfL, 0xadf2b89bL, 0x5ad6b472L, 0x5a88f54cL, + 0xe029ac71L, 0xe019a5e6L, 0x47b0acfdL, 0xed93fa9bL, + 0xe8d3c48dL, 0x283b57ccL, 0xf8d56629L, 0x79132e28L, + 0x785f0191L, 0xed756055L, 0xf7960e44L, 0xe3d35e8cL, + 0x15056dd4L, 0x88f46dbaL, 0x03a16125L, 0x0564f0bdL, + 0xc3eb9e15L, 0x3c9057a2L, 0x97271aecL, 0xa93a072aL, + 0x1b3f6d9bL, 0x1e6321f5L, 0xf59c66fbL, 0x26dcf319L, + 0x7533d928L, 0xb155fdf5L, 0x03563482L, 0x8aba3cbbL, + 0x28517711L, 0xc20ad9f8L, 0xabcc5167L, 0xccad925fL, + 0x4de81751L, 0x3830dc8eL, 0x379d5862L, 0x9320f991L, + 0xea7a90c2L, 0xfb3e7bceL, 0x5121ce64L, 0x774fbe32L, + 0xa8b6e37eL, 0xc3293d46L, 0x48de5369L, 0x6413e680L, + 0xa2ae0810L, 0xdd6db224L, 0x69852dfdL, 0x09072166L, + 0xb39a460aL, 0x6445c0ddL, 0x586cdecfL, 0x1c20c8aeL, + 0x5bbef7ddL, 0x1b588d40L, 0xccd2017fL, 0x6bb4e3bbL, + 0xdda26a7eL, 0x3a59ff45L, 0x3e350a44L, 0xbcb4cdd5L, + 0x72eacea8L, 0xfa6484bbL, 0x8d6612aeL, 0xbf3c6f47L, + 0xd29be463L, 0x542f5d9eL, 0xaec2771bL, 0xf64e6370L, + 0x740e0d8dL, 0xe75b1357L, 0xf8721671L, 0xaf537d5dL, + 0x4040cb08L, 0x4eb4e2ccL, 0x34d2466aL, 0x0115af84L, + 0xe1b00428L, 0x95983a1dL, 0x06b89fb4L, 0xce6ea048L, + 0x6f3f3b82L, 0x3520ab82L, 0x011a1d4bL, 0x277227f8L, + 0x611560b1L, 0xe7933fdcL, 0xbb3a792bL, 0x344525bdL, + 0xa08839e1L, 0x51ce794bL, 0x2f32c9b7L, 0xa01fbac9L, + 0xe01cc87eL, 0xbcc7d1f6L, 0xcf0111c3L, 0xa1e8aac7L, + 0x1a908749L, 0xd44fbd9aL, 0xd0dadecbL, 0xd50ada38L, + 0x0339c32aL, 0xc6913667L, 0x8df9317cL, 0xe0b12b4fL, + 0xf79e59b7L, 0x43f5bb3aL, 0xf2d519ffL, 0x27d9459cL, + 0xbf97222cL, 0x15e6fc2aL, 0x0f91fc71L, 0x9b941525L, + 0xfae59361L, 0xceb69cebL, 0xc2a86459L, 0x12baa8d1L, + 0xb6c1075eL, 0xe3056a0cL, 0x10d25065L, 0xcb03a442L, + 0xe0ec6e0eL, 0x1698db3bL, 0x4c98a0beL, 0x3278e964L, + 0x9f1f9532L, 0xe0d392dfL, 0xd3a0342bL, 0x8971f21eL, + 0x1b0a7441L, 0x4ba3348cL, 0xc5be7120L, 0xc37632d8L, + 0xdf359f8dL, 0x9b992f2eL, 0xe60b6f47L, 0x0fe3f11dL, + 0xe54cda54L, 0x1edad891L, 0xce6279cfL, 0xcd3e7e6fL, + 0x1618b166L, 0xfd2c1d05L, 0x848fd2c5L, 0xf6fb2299L, + 0xf523f357L, 0xa6327623L, 0x93a83531L, 0x56cccd02L, + 0xacf08162L, 0x5a75ebb5L, 0x6e163697L, 0x88d273ccL, + 0xde966292L, 0x81b949d0L, 0x4c50901bL, 0x71c65614L, + 0xe6c6c7bdL, 0x327a140aL, 0x45e1d006L, 0xc3f27b9aL, + 0xc9aa53fdL, 0x62a80f00L, 0xbb25bfe2L, 0x35bdd2f6L, + 0x71126905L, 0xb2040222L, 0xb6cbcf7cL, 0xcd769c2bL, + 0x53113ec0L, 0x1640e3d3L, 0x38abbd60L, 0x2547adf0L, + 0xba38209cL, 0xf746ce76L, 0x77afa1c5L, 0x20756060L, + 0x85cbfe4eL, 0x8ae88dd8L, 0x7aaaf9b0L, 0x4cf9aa7eL, + 0x1948c25cL, 0x02fb8a8cL, 0x01c36ae4L, 0xd6ebe1f9L, + 0x90d4f869L, 0xa65cdea0L, 0x3f09252dL, 0xc208e69fL, + 0xb74e6132L, 0xce77e25bL, 0x578fdfe3L, 0x3ac372e6L, + } + }; + diff --git a/src/libcrypto/libblowfish/bf_skey.c b/src/libcrypto/libblowfish/bf_skey.c new file mode 100644 index 000000000..8cdbbd283 --- /dev/null +++ b/src/libcrypto/libblowfish/bf_skey.c @@ -0,0 +1,122 @@ +/* crypto/bf/bf_skey.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +#include "blowfish.h" +#include "bf_locl.h" +#include "bf_pi.h" + +void BF_set_key(BF_KEY *key, int len, const unsigned char *data) + { + int i; + BF_LONG *p,ri,in[2]; + const unsigned char *d,*end; + + + memcpy((char *)key,(const char *)&bf_init,sizeof(BF_KEY)); + p=key->P; + + if (len > ((BF_ROUNDS+2)*4)) len=(BF_ROUNDS+2)*4; + + d=data; + end= &(data[len]); + for (i=0; i<(BF_ROUNDS+2); i++) + { + ri= *(d++); + if (d >= end) d=data; + + ri<<=8; + ri|= *(d++); + if (d >= end) d=data; + + ri<<=8; + ri|= *(d++); + if (d >= end) d=data; + + ri<<=8; + ri|= *(d++); + if (d >= end) d=data; + + p[i]^=ri; + } + + in[0]=0L; + in[1]=0L; + for (i=0; i<(BF_ROUNDS+2); i+=2) + { + BF_encrypt(in,key); + p[i ]=in[0]; + p[i+1]=in[1]; + } + + p=key->S; + for (i=0; i<4*256; i+=2) + { + BF_encrypt(in,key); + p[i ]=in[0]; + p[i+1]=in[1]; + } + } + diff --git a/src/libcrypto/libblowfish/blowfish.h b/src/libcrypto/libblowfish/blowfish.h new file mode 100644 index 000000000..ccb97e272 --- /dev/null +++ b/src/libcrypto/libblowfish/blowfish.h @@ -0,0 +1,133 @@ +/* crypto/bf/blowfish.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_BLOWFISH_H +#define HEADER_BLOWFISH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef NO_BF +#error BF is disabled. +#endif + +#define BF_ENCRYPT 1 +#define BF_DECRYPT 0 + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! BF_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! BF_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +#if defined(WIN16) || defined(__LP32__) +#define BF_LONG unsigned long +#elif defined(_CRAY) || defined(__ILP64__) +#define BF_LONG unsigned long +#define BF_LONG_LOG2 3 +#endif +/* + * _CRAY note. I could declare short, but I have no idea what impact + * does it have on performance on none-T3E machines. I could declare + * int, but at least on C90 sizeof(int) can be chosen at compile time. + * So I've chosen long... + * + */ + +/* des.h-like hack */ +#ifndef BF_LONG +#ifdef __KERNEL__ +#include +#else +#include +#endif +#define BF_LONG u_int32_t +#endif + +#define BF_ROUNDS 16 +#define BF_BLOCK 8 + +typedef struct bf_key_st + { + BF_LONG P[BF_ROUNDS+2]; + BF_LONG S[4*256]; + } BF_KEY; + + +void BF_set_key(BF_KEY *key, int len, const unsigned char *data); + +void BF_encrypt(BF_LONG *data,const BF_KEY *key); +void BF_decrypt(BF_LONG *data,const BF_KEY *key); + +void BF_ecb_encrypt(const unsigned char *in, unsigned char *out, + const BF_KEY *key, int enc); +void BF_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int enc); +void BF_cfb64_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int *num, int enc); +void BF_ofb64_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int *num); +const char *BF_options(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libcrypto/libdes/cbc_enc.c b/src/libcrypto/libdes/cbc_enc.c new file mode 100644 index 000000000..a06f9f99e --- /dev/null +++ b/src/libcrypto/libdes/cbc_enc.c @@ -0,0 +1,135 @@ +/* crypto/des/cbc_enc.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "des_locl.h" + +void des_cbc_encrypt(input, output, length, schedule, ivec, enc) +des_cblock (*input); +des_cblock (*output); +long length; +des_key_schedule schedule; +des_cblock (*ivec); +int enc; + { + register DES_LONG tin0,tin1; + register DES_LONG tout0,tout1,xor0,xor1; + register unsigned char *in,*out; + register long l=length; + DES_LONG tin[2]; + unsigned char *iv; + + in=(unsigned char *)input; + out=(unsigned char *)output; + iv=(unsigned char *)ivec; + + if (enc) + { + c2l(iv,tout0); + c2l(iv,tout1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + tin0^=tout0; tin[0]=tin0; + tin1^=tout1; tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); + tout0=tin[0]; l2c(tout0,out); + tout1=tin[1]; l2c(tout1,out); + } + if (l != -8) + { + c2ln(in,tin0,tin1,l+8); + tin0^=tout0; tin[0]=tin0; + tin1^=tout1; tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); + tout0=tin[0]; l2c(tout0,out); + tout1=tin[1]; l2c(tout1,out); + } + } + else + { + c2l(iv,xor0); + c2l(iv,xor1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); tin[0]=tin0; + c2l(in,tin1); tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2c(tout0,out); + l2c(tout1,out); + xor0=tin0; + xor1=tin1; + } + if (l != -8) + { + c2l(in,tin0); tin[0]=tin0; + c2l(in,tin1); tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2cn(tout0,tout1,out,l+8); + /* xor0=tin0; + xor1=tin1; */ + } + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; + } + diff --git a/src/libcrypto/libdes/des.h b/src/libcrypto/libdes/des.h new file mode 100644 index 000000000..baddf8647 --- /dev/null +++ b/src/libcrypto/libdes/des.h @@ -0,0 +1,308 @@ +/* crypto/des/des.org */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * Always modify des.org since des.h is automatically generated from + * it during SSLeay configuration. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +#ifndef HEADER_DES_H +#define HEADER_DES_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a + * %20 speed up (longs are 8 bytes, int's are 4). */ +/* Must be unsigned int on ia64/Itanium or DES breaks badly */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#ifndef DES_LONG +#define DES_LONG u_int32_t +#endif + +typedef unsigned char des_cblock[8]; +typedef struct des_ks_struct + { + union { + des_cblock _; + /* make sure things are correct size on machines with + * 8 byte longs */ + DES_LONG pad[2]; + } ks; +#undef _ +#define _ ks._ + } des_key_schedule[16]; + +#define DES_KEY_SZ (sizeof(des_cblock)) +#define DES_SCHEDULE_SZ (sizeof(des_key_schedule)) + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +#define DES_CBC_MODE 0 +#define DES_PCBC_MODE 1 + +#define des_ecb2_encrypt(i,o,k1,k2,e) \ + des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) + +#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ + des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) + +#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ + des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) + +#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ + des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) + +#define C_Block des_cblock +#define Key_schedule des_key_schedule +#ifdef KERBEROS +#define ENCRYPT DES_ENCRYPT +#define DECRYPT DES_DECRYPT +#endif +#define KEY_SZ DES_KEY_SZ +#define string_to_key des_string_to_key +#define read_pw_string des_read_pw_string +#define random_key des_random_key +#define pcbc_encrypt des_pcbc_encrypt +#define set_key des_set_key +#define key_sched des_key_sched +#define ecb_encrypt des_ecb_encrypt +#define cbc_encrypt des_cbc_encrypt +#define ncbc_encrypt des_ncbc_encrypt +#define xcbc_encrypt des_xcbc_encrypt +#define cbc_cksum des_cbc_cksum +#define quad_cksum des_quad_cksum + +/* For compatibility with the MIT lib - eay 20/05/92 */ +typedef des_key_schedule bit_64; +#define des_fixup_key_parity des_set_odd_parity +#define des_check_key_parity check_parity + +extern int des_check_key; /* defaults to false */ +extern int des_rw_mode; /* defaults to DES_PCBC_MODE */ + +/* The next line is used to disable full ANSI prototypes, if your + * compiler has problems with the prototypes, make sure this line always + * evaluates to true :-) */ +#if defined(MSDOS) || defined(__STDC__) +#undef NOPROTO +#endif +#ifndef NOPROTO +char *des_options(void); +void des_ecb3_encrypt(des_cblock *input,des_cblock *output, + des_key_schedule ks1,des_key_schedule ks2, + des_key_schedule ks3, int enc); +DES_LONG des_cbc_cksum(des_cblock *input,des_cblock *output, + long length,des_key_schedule schedule,des_cblock *ivec); +void des_cbc_encrypt(des_cblock *input,des_cblock *output,long length, + des_key_schedule schedule,des_cblock *ivec,int enc); +void des_ncbc_encrypt(des_cblock *input,des_cblock *output,long length, + des_key_schedule schedule,des_cblock *ivec,int enc); +void des_xcbc_encrypt(des_cblock *input,des_cblock *output,long length, + des_key_schedule schedule,des_cblock *ivec, + des_cblock *inw,des_cblock *outw,int enc); +void des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits, + long length,des_key_schedule schedule,des_cblock *ivec,int enc); +void des_ecb_encrypt(des_cblock *input,des_cblock *output, + des_key_schedule ks,int enc); +void des_encrypt(DES_LONG *data,des_key_schedule ks, int enc); +void des_encrypt2(DES_LONG *data,des_key_schedule ks, int enc); +void des_encrypt3(DES_LONG *data, des_key_schedule ks1, + des_key_schedule ks2, des_key_schedule ks3); +void des_decrypt3(DES_LONG *data, des_key_schedule ks1, + des_key_schedule ks2, des_key_schedule ks3); +void des_ede3_cbc_encrypt(des_cblock *input, des_cblock *output, + long length, des_key_schedule ks1, des_key_schedule ks2, + des_key_schedule ks3, des_cblock *ivec, int enc); +void des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out, + long length, des_key_schedule ks1, des_key_schedule ks2, + des_key_schedule ks3, des_cblock *ivec, int *num, int enc); +void des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out, + long length, des_key_schedule ks1, des_key_schedule ks2, + des_key_schedule ks3, des_cblock *ivec, int *num); + +void des_xwhite_in2out(des_cblock (*des_key), des_cblock (*in_white), + des_cblock (*out_white)); + +int des_enc_read(int fd,char *buf,int len,des_key_schedule sched, + des_cblock *iv); +int des_enc_write(int fd,char *buf,int len,des_key_schedule sched, + des_cblock *iv); +char *des_fcrypt(const char *buf,const char *salt, char *ret); +#ifdef PERL5 +char *des_crypt(const char *buf,const char *salt); +#else +/* some stupid compilers complain because I have declared char instead + * of const char */ +#ifndef __KERNEL__ +#ifdef HEADER_DES_LOCL_H +char *crypt(const char *buf,const char *salt); +#else /* HEADER_DES_LOCL_H */ +char *crypt(void); +#endif /* HEADER_DES_LOCL_H */ +#endif /* __KERNEL__ */ +#endif /* PERL5 */ +void des_ofb_encrypt(unsigned char *in,unsigned char *out, + int numbits,long length,des_key_schedule schedule,des_cblock *ivec); +void des_pcbc_encrypt(des_cblock *input,des_cblock *output,long length, + des_key_schedule schedule,des_cblock *ivec,int enc); +DES_LONG des_quad_cksum(des_cblock *input,des_cblock *output, + long length,int out_count,des_cblock *seed); +void des_random_seed(des_cblock key); +void des_random_key(des_cblock ret); +int des_read_password(des_cblock *key,char *prompt,int verify); +int des_read_2passwords(des_cblock *key1,des_cblock *key2, + char *prompt,int verify); +int des_read_pw_string(char *buf,int length,char *prompt,int verify); +void des_set_odd_parity(des_cblock *key); +int des_is_weak_key(des_cblock *key); +int des_set_key(des_cblock *key,des_key_schedule schedule); +int des_key_sched(des_cblock *key,des_key_schedule schedule); +void des_string_to_key(char *str,des_cblock *key); +void des_string_to_2keys(char *str,des_cblock *key1,des_cblock *key2); +void des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, + des_key_schedule schedule, des_cblock *ivec, int *num, int enc); +void des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, + des_key_schedule schedule, des_cblock *ivec, int *num); +int des_read_pw(char *buf, char *buff, int size, char *prompt, int verify); + +/* Extra functions from Mark Murray */ +/* The following functions are not in the normal unix build or the + * SSLeay build. When using the SSLeay build, use RAND_seed() + * and RAND_bytes() instead. */ +int des_new_random_key(des_cblock *key); +void des_init_random_number_generator(des_cblock *key); +void des_set_random_generator_seed(des_cblock *key); +void des_set_sequence_number(des_cblock new_sequence_number); +void des_generate_random_block(des_cblock *block); + +#else + +char *des_options(); +void des_ecb3_encrypt(); +DES_LONG des_cbc_cksum(); +void des_cbc_encrypt(); +void des_ncbc_encrypt(); +void des_xcbc_encrypt(); +void des_cfb_encrypt(); +void des_ede3_cfb64_encrypt(); +void des_ede3_ofb64_encrypt(); +void des_ecb_encrypt(); +void des_encrypt(); +void des_encrypt2(); +void des_encrypt3(); +void des_decrypt3(); +void des_ede3_cbc_encrypt(); +int des_enc_read(); +int des_enc_write(); +char *des_fcrypt(); +#ifdef PERL5 +char *des_crypt(); +#else +char *crypt(); +#endif +void des_ofb_encrypt(); +void des_pcbc_encrypt(); +DES_LONG des_quad_cksum(); +void des_random_seed(); +void des_random_key(); +int des_read_password(); +int des_read_2passwords(); +int des_read_pw_string(); +void des_set_odd_parity(); +int des_is_weak_key(); +int des_set_key(); +int des_key_sched(); +void des_string_to_key(); +void des_string_to_2keys(); +void des_cfb64_encrypt(); +void des_ofb64_encrypt(); +int des_read_pw(); +void des_xwhite_in2out(); + +/* Extra functions from Mark Murray */ +/* The following functions are not in the normal unix build or the + * SSLeay build. When using the SSLeay build, use RAND_seed() + * and RAND_bytes() instead. */ +#ifdef FreeBSD +int des_new_random_key(); +void des_init_random_number_generator(); +void des_set_random_generator_seed(); +void des_set_sequence_number(); +void des_generate_random_block(); +#endif + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libcrypto/libdes/des_enc.c b/src/libcrypto/libdes/des_enc.c new file mode 100644 index 000000000..1e1906d25 --- /dev/null +++ b/src/libcrypto/libdes/des_enc.c @@ -0,0 +1,502 @@ +/* crypto/des/des_enc.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "des_locl.h" + +void des_encrypt(data, ks, enc) +DES_LONG *data; +des_key_schedule ks; +int enc; + { + register DES_LONG l,r,t,u; +#ifdef DES_PTR + register unsigned char *des_SP=(unsigned char *)des_SPtrans; +#endif +#ifndef DES_UNROLL + register int i; +#endif + register DES_LONG *s; + + r=data[0]; + l=data[1]; + + IP(r,l); + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. */ + /* clear the top bits on machines with 8byte longs */ + /* shift left by 2 */ + r=ROTATE(r,29)&0xffffffffL; + l=ROTATE(l,29)&0xffffffffL; + + s=(DES_LONG *)ks; + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (enc) + { +#ifdef DES_UNROLL + D_ENCRYPT(l,r, 0); /* 1 */ + D_ENCRYPT(r,l, 2); /* 2 */ + D_ENCRYPT(l,r, 4); /* 3 */ + D_ENCRYPT(r,l, 6); /* 4 */ + D_ENCRYPT(l,r, 8); /* 5 */ + D_ENCRYPT(r,l,10); /* 6 */ + D_ENCRYPT(l,r,12); /* 7 */ + D_ENCRYPT(r,l,14); /* 8 */ + D_ENCRYPT(l,r,16); /* 9 */ + D_ENCRYPT(r,l,18); /* 10 */ + D_ENCRYPT(l,r,20); /* 11 */ + D_ENCRYPT(r,l,22); /* 12 */ + D_ENCRYPT(l,r,24); /* 13 */ + D_ENCRYPT(r,l,26); /* 14 */ + D_ENCRYPT(l,r,28); /* 15 */ + D_ENCRYPT(r,l,30); /* 16 */ +#else + for (i=0; i<32; i+=8) + { + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + D_ENCRYPT(l,r,i+4); /* 3 */ + D_ENCRYPT(r,l,i+6); /* 4 */ + } +#endif + } + else + { +#ifdef DES_UNROLL + D_ENCRYPT(l,r,30); /* 16 */ + D_ENCRYPT(r,l,28); /* 15 */ + D_ENCRYPT(l,r,26); /* 14 */ + D_ENCRYPT(r,l,24); /* 13 */ + D_ENCRYPT(l,r,22); /* 12 */ + D_ENCRYPT(r,l,20); /* 11 */ + D_ENCRYPT(l,r,18); /* 10 */ + D_ENCRYPT(r,l,16); /* 9 */ + D_ENCRYPT(l,r,14); /* 8 */ + D_ENCRYPT(r,l,12); /* 7 */ + D_ENCRYPT(l,r,10); /* 6 */ + D_ENCRYPT(r,l, 8); /* 5 */ + D_ENCRYPT(l,r, 6); /* 4 */ + D_ENCRYPT(r,l, 4); /* 3 */ + D_ENCRYPT(l,r, 2); /* 2 */ + D_ENCRYPT(r,l, 0); /* 1 */ +#else + for (i=30; i>0; i-=8) + { + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + D_ENCRYPT(l,r,i-4); /* 14 */ + D_ENCRYPT(r,l,i-6); /* 13 */ + } +#endif + } + + /* rotate and clear the top bits on machines with 8byte longs */ + l=ROTATE(l,3)&0xffffffffL; + r=ROTATE(r,3)&0xffffffffL; + + FP(r,l); + data[0]=l; + data[1]=r; + l=r=t=u=0; + } + +void des_encrypt2(data, ks, enc) +DES_LONG *data; +des_key_schedule ks; +int enc; + { + register DES_LONG l,r,t,u; +#ifdef DES_PTR + register unsigned char *des_SP=(unsigned char *)des_SPtrans; +#endif +#ifndef DES_UNROLL + register int i; +#endif + register DES_LONG *s; + + r=data[0]; + l=data[1]; + + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. */ + /* clear the top bits on machines with 8byte longs */ + r=ROTATE(r,29)&0xffffffffL; + l=ROTATE(l,29)&0xffffffffL; + + s=(DES_LONG *)ks; + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (enc) + { +#ifdef DES_UNROLL + D_ENCRYPT(l,r, 0); /* 1 */ + D_ENCRYPT(r,l, 2); /* 2 */ + D_ENCRYPT(l,r, 4); /* 3 */ + D_ENCRYPT(r,l, 6); /* 4 */ + D_ENCRYPT(l,r, 8); /* 5 */ + D_ENCRYPT(r,l,10); /* 6 */ + D_ENCRYPT(l,r,12); /* 7 */ + D_ENCRYPT(r,l,14); /* 8 */ + D_ENCRYPT(l,r,16); /* 9 */ + D_ENCRYPT(r,l,18); /* 10 */ + D_ENCRYPT(l,r,20); /* 11 */ + D_ENCRYPT(r,l,22); /* 12 */ + D_ENCRYPT(l,r,24); /* 13 */ + D_ENCRYPT(r,l,26); /* 14 */ + D_ENCRYPT(l,r,28); /* 15 */ + D_ENCRYPT(r,l,30); /* 16 */ +#else + for (i=0; i<32; i+=8) + { + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + D_ENCRYPT(l,r,i+4); /* 3 */ + D_ENCRYPT(r,l,i+6); /* 4 */ + } +#endif + } + else + { +#ifdef DES_UNROLL + D_ENCRYPT(l,r,30); /* 16 */ + D_ENCRYPT(r,l,28); /* 15 */ + D_ENCRYPT(l,r,26); /* 14 */ + D_ENCRYPT(r,l,24); /* 13 */ + D_ENCRYPT(l,r,22); /* 12 */ + D_ENCRYPT(r,l,20); /* 11 */ + D_ENCRYPT(l,r,18); /* 10 */ + D_ENCRYPT(r,l,16); /* 9 */ + D_ENCRYPT(l,r,14); /* 8 */ + D_ENCRYPT(r,l,12); /* 7 */ + D_ENCRYPT(l,r,10); /* 6 */ + D_ENCRYPT(r,l, 8); /* 5 */ + D_ENCRYPT(l,r, 6); /* 4 */ + D_ENCRYPT(r,l, 4); /* 3 */ + D_ENCRYPT(l,r, 2); /* 2 */ + D_ENCRYPT(r,l, 0); /* 1 */ +#else + for (i=30; i>0; i-=8) + { + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + D_ENCRYPT(l,r,i-4); /* 14 */ + D_ENCRYPT(r,l,i-6); /* 13 */ + } +#endif + } + /* rotate and clear the top bits on machines with 8byte longs */ + data[0]=ROTATE(l,3)&0xffffffffL; + data[1]=ROTATE(r,3)&0xffffffffL; + l=r=t=u=0; + } + +void des_encrypt3(data,ks1,ks2,ks3) +DES_LONG *data; +des_key_schedule ks1; +des_key_schedule ks2; +des_key_schedule ks3; + { + register DES_LONG l,r; + + l=data[0]; + r=data[1]; + IP(l,r); + data[0]=l; + data[1]=r; + des_encrypt2((DES_LONG *)data,ks1,DES_ENCRYPT); + des_encrypt2((DES_LONG *)data,ks2,DES_DECRYPT); + des_encrypt2((DES_LONG *)data,ks3,DES_ENCRYPT); + l=data[0]; + r=data[1]; + FP(r,l); + data[0]=l; + data[1]=r; + } + +void des_decrypt3(data,ks1,ks2,ks3) +DES_LONG *data; +des_key_schedule ks1; +des_key_schedule ks2; +des_key_schedule ks3; + { + register DES_LONG l,r; + + l=data[0]; + r=data[1]; + IP(l,r); + data[0]=l; + data[1]=r; + des_encrypt2((DES_LONG *)data,ks3,DES_DECRYPT); + des_encrypt2((DES_LONG *)data,ks2,DES_ENCRYPT); + des_encrypt2((DES_LONG *)data,ks1,DES_DECRYPT); + l=data[0]; + r=data[1]; + FP(r,l); + data[0]=l; + data[1]=r; + } + +#ifndef DES_DEFAULT_OPTIONS + +void des_ncbc_encrypt(input, output, length, schedule, ivec, enc) +des_cblock (*input); +des_cblock (*output); +long length; +des_key_schedule schedule; +des_cblock (*ivec); +int enc; + { + register DES_LONG tin0,tin1; + register DES_LONG tout0,tout1,xor0,xor1; + register unsigned char *in,*out; + register long l=length; + DES_LONG tin[2]; + unsigned char *iv; + + in=(unsigned char *)input; + out=(unsigned char *)output; + iv=(unsigned char *)ivec; + + if (enc) + { + c2l(iv,tout0); + c2l(iv,tout1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + tin0^=tout0; tin[0]=tin0; + tin1^=tout1; tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); + tout0=tin[0]; l2c(tout0,out); + tout1=tin[1]; l2c(tout1,out); + } + if (l != -8) + { + c2ln(in,tin0,tin1,l+8); + tin0^=tout0; tin[0]=tin0; + tin1^=tout1; tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); + tout0=tin[0]; l2c(tout0,out); + tout1=tin[1]; l2c(tout1,out); + } + iv=(unsigned char *)ivec; + l2c(tout0,iv); + l2c(tout1,iv); + } + else + { + c2l(iv,xor0); + c2l(iv,xor1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); tin[0]=tin0; + c2l(in,tin1); tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2c(tout0,out); + l2c(tout1,out); + xor0=tin0; + xor1=tin1; + } + if (l != -8) + { + c2l(in,tin0); tin[0]=tin0; + c2l(in,tin1); tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2cn(tout0,tout1,out,l+8); + xor0=tin0; + xor1=tin1; + } + + iv=(unsigned char *)ivec; + l2c(xor0,iv); + l2c(xor1,iv); + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; + } + +void des_ede3_cbc_encrypt(input, output, length, ks1, ks2, ks3, ivec, enc) +des_cblock (*input); +des_cblock (*output); +long length; +des_key_schedule ks1; +des_key_schedule ks2; +des_key_schedule ks3; +des_cblock (*ivec); +int enc; + { + register DES_LONG tin0,tin1; + register DES_LONG tout0,tout1,xor0,xor1; + register unsigned char *in,*out; + register long l=length; + DES_LONG tin[2]; + unsigned char *iv; + + in=(unsigned char *)input; + out=(unsigned char *)output; + iv=(unsigned char *)ivec; + + if (enc) + { + c2l(iv,tout0); + c2l(iv,tout1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + tin0^=tout0; + tin1^=tout1; + + tin[0]=tin0; + tin[1]=tin1; + des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + l2c(tout0,out); + l2c(tout1,out); + } + if (l != -8) + { + c2ln(in,tin0,tin1,l+8); + tin0^=tout0; + tin1^=tout1; + + tin[0]=tin0; + tin[1]=tin1; + des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + l2c(tout0,out); + l2c(tout1,out); + } + iv=(unsigned char *)ivec; + l2c(tout0,iv); + l2c(tout1,iv); + } + else + { + register DES_LONG t0,t1; + + c2l(iv,xor0); + c2l(iv,xor1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + + t0=tin0; + t1=tin1; + + tin[0]=tin0; + tin[1]=tin1; + des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + tout0^=xor0; + tout1^=xor1; + l2c(tout0,out); + l2c(tout1,out); + xor0=t0; + xor1=t1; + } + if (l != -8) + { + c2l(in,tin0); + c2l(in,tin1); + + t0=tin0; + t1=tin1; + + tin[0]=tin0; + tin[1]=tin1; + des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + tout0^=xor0; + tout1^=xor1; + l2cn(tout0,tout1,out,l+8); + xor0=t0; + xor1=t1; + } + + iv=(unsigned char *)ivec; + l2c(xor0,iv); + l2c(xor1,iv); + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; + } + +#endif /* DES_DEFAULT_OPTIONS */ diff --git a/src/libcrypto/libdes/des_locl.h b/src/libcrypto/libdes/des_locl.h new file mode 100644 index 000000000..4e0b3662f --- /dev/null +++ b/src/libcrypto/libdes/des_locl.h @@ -0,0 +1,515 @@ +/* crypto/des/des_locl.org */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * Always modify des_locl.org since des_locl.h is automatically generated from + * it during SSLeay configuration. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +#ifndef HEADER_DES_LOCL_H +#define HEADER_DES_LOCL_H + +#if defined(WIN32) || defined(WIN16) +#ifndef MSDOS +#define MSDOS +#endif +#endif + +#include "des.h" + +#ifndef DES_DEFAULT_OPTIONS +/* the following is tweaked from a config script, that is why it is a + * protected undef/define */ +#ifndef DES_PTR +#define DES_PTR +#endif + +/* This helps C compiler generate the correct code for multiple functional + * units. It reduces register dependancies at the expense of 2 more + * registers */ +#ifndef DES_RISC1 +#define DES_RISC1 +#endif + +#ifndef DES_RISC2 +#undef DES_RISC2 +#endif + +#if defined(DES_RISC1) && defined(DES_RISC2) +YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!! +#endif + +/* Unroll the inner loop, this sometimes helps, sometimes hinders. + * Very mucy CPU dependant */ +#ifndef DES_UNROLL +#define DES_UNROLL +#endif + +/* These default values were supplied by + * Peter Gutman + * They are only used if nothing else has been defined */ +#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL) +/* Special defines which change the way the code is built depending on the + CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find + even newer MIPS CPU's, but at the moment one size fits all for + optimization options. Older Sparc's work better with only UNROLL, but + there's no way to tell at compile time what it is you're running on */ + +#if defined( sun ) /* Newer Sparc's */ + #define DES_PTR + #define DES_RISC1 + #define DES_UNROLL +#elif defined( __ultrix ) /* Older MIPS */ + #define DES_PTR + #define DES_RISC2 + #define DES_UNROLL +#elif defined( __osf1__ ) /* Alpha */ + #define DES_PTR + #define DES_RISC2 +#elif defined ( _AIX ) /* RS6000 */ + /* Unknown */ +#elif defined( __hpux ) /* HP-PA */ + /* Unknown */ +#elif defined( __aux ) /* 68K */ + /* Unknown */ +#elif defined( __dgux ) /* 88K (but P6 in latest boxes) */ + #define DES_UNROLL +#elif defined( __sgi ) /* Newer MIPS */ + #define DES_PTR + #define DES_RISC2 + #define DES_UNROLL +#elif defined( i386 ) /* x86 boxes, should be gcc */ + #define DES_PTR + #define DES_RISC1 + #define DES_UNROLL +#endif /* Systems-specific speed defines */ +#endif + +#endif /* DES_DEFAULT_OPTIONS */ + +#ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */ +#include +#include +#include +#include +#ifndef RAND +#define RAND +#endif +#undef NOPROTO +#endif + +#if defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS) +#ifndef __KERNEL__ +#include +#else +#include +#endif +#endif + +#ifndef RAND +#define RAND +#endif + +#ifdef linux +#undef RAND +#endif + +#ifdef MSDOS +#define getpid() 2 +#define RAND +#undef NOPROTO +#endif + +#if defined(NOCONST) +#define const +#endif + +#ifdef __STDC__ +#undef NOPROTO +#endif + +#ifdef RAND +#define srandom(s) srand(s) +#define random rand +#endif + +#define ITERATIONS 16 +#define HALF_ITERATIONS 8 + +/* used in des_read and des_write */ +#define MAXWRITE (1024*16) +#define BSIZE (MAXWRITE+4) + +#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ + l|=((DES_LONG)(*((c)++)))<< 8L, \ + l|=((DES_LONG)(*((c)++)))<<16L, \ + l|=((DES_LONG)(*((c)++)))<<24L) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ + case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ + case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ + case 5: l2|=((DES_LONG)(*(--(c)))); \ + case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ + case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ + case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ + case 1: l1|=((DES_LONG)(*(--(c)))); \ + } \ + } + +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +/* replacements for htonl and ntohl since I have no idea what to do + * when faced with machines with 8 byte longs. */ +#define HDRSIZE 4 + +#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \ + l|=((DES_LONG)(*((c)++)))<<16L, \ + l|=((DES_LONG)(*((c)++)))<< 8L, \ + l|=((DES_LONG)(*((c)++)))) + +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +#if defined(WIN32) +#define ROTATE(a,n) (_lrotr(a,n)) +#else +#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) +#endif + +/* Don't worry about the LOAD_DATA() stuff, that is used by + * fcrypt() to add it's little bit to the front */ + +#ifdef DES_FCRYPT + +#define LOAD_DATA_tmp(R,S,u,t,E0,E1) \ + { DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); } + +#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ + t=R^(R>>16L); \ + u=t&E0; t&=E1; \ + tmp=(u<<16); u^=R^s[S ]; u^=tmp; \ + tmp=(t<<16); t^=R^s[S+1]; t^=tmp +#else +#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g) +#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ + u=R^s[S ]; \ + t=R^s[S+1] +#endif + +/* The changes to this macro may help or hinder, depending on the + * compiler and the achitecture. gcc2 always seems to do well :-). + * Inspired by Dana How + * DO NOT use the alternative version on machines with 8 byte longs. + * It does not seem to work on the Alpha, even when DES_LONG is 4 + * bytes, probably an issue of accessing non-word aligned objects :-( */ +#ifdef DES_PTR + +/* It recently occured to me that 0^0^0^0^0^0^0 == 0, so there + * is no reason to not xor all the sub items together. This potentially + * saves a register since things can be xored directly into L */ + +#if defined(DES_RISC1) || defined(DES_RISC2) +#ifdef DES_RISC1 +#define D_ENCRYPT(LL,R,S) { \ + unsigned int u1,u2,u3; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u2=(int)u>>8L; \ + u1=(int)u&0xfc; \ + u2&=0xfc; \ + t=ROTATE(t,4); \ + u>>=16L; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \ + u3=(int)(u>>8L); \ + u1=(int)u&0xfc; \ + u3&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+u3); \ + u2=(int)t>>8L; \ + u1=(int)t&0xfc; \ + u2&=0xfc; \ + t>>=16L; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \ + u3=(int)t>>8L; \ + u1=(int)t&0xfc; \ + u3&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+u3); } +#endif +#ifdef DES_RISC2 +#define D_ENCRYPT(LL,R,S) { \ + unsigned int u1,u2,s1,s2; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u2=(int)u>>8L; \ + u1=(int)u&0xfc; \ + u2&=0xfc; \ + t=ROTATE(t,4); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \ + s1=(int)(u>>16L); \ + s2=(int)(u>>24L); \ + s1&=0xfc; \ + s2&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+s1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+s2); \ + u2=(int)t>>8L; \ + u1=(int)t&0xfc; \ + u2&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \ + s1=(int)(t>>16L); \ + s2=(int)(t>>24L); \ + s1&=0xfc; \ + s2&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+s1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+s2); } +#endif +#else +#define D_ENCRYPT(LL,R,S) { \ + LOAD_DATA_tmp(R,S,u,t,E0,E1); \ + t=ROTATE(t,4); \ + LL^= \ + *(DES_LONG *)((unsigned char *)des_SP +((u )&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x200+((u>> 8L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x400+((u>>16L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x600+((u>>24L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x100+((t )&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x300+((t>> 8L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x500+((t>>16L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x700+((t>>24L)&0xfc)); } +#endif + +#else /* original version */ + +#if defined(DES_RISC1) || defined(DES_RISC2) +#ifdef DES_RISC1 +#define D_ENCRYPT(LL,R,S) {\ + unsigned int u1,u2,u3; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u>>=2L; \ + t=ROTATE(t,6); \ + u2=(int)u>>8L; \ + u1=(int)u&0x3f; \ + u2&=0x3f; \ + u>>=16L; \ + LL^=des_SPtrans[0][u1]; \ + LL^=des_SPtrans[2][u2]; \ + u3=(int)u>>8L; \ + u1=(int)u&0x3f; \ + u3&=0x3f; \ + LL^=des_SPtrans[4][u1]; \ + LL^=des_SPtrans[6][u3]; \ + u2=(int)t>>8L; \ + u1=(int)t&0x3f; \ + u2&=0x3f; \ + t>>=16L; \ + LL^=des_SPtrans[1][u1]; \ + LL^=des_SPtrans[3][u2]; \ + u3=(int)t>>8L; \ + u1=(int)t&0x3f; \ + u3&=0x3f; \ + LL^=des_SPtrans[5][u1]; \ + LL^=des_SPtrans[7][u3]; } +#endif +#ifdef DES_RISC2 +#define D_ENCRYPT(LL,R,S) {\ + unsigned int u1,u2,s1,s2; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u>>=2L; \ + t=ROTATE(t,6); \ + u2=(int)u>>8L; \ + u1=(int)u&0x3f; \ + u2&=0x3f; \ + LL^=des_SPtrans[0][u1]; \ + LL^=des_SPtrans[2][u2]; \ + s1=(int)u>>16L; \ + s2=(int)u>>24L; \ + s1&=0x3f; \ + s2&=0x3f; \ + LL^=des_SPtrans[4][s1]; \ + LL^=des_SPtrans[6][s2]; \ + u2=(int)t>>8L; \ + u1=(int)t&0x3f; \ + u2&=0x3f; \ + LL^=des_SPtrans[1][u1]; \ + LL^=des_SPtrans[3][u2]; \ + s1=(int)t>>16; \ + s2=(int)t>>24L; \ + s1&=0x3f; \ + s2&=0x3f; \ + LL^=des_SPtrans[5][s1]; \ + LL^=des_SPtrans[7][s2]; } +#endif + +#else + +#define D_ENCRYPT(LL,R,S) {\ + LOAD_DATA_tmp(R,S,u,t,E0,E1); \ + t=ROTATE(t,4); \ + LL^=\ + des_SPtrans[0][(u>> 2L)&0x3f]^ \ + des_SPtrans[2][(u>>10L)&0x3f]^ \ + des_SPtrans[4][(u>>18L)&0x3f]^ \ + des_SPtrans[6][(u>>26L)&0x3f]^ \ + des_SPtrans[1][(t>> 2L)&0x3f]^ \ + des_SPtrans[3][(t>>10L)&0x3f]^ \ + des_SPtrans[5][(t>>18L)&0x3f]^ \ + des_SPtrans[7][(t>>26L)&0x3f]; } +#endif +#endif + + /* IP and FP + * The problem is more of a geometric problem that random bit fiddling. + 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 + 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 + 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 + 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 + + 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 + 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 + 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 + 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 + + The output has been subject to swaps of the form + 0 1 -> 3 1 but the odd and even bits have been put into + 2 3 2 0 + different words. The main trick is to remember that + t=((l>>size)^r)&(mask); + r^=t; + l^=(t<>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + +#define IP(l,r) \ + { \ + register DES_LONG tt; \ + PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ + PERM_OP(l,r,tt,16,0x0000ffffL); \ + PERM_OP(r,l,tt, 2,0x33333333L); \ + PERM_OP(l,r,tt, 8,0x00ff00ffL); \ + PERM_OP(r,l,tt, 1,0x55555555L); \ + } + +#define FP(l,r) \ + { \ + register DES_LONG tt; \ + PERM_OP(l,r,tt, 1,0x55555555L); \ + PERM_OP(r,l,tt, 8,0x00ff00ffL); \ + PERM_OP(l,r,tt, 2,0x33333333L); \ + PERM_OP(r,l,tt,16,0x0000ffffL); \ + PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ + } + +extern const DES_LONG des_SPtrans[8][64]; + +#ifndef NOPROTO +void fcrypt_body(DES_LONG *out,des_key_schedule ks, + DES_LONG Eswap0, DES_LONG Eswap1); +#else +void fcrypt_body(); +#endif + +#endif diff --git a/src/libcrypto/libdes/des_opts.c b/src/libcrypto/libdes/des_opts.c new file mode 100644 index 000000000..b6693c405 --- /dev/null +++ b/src/libcrypto/libdes/des_opts.c @@ -0,0 +1,620 @@ +/* crypto/des/des_opts.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* define PART1, PART2, PART3 or PART4 to build only with a few of the options. + * This is for machines with 64k code segment size restrictions. */ + +#ifndef MSDOS +#define TIMES +#endif + +#include +#ifndef MSDOS +#include +#else +#include +extern void exit(); +#endif +#include +#ifndef VMS +#ifndef _IRIX +#include +#endif +#ifdef TIMES +#include +#include +#endif +#else /* VMS */ +#include +struct tms { + time_t tms_utime; + time_t tms_stime; + time_t tms_uchild; /* I dunno... */ + time_t tms_uchildsys; /* so these names are a guess :-) */ + } +#endif +#ifndef TIMES +#include +#endif + +#ifdef sun +#include +#include +#endif + +#include "des_locl.h" +#include "spr.h" + +#define DES_DEFAULT_OPTIONS + +#if !defined(PART1) && !defined(PART2) && !defined(PART3) && !defined(PART4) +#define PART1 +#define PART2 +#define PART3 +#define PART4 +#endif + +#ifdef PART1 + +#undef DES_UNROLL +#undef DES_RISC1 +#undef DES_RISC2 +#undef DES_PTR +#undef D_ENCRYPT +#define des_encrypt des_encrypt_u4_cisc_idx +#define des_encrypt2 des_encrypt2_u4_cisc_idx +#define des_encrypt3 des_encrypt3_u4_cisc_idx +#define des_decrypt3 des_decrypt3_u4_cisc_idx +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#define DES_UNROLL +#undef DES_RISC1 +#undef DES_RISC2 +#undef DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u16_cisc_idx +#define des_encrypt2 des_encrypt2_u16_cisc_idx +#define des_encrypt3 des_encrypt3_u16_cisc_idx +#define des_decrypt3 des_decrypt3_u16_cisc_idx +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#undef DES_UNROLL +#define DES_RISC1 +#undef DES_RISC2 +#undef DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u4_risc1_idx +#define des_encrypt2 des_encrypt2_u4_risc1_idx +#define des_encrypt3 des_encrypt3_u4_risc1_idx +#define des_decrypt3 des_decrypt3_u4_risc1_idx +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#endif + +#ifdef PART2 + +#undef DES_UNROLL +#undef DES_RISC1 +#define DES_RISC2 +#undef DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u4_risc2_idx +#define des_encrypt2 des_encrypt2_u4_risc2_idx +#define des_encrypt3 des_encrypt3_u4_risc2_idx +#define des_decrypt3 des_decrypt3_u4_risc2_idx +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#define DES_UNROLL +#define DES_RISC1 +#undef DES_RISC2 +#undef DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u16_risc1_idx +#define des_encrypt2 des_encrypt2_u16_risc1_idx +#define des_encrypt3 des_encrypt3_u16_risc1_idx +#define des_decrypt3 des_decrypt3_u16_risc1_idx +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#define DES_UNROLL +#undef DES_RISC1 +#define DES_RISC2 +#undef DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u16_risc2_idx +#define des_encrypt2 des_encrypt2_u16_risc2_idx +#define des_encrypt3 des_encrypt3_u16_risc2_idx +#define des_decrypt3 des_decrypt3_u16_risc2_idx +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#endif + +#ifdef PART3 + +#undef DES_UNROLL +#undef DES_RISC1 +#undef DES_RISC2 +#define DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u4_cisc_ptr +#define des_encrypt2 des_encrypt2_u4_cisc_ptr +#define des_encrypt3 des_encrypt3_u4_cisc_ptr +#define des_decrypt3 des_decrypt3_u4_cisc_ptr +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#define DES_UNROLL +#undef DES_RISC1 +#undef DES_RISC2 +#define DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u16_cisc_ptr +#define des_encrypt2 des_encrypt2_u16_cisc_ptr +#define des_encrypt3 des_encrypt3_u16_cisc_ptr +#define des_decrypt3 des_decrypt3_u16_cisc_ptr +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#undef DES_UNROLL +#define DES_RISC1 +#undef DES_RISC2 +#define DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u4_risc1_ptr +#define des_encrypt2 des_encrypt2_u4_risc1_ptr +#define des_encrypt3 des_encrypt3_u4_risc1_ptr +#define des_decrypt3 des_decrypt3_u4_risc1_ptr +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#endif + +#ifdef PART4 + +#undef DES_UNROLL +#undef DES_RISC1 +#define DES_RISC2 +#define DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u4_risc2_ptr +#define des_encrypt2 des_encrypt2_u4_risc2_ptr +#define des_encrypt3 des_encrypt3_u4_risc2_ptr +#define des_decrypt3 des_decrypt3_u4_risc2_ptr +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#define DES_UNROLL +#define DES_RISC1 +#undef DES_RISC2 +#define DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u16_risc1_ptr +#define des_encrypt2 des_encrypt2_u16_risc1_ptr +#define des_encrypt3 des_encrypt3_u16_risc1_ptr +#define des_decrypt3 des_decrypt3_u16_risc1_ptr +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#define DES_UNROLL +#undef DES_RISC1 +#define DES_RISC2 +#define DES_PTR +#undef D_ENCRYPT +#undef des_encrypt +#undef des_encrypt2 +#undef des_encrypt3 +#undef des_decrypt3 +#define des_encrypt des_encrypt_u16_risc2_ptr +#define des_encrypt2 des_encrypt2_u16_risc2_ptr +#define des_encrypt3 des_encrypt3_u16_risc2_ptr +#define des_decrypt3 des_decrypt3_u16_risc2_ptr +#undef HEADER_DES_LOCL_H +#include "des_enc.c" + +#endif + +/* The following if from times(3) man page. It may need to be changed */ +#ifndef HZ +# ifndef CLK_TCK +# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */ +# ifndef VMS +# define HZ 100.0 +# else /* VMS */ +# define HZ 100.0 +# endif +# else /* _BSD_CLK_TCK_ */ +# define HZ ((double)_BSD_CLK_TCK_) +# endif +# else /* CLK_TCK */ +# define HZ ((double)CLK_TCK) +# endif +#endif + +#define BUFSIZE ((long)1024) +long run=0; + +#ifndef NOPROTO +double Time_F(int s); +#else +double Time_F(); +#endif + +#ifdef SIGALRM +#if defined(__STDC__) || defined(sgi) +#define SIGRETTYPE void +#else +#define SIGRETTYPE int +#endif + +#ifndef NOPROTO +SIGRETTYPE sig_done(int sig); +#else +SIGRETTYPE sig_done(); +#endif + +SIGRETTYPE sig_done(sig) +int sig; + { + signal(SIGALRM,sig_done); + run=0; +#ifdef LINT + sig=sig; +#endif + } +#endif + +#define START 0 +#define STOP 1 + +double Time_F(s) +int s; + { + double ret; +#ifdef TIMES + static struct tms tstart,tend; + + if (s == START) + { + times(&tstart); + return(0); + } + else + { + times(&tend); + ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ; + return((ret == 0.0)?1e-6:ret); + } +#else /* !times() */ + static struct timeb tstart,tend; + long i; + + if (s == START) + { + ftime(&tstart); + return(0); + } + else + { + ftime(&tend); + i=(long)tend.millitm-(long)tstart.millitm; + ret=((double)(tend.time-tstart.time))+((double)i)/1000.0; + return((ret == 0.0)?1e-6:ret); + } +#endif + } + +#ifdef SIGALRM +#define print_name(name) fprintf(stderr,"Doing %s's for 10 seconds\n",name); alarm(10); +#else +#define print_name(name) fprintf(stderr,"Doing %s %ld times\n",name,cb); +#endif + +#define time_it(func,name,index) \ + print_name(name); \ + Time_F(START); \ + for (count=0,run=1; COND(cb); count++) \ + { \ + unsigned long d[2]; \ + func(d,&(sch[0]),DES_ENCRYPT); \ + } \ + tm[index]=Time_F(STOP); \ + fprintf(stderr,"%ld %s's in %.2f second\n",count,name,tm[index]); \ + tm[index]=((double)COUNT(cb))/tm[index]; + +#define print_it(name,index) \ + fprintf(stderr,"%s bytes per sec = %12.2f (%5.1fuS)\n",name, \ + tm[index]*8,1.0e6/tm[index]); + +int main(argc,argv) +int argc; +char **argv; + { + long count; + static unsigned char buf[BUFSIZE]; + static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; + static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12}; + static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34}; + des_key_schedule sch,sch2,sch3; + double d,tm[16],max=0; + int rank[16]; + char *str[16]; + int max_idx=0,i,num=0,j; +#ifndef SIGALARM + long ca,cb,cc,cd,ce; +#endif + + for (i=0; i<12; i++) + { + tm[i]=0.0; + rank[i]=0; + } + +#ifndef TIMES + fprintf(stderr,"To get the most acurate results, try to run this\n"); + fprintf(stderr,"program when this computer is idle.\n"); +#endif + + des_set_key((C_Block *)key,sch); + des_set_key((C_Block *)key2,sch2); + des_set_key((C_Block *)key3,sch3); + +#ifndef SIGALRM + fprintf(stderr,"First we calculate the approximate speed ...\n"); + des_set_key((C_Block *)key,sch); + count=10; + do { + long i; + unsigned long data[2]; + + count*=2; + Time_F(START); + for (i=count; i; i--) + des_encrypt(data,&(sch[0]),DES_ENCRYPT); + d=Time_F(STOP); + } while (d < 3.0); + ca=count; + cb=count*3; + cc=count*3*8/BUFSIZE+1; + cd=count*8/BUFSIZE+1; + + ce=count/20+1; +#define COND(d) (count != (d)) +#define COUNT(d) (d) +#else +#define COND(c) (run) +#define COUNT(d) (count) + signal(SIGALRM,sig_done); + alarm(10); +#endif + +#ifdef PART1 + time_it(des_encrypt_u4_cisc_idx, "des_encrypt_u4_cisc_idx ", 0); + time_it(des_encrypt_u16_cisc_idx, "des_encrypt_u16_cisc_idx ", 1); + time_it(des_encrypt_u4_risc1_idx, "des_encrypt_u4_risc1_idx ", 2); + num+=3; +#endif +#ifdef PART2 + time_it(des_encrypt_u16_risc1_idx,"des_encrypt_u16_risc1_idx", 3); + time_it(des_encrypt_u4_risc2_idx, "des_encrypt_u4_risc2_idx ", 4); + time_it(des_encrypt_u16_risc2_idx,"des_encrypt_u16_risc2_idx", 5); + num+=3; +#endif +#ifdef PART3 + time_it(des_encrypt_u4_cisc_ptr, "des_encrypt_u4_cisc_ptr ", 6); + time_it(des_encrypt_u16_cisc_ptr, "des_encrypt_u16_cisc_ptr ", 7); + time_it(des_encrypt_u4_risc1_ptr, "des_encrypt_u4_risc1_ptr ", 8); + num+=3; +#endif +#ifdef PART4 + time_it(des_encrypt_u16_risc1_ptr,"des_encrypt_u16_risc1_ptr", 9); + time_it(des_encrypt_u4_risc2_ptr, "des_encrypt_u4_risc2_ptr ",10); + time_it(des_encrypt_u16_risc2_ptr,"des_encrypt_u16_risc2_ptr",11); + num+=3; +#endif + +#ifdef PART1 + str[0]=" 4 c i"; + print_it("des_encrypt_u4_cisc_idx ",0); + max=tm[0]; + max_idx=0; + str[1]="16 c i"; + print_it("des_encrypt_u16_cisc_idx ",1); + if (max < tm[1]) { max=tm[1]; max_idx=1; } + str[2]=" 4 r1 i"; + print_it("des_encrypt_u4_risc1_idx ",2); + if (max < tm[2]) { max=tm[2]; max_idx=2; } +#endif +#ifdef PART2 + str[3]="16 r1 i"; + print_it("des_encrypt_u16_risc1_idx",3); + if (max < tm[3]) { max=tm[3]; max_idx=3; } + str[4]=" 4 r2 i"; + print_it("des_encrypt_u4_risc2_idx ",4); + if (max < tm[4]) { max=tm[4]; max_idx=4; } + str[5]="16 r2 i"; + print_it("des_encrypt_u16_risc2_idx",5); + if (max < tm[5]) { max=tm[5]; max_idx=5; } +#endif +#ifdef PART3 + str[6]=" 4 c p"; + print_it("des_encrypt_u4_cisc_ptr ",6); + if (max < tm[6]) { max=tm[6]; max_idx=6; } + str[7]="16 c p"; + print_it("des_encrypt_u16_cisc_ptr ",7); + if (max < tm[7]) { max=tm[7]; max_idx=7; } + str[8]=" 4 r1 p"; + print_it("des_encrypt_u4_risc1_ptr ",8); + if (max < tm[8]) { max=tm[8]; max_idx=8; } +#endif +#ifdef PART4 + str[9]="16 r1 p"; + print_it("des_encrypt_u16_risc1_ptr",9); + if (max < tm[9]) { max=tm[9]; max_idx=9; } + str[10]=" 4 r2 p"; + print_it("des_encrypt_u4_risc2_ptr ",10); + if (max < tm[10]) { max=tm[10]; max_idx=10; } + str[11]="16 r2 p"; + print_it("des_encrypt_u16_risc2_ptr",11); + if (max < tm[11]) { max=tm[11]; max_idx=11; } +#endif + printf("options des ecb/s\n"); + printf("%s %12.2f 100.0%%\n",str[max_idx],tm[max_idx]); + d=tm[max_idx]; + tm[max_idx]= -2.0; + max= -1.0; + for (;;) + { + for (i=0; i<12; i++) + { + if (max < tm[i]) { max=tm[i]; j=i; } + } + if (max < 0.0) break; + printf("%s %12.2f %4.1f%%\n",str[j],tm[j],tm[j]/d*100.0); + tm[j]= -2.0; + max= -1.0; + } + + switch (max_idx) + { + case 0: + printf("-DDES_DEFAULT_OPTIONS\n"); + break; + case 1: + printf("-DDES_UNROLL\n"); + break; + case 2: + printf("-DDES_RISC1\n"); + break; + case 3: + printf("-DDES_UNROLL -DDES_RISC1\n"); + break; + case 4: + printf("-DDES_RISC2\n"); + break; + case 5: + printf("-DDES_UNROLL -DDES_RISC2\n"); + break; + case 6: + printf("-DDES_PTR\n"); + break; + case 7: + printf("-DDES_UNROLL -DDES_PTR\n"); + break; + case 8: + printf("-DDES_RISC1 -DDES_PTR\n"); + break; + case 9: + printf("-DDES_UNROLL -DDES_RISC1 -DDES_PTR\n"); + break; + case 10: + printf("-DDES_RISC2 -DDES_PTR\n"); + break; + case 11: + printf("-DDES_UNROLL -DDES_RISC2 -DDES_PTR\n"); + break; + } + exit(0); +#if defined(LINT) || defined(MSDOS) + return(0); +#endif + } diff --git a/src/libcrypto/libdes/des_ver.h b/src/libcrypto/libdes/des_ver.h new file mode 100644 index 000000000..98352bc0d --- /dev/null +++ b/src/libcrypto/libdes/des_ver.h @@ -0,0 +1,60 @@ +/* crypto/des/des_ver.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +extern char *DES_version; /* SSLeay version string */ +extern char *libdes_version; /* old libdes version string */ diff --git a/src/libcrypto/libdes/destest.c b/src/libcrypto/libdes/destest.c new file mode 100644 index 000000000..ae896499e --- /dev/null +++ b/src/libcrypto/libdes/destest.c @@ -0,0 +1,871 @@ +/* crypto/des/destest.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#if defined(WIN32) || defined(WIN16) || defined(WINDOWS) +#ifndef MSDOS +#define MSDOS +#endif +#endif + +#include +#include +#ifndef MSDOS +#include +#else +#include +#endif +#include +#include "des_locl.h" + +/* tisk tisk - the test keys don't all have odd parity :-( */ +/* test data */ +#define NUM_TESTS 34 +static unsigned char key_data[NUM_TESTS][8]={ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, + {0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}, + {0x7C,0xA1,0x10,0x45,0x4A,0x1A,0x6E,0x57}, + {0x01,0x31,0xD9,0x61,0x9D,0xC1,0x37,0x6E}, + {0x07,0xA1,0x13,0x3E,0x4A,0x0B,0x26,0x86}, + {0x38,0x49,0x67,0x4C,0x26,0x02,0x31,0x9E}, + {0x04,0xB9,0x15,0xBA,0x43,0xFE,0xB5,0xB6}, + {0x01,0x13,0xB9,0x70,0xFD,0x34,0xF2,0xCE}, + {0x01,0x70,0xF1,0x75,0x46,0x8F,0xB5,0xE6}, + {0x43,0x29,0x7F,0xAD,0x38,0xE3,0x73,0xFE}, + {0x07,0xA7,0x13,0x70,0x45,0xDA,0x2A,0x16}, + {0x04,0x68,0x91,0x04,0xC2,0xFD,0x3B,0x2F}, + {0x37,0xD0,0x6B,0xB5,0x16,0xCB,0x75,0x46}, + {0x1F,0x08,0x26,0x0D,0x1A,0xC2,0x46,0x5E}, + {0x58,0x40,0x23,0x64,0x1A,0xBA,0x61,0x76}, + {0x02,0x58,0x16,0x16,0x46,0x29,0xB0,0x07}, + {0x49,0x79,0x3E,0xBC,0x79,0xB3,0x25,0x8F}, + {0x4F,0xB0,0x5E,0x15,0x15,0xAB,0x73,0xA7}, + {0x49,0xE9,0x5D,0x6D,0x4C,0xA2,0x29,0xBF}, + {0x01,0x83,0x10,0xDC,0x40,0x9B,0x26,0xD6}, + {0x1C,0x58,0x7F,0x1C,0x13,0x92,0x4F,0xEF}, + {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, + {0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E}, + {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}}; + +static unsigned char plain_data[NUM_TESTS][8]={ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, + {0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, + {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, + {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0x01,0xA1,0xD6,0xD0,0x39,0x77,0x67,0x42}, + {0x5C,0xD5,0x4C,0xA8,0x3D,0xEF,0x57,0xDA}, + {0x02,0x48,0xD4,0x38,0x06,0xF6,0x71,0x72}, + {0x51,0x45,0x4B,0x58,0x2D,0xDF,0x44,0x0A}, + {0x42,0xFD,0x44,0x30,0x59,0x57,0x7F,0xA2}, + {0x05,0x9B,0x5E,0x08,0x51,0xCF,0x14,0x3A}, + {0x07,0x56,0xD8,0xE0,0x77,0x47,0x61,0xD2}, + {0x76,0x25,0x14,0xB8,0x29,0xBF,0x48,0x6A}, + {0x3B,0xDD,0x11,0x90,0x49,0x37,0x28,0x02}, + {0x26,0x95,0x5F,0x68,0x35,0xAF,0x60,0x9A}, + {0x16,0x4D,0x5E,0x40,0x4F,0x27,0x52,0x32}, + {0x6B,0x05,0x6E,0x18,0x75,0x9F,0x5C,0xCA}, + {0x00,0x4B,0xD6,0xEF,0x09,0x17,0x60,0x62}, + {0x48,0x0D,0x39,0x00,0x6E,0xE7,0x62,0xF2}, + {0x43,0x75,0x40,0xC8,0x69,0x8F,0x3C,0xFA}, + {0x07,0x2D,0x43,0xA0,0x77,0x07,0x52,0x92}, + {0x02,0xFE,0x55,0x77,0x81,0x17,0xF1,0x2A}, + {0x1D,0x9D,0x5C,0x50,0x18,0xF7,0x28,0xC2}, + {0x30,0x55,0x32,0x28,0x6D,0x6F,0x29,0x5A}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}, + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; + +static unsigned char cipher_data[NUM_TESTS][8]={ + {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7}, + {0x73,0x59,0xB2,0x16,0x3E,0x4E,0xDC,0x58}, + {0x95,0x8E,0x6E,0x62,0x7A,0x05,0x55,0x7B}, + {0xF4,0x03,0x79,0xAB,0x9E,0x0E,0xC5,0x33}, + {0x17,0x66,0x8D,0xFC,0x72,0x92,0x53,0x2D}, + {0x8A,0x5A,0xE1,0xF8,0x1A,0xB8,0xF2,0xDD}, + {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7}, + {0xED,0x39,0xD9,0x50,0xFA,0x74,0xBC,0xC4}, + {0x69,0x0F,0x5B,0x0D,0x9A,0x26,0x93,0x9B}, + {0x7A,0x38,0x9D,0x10,0x35,0x4B,0xD2,0x71}, + {0x86,0x8E,0xBB,0x51,0xCA,0xB4,0x59,0x9A}, + {0x71,0x78,0x87,0x6E,0x01,0xF1,0x9B,0x2A}, + {0xAF,0x37,0xFB,0x42,0x1F,0x8C,0x40,0x95}, + {0x86,0xA5,0x60,0xF1,0x0E,0xC6,0xD8,0x5B}, + {0x0C,0xD3,0xDA,0x02,0x00,0x21,0xDC,0x09}, + {0xEA,0x67,0x6B,0x2C,0xB7,0xDB,0x2B,0x7A}, + {0xDF,0xD6,0x4A,0x81,0x5C,0xAF,0x1A,0x0F}, + {0x5C,0x51,0x3C,0x9C,0x48,0x86,0xC0,0x88}, + {0x0A,0x2A,0xEE,0xAE,0x3F,0xF4,0xAB,0x77}, + {0xEF,0x1B,0xF0,0x3E,0x5D,0xFA,0x57,0x5A}, + {0x88,0xBF,0x0D,0xB6,0xD7,0x0D,0xEE,0x56}, + {0xA1,0xF9,0x91,0x55,0x41,0x02,0x0B,0x56}, + {0x6F,0xBF,0x1C,0xAF,0xCF,0xFD,0x05,0x56}, + {0x2F,0x22,0xE4,0x9B,0xAB,0x7C,0xA1,0xAC}, + {0x5A,0x6B,0x61,0x2C,0xC2,0x6C,0xCE,0x4A}, + {0x5F,0x4C,0x03,0x8E,0xD1,0x2B,0x2E,0x41}, + {0x63,0xFA,0xC0,0xD0,0x34,0xD9,0xF7,0x93}, + {0x61,0x7B,0x3A,0x0C,0xE8,0xF0,0x71,0x00}, + {0xDB,0x95,0x86,0x05,0xF8,0xC8,0xC6,0x06}, + {0xED,0xBF,0xD1,0xC6,0x6C,0x29,0xCC,0xC7}, + {0x35,0x55,0x50,0xB2,0x15,0x0E,0x24,0x51}, + {0xCA,0xAA,0xAF,0x4D,0xEA,0xF1,0xDB,0xAE}, + {0xD5,0xD4,0x4F,0xF7,0x20,0x68,0x3D,0x0D}, + {0x2A,0x2B,0xB0,0x08,0xDF,0x97,0xC2,0xF2}}; + +static unsigned char cipher_ecb2[NUM_TESTS-1][8]={ + {0x92,0x95,0xB5,0x9B,0xB3,0x84,0x73,0x6E}, + {0x19,0x9E,0x9D,0x6D,0xF3,0x9A,0xA8,0x16}, + {0x2A,0x4B,0x4D,0x24,0x52,0x43,0x84,0x27}, + {0x35,0x84,0x3C,0x01,0x9D,0x18,0xC5,0xB6}, + {0x4A,0x5B,0x2F,0x42,0xAA,0x77,0x19,0x25}, + {0xA0,0x6B,0xA9,0xB8,0xCA,0x5B,0x17,0x8A}, + {0xAB,0x9D,0xB7,0xFB,0xED,0x95,0xF2,0x74}, + {0x3D,0x25,0x6C,0x23,0xA7,0x25,0x2F,0xD6}, + {0xB7,0x6F,0xAB,0x4F,0xBD,0xBD,0xB7,0x67}, + {0x8F,0x68,0x27,0xD6,0x9C,0xF4,0x1A,0x10}, + {0x82,0x57,0xA1,0xD6,0x50,0x5E,0x81,0x85}, + {0xA2,0x0F,0x0A,0xCD,0x80,0x89,0x7D,0xFA}, + {0xCD,0x2A,0x53,0x3A,0xDB,0x0D,0x7E,0xF3}, + {0xD2,0xC2,0xBE,0x27,0xE8,0x1B,0x68,0xE3}, + {0xE9,0x24,0xCF,0x4F,0x89,0x3C,0x5B,0x0A}, + {0xA7,0x18,0xC3,0x9F,0xFA,0x9F,0xD7,0x69}, + {0x77,0x2C,0x79,0xB1,0xD2,0x31,0x7E,0xB1}, + {0x49,0xAB,0x92,0x7F,0xD0,0x22,0x00,0xB7}, + {0xCE,0x1C,0x6C,0x7D,0x85,0xE3,0x4A,0x6F}, + {0xBE,0x91,0xD6,0xE1,0x27,0xB2,0xE9,0x87}, + {0x70,0x28,0xAE,0x8F,0xD1,0xF5,0x74,0x1A}, + {0xAA,0x37,0x80,0xBB,0xF3,0x22,0x1D,0xDE}, + {0xA6,0xC4,0xD2,0x5E,0x28,0x93,0xAC,0xB3}, + {0x22,0x07,0x81,0x5A,0xE4,0xB7,0x1A,0xAD}, + {0xDC,0xCE,0x05,0xE7,0x07,0xBD,0xF5,0x84}, + {0x26,0x1D,0x39,0x2C,0xB3,0xBA,0xA5,0x85}, + {0xB4,0xF7,0x0F,0x72,0xFB,0x04,0xF0,0xDC}, + {0x95,0xBA,0xA9,0x4E,0x87,0x36,0xF2,0x89}, + {0xD4,0x07,0x3A,0xF1,0x5A,0x17,0x82,0x0E}, + {0xEF,0x6F,0xAF,0xA7,0x66,0x1A,0x7E,0x89}, + {0xC1,0x97,0xF5,0x58,0x74,0x8A,0x20,0xE7}, + {0x43,0x34,0xCF,0xDA,0x22,0xC4,0x86,0xC8}, + {0x08,0xD7,0xB4,0xFB,0x62,0x9D,0x08,0x85}}; + +static unsigned char cbc_key [8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; +static unsigned char cbc2_key[8]={0xf0,0xe1,0xd2,0xc3,0xb4,0xa5,0x96,0x87}; +static unsigned char cbc3_key[8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; +static unsigned char cbc_iv [8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; +static char cbc_data[40]="7654321 Now is the time for \0001"; + +static unsigned char cbc_ok[32]={ + 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4, + 0xac,0xd8,0xae,0xfd,0xdf,0xd8,0xa1,0xeb, + 0x46,0x8e,0x91,0x15,0x78,0x88,0xba,0x68, + 0x1d,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4}; + +static unsigned char xcbc_ok[32]={ + 0x86,0x74,0x81,0x0D,0x61,0xA4,0xA5,0x48, + 0xB9,0x93,0x03,0xE1,0xB8,0xBB,0xBD,0xBD, + 0x64,0x30,0x0B,0xB9,0x06,0x65,0x81,0x76, + 0x04,0x1D,0x77,0x62,0x17,0xCA,0x2B,0xD2, + }; + +static unsigned char cbc3_ok[32]={ + 0x3F,0xE3,0x01,0xC9,0x62,0xAC,0x01,0xD0, + 0x22,0x13,0x76,0x3C,0x1C,0xBD,0x4C,0xDC, + 0x79,0x96,0x57,0xC0,0x64,0xEC,0xF5,0xD4, + 0x1C,0x67,0x38,0x12,0xCF,0xDE,0x96,0x75}; + +static unsigned char pcbc_ok[32]={ + 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4, + 0x6d,0xec,0xb4,0x70,0xa0,0xe5,0x6b,0x15, + 0xae,0xa6,0xbf,0x61,0xed,0x7d,0x9c,0x9f, + 0xf7,0x17,0x46,0x3b,0x8a,0xb3,0xcc,0x88}; + +static unsigned char cfb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; +static unsigned char cfb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef}; +static unsigned char cfb_buf1[40],cfb_buf2[40],cfb_tmp[8]; +static unsigned char plain[24]= + { + 0x4e,0x6f,0x77,0x20,0x69,0x73, + 0x20,0x74,0x68,0x65,0x20,0x74, + 0x69,0x6d,0x65,0x20,0x66,0x6f, + 0x72,0x20,0x61,0x6c,0x6c,0x20 + }; +static unsigned char cfb_cipher8[24]= { + 0xf3,0x1f,0xda,0x07,0x01,0x14, 0x62,0xee,0x18,0x7f,0x43,0xd8, + 0x0a,0x7c,0xd9,0xb5,0xb0,0xd2, 0x90,0xda,0x6e,0x5b,0x9a,0x87 }; +static unsigned char cfb_cipher16[24]={ + 0xF3,0x09,0x87,0x87,0x7F,0x57, 0xF7,0x3C,0x36,0xB6,0xDB,0x70, + 0xD8,0xD5,0x34,0x19,0xD3,0x86, 0xB2,0x23,0xB7,0xB2,0xAD,0x1B }; +static unsigned char cfb_cipher32[24]={ + 0xF3,0x09,0x62,0x49,0xA4,0xDF, 0xA4,0x9F,0x33,0xDC,0x7B,0xAD, + 0x4C,0xC8,0x9F,0x64,0xE4,0x53, 0xE5,0xEC,0x67,0x20,0xDA,0xB6 }; +static unsigned char cfb_cipher48[24]={ + 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x30,0xB5,0x15,0xEC,0xBB,0x85, + 0x97,0x5A,0x13,0x8C,0x68,0x60, 0xE2,0x38,0x34,0x3C,0xDC,0x1F }; +static unsigned char cfb_cipher64[24]={ + 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x6E,0x51,0xA6,0x9E,0x83,0x9B, + 0x1A,0x92,0xF7,0x84,0x03,0x46, 0x71,0x33,0x89,0x8E,0xA6,0x22 }; + +static unsigned char ofb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}; +static unsigned char ofb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef}; +static unsigned char ofb_buf1[24],ofb_buf2[24],ofb_tmp[8]; +static unsigned char ofb_cipher[24]= + { + 0xf3,0x09,0x62,0x49,0xc7,0xf4,0x6e,0x51, + 0x35,0xf2,0x4a,0x24,0x2e,0xeb,0x3d,0x3f, + 0x3d,0x6d,0x5b,0xe3,0x25,0x5a,0xf8,0xc3 + }; + +DES_LONG cbc_cksum_ret=0xB462FEF7L; +unsigned char cbc_cksum_data[8]={0x1D,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4}; + +#ifndef NOPROTO +static char *pt(unsigned char *p); +static int cfb_test(int bits, unsigned char *cfb_cipher); +static int cfb64_test(unsigned char *cfb_cipher); +static int ede_cfb64_test(unsigned char *cfb_cipher); +#else +static char *pt(); +static int cfb_test(); +static int cfb64_test(); +static int ede_cfb64_test(); +#endif + +int main(argc,argv) +int argc; +char *argv[]; + { + int i,j,err=0; + des_cblock in,out,outin,iv3; + des_key_schedule ks,ks2,ks3; + unsigned char cbc_in[40]; + unsigned char cbc_out[40]; + DES_LONG cs; + unsigned char qret[4][4],cret[8]; + DES_LONG lqret[4]; + int num; + char *str; + + printf("Doing ecb\n"); + for (i=0; i>4)&0xf]; + ret[i*2+1]=f[p[i]&0xf]; + } + ret[16]='\0'; + return(ret); + } + +#ifndef LIBDES_LIT + +static int cfb_test(bits, cfb_cipher) +int bits; +unsigned char *cfb_cipher; + { + des_key_schedule ks; + int i,err=0; + + des_key_sched((C_Block *)cfb_key,ks); + memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); + des_cfb_encrypt(plain,cfb_buf1,bits,(long)sizeof(plain),ks, + (C_Block *)cfb_tmp,DES_ENCRYPT); + if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0) + { + err=1; + printf("cfb_encrypt encrypt error\n"); + for (i=0; i<24; i+=8) + printf("%s\n",pt(&(cfb_buf1[i]))); + } + memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); + des_cfb_encrypt(cfb_buf1,cfb_buf2,bits,(long)sizeof(plain),ks, + (C_Block *)cfb_tmp,DES_DECRYPT); + if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0) + { + err=1; + printf("cfb_encrypt decrypt error\n"); + for (i=0; i<24; i+=8) + printf("%s\n",pt(&(cfb_buf1[i]))); + } + return(err); + } + +static int cfb64_test(cfb_cipher) +unsigned char *cfb_cipher; + { + des_key_schedule ks; + int err=0,i,n; + + des_key_sched((C_Block *)cfb_key,ks); + memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); + n=0; + des_cfb64_encrypt(plain,cfb_buf1,(long)12,ks, + (C_Block *)cfb_tmp,&n,DES_ENCRYPT); + des_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]), + (long)sizeof(plain)-12,ks, + (C_Block *)cfb_tmp,&n,DES_ENCRYPT); + if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0) + { + err=1; + printf("cfb_encrypt encrypt error\n"); + for (i=0; i<24; i+=8) + printf("%s\n",pt(&(cfb_buf1[i]))); + } + memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); + n=0; + des_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks, + (C_Block *)cfb_tmp,&n,DES_DECRYPT); + des_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]), + (long)sizeof(plain)-17,ks, + (C_Block *)cfb_tmp,&n,DES_DECRYPT); + if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0) + { + err=1; + printf("cfb_encrypt decrypt error\n"); + for (i=0; i<24; i+=8) + printf("%s\n",pt(&(cfb_buf2[i]))); + } + return(err); + } + +static int ede_cfb64_test(cfb_cipher) +unsigned char *cfb_cipher; + { + des_key_schedule ks; + int err=0,i,n; + + des_key_sched((C_Block *)cfb_key,ks); + memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); + n=0; + des_ede3_cfb64_encrypt(plain,cfb_buf1,(long)12,ks,ks,ks, + (C_Block *)cfb_tmp,&n,DES_ENCRYPT); + des_ede3_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]), + (long)sizeof(plain)-12,ks,ks,ks, + (C_Block *)cfb_tmp,&n,DES_ENCRYPT); + if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0) + { + err=1; + printf("ede_cfb_encrypt encrypt error\n"); + for (i=0; i<24; i+=8) + printf("%s\n",pt(&(cfb_buf1[i]))); + } + memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv)); + n=0; + des_ede3_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks,ks,ks, + (C_Block *)cfb_tmp,&n,DES_DECRYPT); + des_ede3_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]), + (long)sizeof(plain)-17,ks,ks,ks, + (C_Block *)cfb_tmp,&n,DES_DECRYPT); + if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0) + { + err=1; + printf("ede_cfb_encrypt decrypt error\n"); + for (i=0; i<24; i+=8) + printf("%s\n",pt(&(cfb_buf2[i]))); + } + return(err); + } + +#endif + diff --git a/src/libcrypto/libdes/ecb_enc.c b/src/libcrypto/libdes/ecb_enc.c new file mode 100644 index 000000000..0b7afcf3a --- /dev/null +++ b/src/libcrypto/libdes/ecb_enc.c @@ -0,0 +1,128 @@ +/* crypto/des/ecb_enc.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "des_locl.h" +#include "spr.h" + +char *libdes_version="libdes v 3.24 - 20-Apr-1996 - eay"; +char *DES_version="DES part of SSLeay 0.8.2b 08-Jan-1998"; + +/* RCSID $Id: ecb_enc.c,v 1.1 2004/03/15 20:35:25 as Exp $ */ +/* This function ifdef'ed out for FreeS/WAN project. */ +#ifdef notdef +char *des_options() + { + static int init=1; + static char buf[32]; + + if (init) + { + char *ptr,*unroll,*risc,*size; + + init=0; +#ifdef DES_PTR + ptr="ptr"; +#else + ptr="idx"; +#endif +#if defined(DES_RISC1) || defined(DES_RISC2) +#ifdef DES_RISC1 + risc="risc1"; +#endif +#ifdef DES_RISC2 + risc="risc2"; +#endif +#else + risc="cisc"; +#endif +#ifdef DES_UNROLL + unroll="16"; +#else + unroll="4"; +#endif + if (sizeof(DES_LONG) != sizeof(long)) + size="int"; + else + size="long"; + sprintf(buf,"des(%s,%s,%s,%s)",ptr,risc,unroll,size); + } + return(buf); + } +#endif + + +void des_ecb_encrypt(input, output, ks, enc) +des_cblock (*input); +des_cblock (*output); +des_key_schedule ks; +int enc; + { + register DES_LONG l; + register unsigned char *in,*out; + DES_LONG ll[2]; + + in=(unsigned char *)input; + out=(unsigned char *)output; + c2l(in,l); ll[0]=l; + c2l(in,l); ll[1]=l; + des_encrypt(ll,ks,enc); + l=ll[0]; l2c(l,out); + l=ll[1]; l2c(l,out); + l=ll[0]=ll[1]=0; + } + diff --git a/src/libcrypto/libdes/fcrypt.c b/src/libcrypto/libdes/fcrypt.c new file mode 100644 index 000000000..8b9d0495b --- /dev/null +++ b/src/libcrypto/libdes/fcrypt.c @@ -0,0 +1,152 @@ +/* NOCW */ + +/* This version of crypt has been developed from my MIT compatable + * DES library. + * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au + * Eric Young (eay@cryptsoft.com) + */ + +/* Modification by Jens Kupferschmidt (Cu) + * I have included directive PARA for shared memory computers. + * I have included a directive LONGCRYPT to using this routine to cipher + * passwords with more then 8 bytes like HP-UX 10.x it used. The MAXPLEN + * definition is the maximum of lenght of password and can changed. I have + * defined 24. + */ + +#include "des_locl.h" + +/* Added more values to handle illegal salt values the way normal + * crypt() implementations do. The patch was sent by + * Bjorn Gronvall + */ +static unsigned const char con_salt[128]={ +0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9, +0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1, +0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, +0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1, +0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9, +0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01, +0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, +0x0A,0x0B,0x05,0x06,0x07,0x08,0x09,0x0A, +0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12, +0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A, +0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22, +0x23,0x24,0x25,0x20,0x21,0x22,0x23,0x24, +0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C, +0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34, +0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C, +0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44, +}; + +static unsigned const char cov_2char[64]={ +0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35, +0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44, +0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C, +0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54, +0x55,0x56,0x57,0x58,0x59,0x5A,0x61,0x62, +0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A, +0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72, +0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A +}; + +#ifndef NOPROTO +void fcrypt_body(DES_LONG *out,des_key_schedule ks, + DES_LONG Eswap0, DES_LONG Eswap1); + +#ifdef PERL5 +char *des_crypt(const char *buf,const char *salt); +#else +char *crypt(const char *buf,const char *salt); +#endif +#else +void fcrypt_body(); +#ifdef PERL5 +char *des_crypt(); +#else +char *crypt(); +#endif +#endif + +#ifdef PERL5 +char *des_crypt(buf,salt) +#else +char *crypt(buf,salt) +#endif +const char *buf; +const char *salt; + { + static char buff[14]; + + return(des_fcrypt(buf,salt,buff)); + } + + +char *des_fcrypt(buf,salt,ret) +const char *buf; +const char *salt; +char *ret; + { + unsigned int i,j,x,y; + DES_LONG Eswap0,Eswap1; + DES_LONG out[2],ll; + des_cblock key; + des_key_schedule ks; + unsigned char bb[9]; + unsigned char *b=bb; + unsigned char c,u; + + /* eay 25/08/92 + * If you call crypt("pwd","*") as often happens when you + * have * as the pwd field in /etc/passwd, the function + * returns *\0XXXXXXXXX + * The \0 makes the string look like * so the pwd "*" would + * crypt to "*". This was found when replacing the crypt in + * our shared libraries. People found that the disbled + * accounts effectivly had no passwd :-(. */ + x=ret[0]=((salt[0] == '\0')?'A':salt[0]); + Eswap0=con_salt[x]<<2; + x=ret[1]=((salt[1] == '\0')?'A':salt[1]); + Eswap1=con_salt[x]<<6; + +/* EAY +r=strlen(buf); +r=(r+7)/8; +*/ + for (i=0; i<8; i++) + { + c= *(buf++); + if (!c) break; + key[i]=(c<<1); + } + for (; i<8; i++) + key[i]=0; + + des_set_key((des_cblock *)(key),ks); + fcrypt_body(&(out[0]),ks,Eswap0,Eswap1); + + ll=out[0]; l2c(ll,b); + ll=out[1]; l2c(ll,b); + y=0; + u=0x80; + bb[8]=0; + for (i=2; i<13; i++) + { + c=0; + for (j=0; j<6; j++) + { + c<<=1; + if (bb[y] & u) c|=1; + u>>=1; + if (!u) + { + y++; + u=0x80; + } + } + ret[i]=cov_2char[c]; + } + ret[13]='\0'; + return(ret); + } + diff --git a/src/libcrypto/libdes/fcrypt_b.c b/src/libcrypto/libdes/fcrypt_b.c new file mode 100644 index 000000000..5900645e7 --- /dev/null +++ b/src/libcrypto/libdes/fcrypt_b.c @@ -0,0 +1,148 @@ +/* crypto/des/fcrypt_b.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* #include */ + +/* This version of crypt has been developed from my MIT compatable + * DES library. + * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au + * Eric Young (eay@cryptsoft.com) + */ + +#define DES_FCRYPT +#include "des_locl.h" +#undef DES_FCRYPT + +#undef PERM_OP +#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + +#undef HPERM_OP +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n))))\ + +void fcrypt_body(out, ks, Eswap0, Eswap1) +DES_LONG *out; +des_key_schedule ks; +DES_LONG Eswap0; +DES_LONG Eswap1; + { + register DES_LONG l,r,t,u; +#ifdef DES_PTR + register unsigned char *des_SP=(unsigned char *)des_SPtrans; +#endif + register DES_LONG *s; + register int j; + register DES_LONG E0,E1; + + l=0; + r=0; + + s=(DES_LONG *)ks; + E0=Eswap0; + E1=Eswap1; + + for (j=0; j<25; j++) + { +#ifdef DES_UNROLL + register int i; + + for (i=0; i<32; i+=8) + { + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + D_ENCRYPT(l,r,i+4); /* 1 */ + D_ENCRYPT(r,l,i+6); /* 2 */ + } +#else + D_ENCRYPT(l,r, 0); /* 1 */ + D_ENCRYPT(r,l, 2); /* 2 */ + D_ENCRYPT(l,r, 4); /* 3 */ + D_ENCRYPT(r,l, 6); /* 4 */ + D_ENCRYPT(l,r, 8); /* 5 */ + D_ENCRYPT(r,l,10); /* 6 */ + D_ENCRYPT(l,r,12); /* 7 */ + D_ENCRYPT(r,l,14); /* 8 */ + D_ENCRYPT(l,r,16); /* 9 */ + D_ENCRYPT(r,l,18); /* 10 */ + D_ENCRYPT(l,r,20); /* 11 */ + D_ENCRYPT(r,l,22); /* 12 */ + D_ENCRYPT(l,r,24); /* 13 */ + D_ENCRYPT(r,l,26); /* 14 */ + D_ENCRYPT(l,r,28); /* 15 */ + D_ENCRYPT(r,l,30); /* 16 */ +#endif + + t=l; + l=r; + r=t; + } + l=ROTATE(l,3)&0xffffffffL; + r=ROTATE(r,3)&0xffffffffL; + + PERM_OP(l,r,t, 1,0x55555555L); + PERM_OP(r,l,t, 8,0x00ff00ffL); + PERM_OP(l,r,t, 2,0x33333333L); + PERM_OP(r,l,t,16,0x0000ffffL); + PERM_OP(l,r,t, 4,0x0f0f0f0fL); + + out[0]=r; + out[1]=l; + } + diff --git a/src/libcrypto/libdes/podd.h b/src/libcrypto/libdes/podd.h new file mode 100644 index 000000000..c00cd6ba0 --- /dev/null +++ b/src/libcrypto/libdes/podd.h @@ -0,0 +1,75 @@ +/* crypto/des/podd.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +static const unsigned char odd_parity[256]={ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, +112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, +128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, +145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, +161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, +176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, +193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, +208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, +224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, +241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254}; diff --git a/src/libcrypto/libdes/set_key.c b/src/libcrypto/libdes/set_key.c new file mode 100644 index 000000000..99ac27348 --- /dev/null +++ b/src/libcrypto/libdes/set_key.c @@ -0,0 +1,246 @@ +/* crypto/des/set_key.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* set_key.c v 1.4 eay 24/9/91 + * 1.4 Speed up by 400% :-) + * 1.3 added register declarations. + * 1.2 unrolled make_key_sched a bit more + * 1.1 added norm_expand_bits + * 1.0 First working version + */ +#include "des_locl.h" +#include "podd.h" +#include "sk.h" + +#ifndef NOPROTO +static int check_parity(des_cblock (*key)); +#else +static int check_parity(); +#endif + +int des_check_key=0; + +void des_set_odd_parity(key) +des_cblock (*key); + { + int i; + + for (i=0; i>(n))^(b))&(m)),\ + * (b)^=(t),\ + * (a)=((a)^((t)<<(n)))) + */ + +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n)))) + +/* return 0 if key parity is odd (correct), + * return -1 if key parity error, + * return -2 if illegal weak key. + */ +int des_set_key(key, schedule) +des_cblock (*key); +des_key_schedule schedule; + { + static int shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; + register DES_LONG c,d,t,s,t2; + register unsigned char *in; + register DES_LONG *k; + register int i; + + if (des_check_key) + { + if (!check_parity(key)) + return(-1); + + if (des_is_weak_key(key)) + return(-2); + } + + k=(DES_LONG *)schedule; + in=(unsigned char *)key; + + c2l(in,c); + c2l(in,d); + + /* do PC1 in 60 simple operations */ +/* PERM_OP(d,c,t,4,0x0f0f0f0fL); + HPERM_OP(c,t,-2, 0xcccc0000L); + HPERM_OP(c,t,-1, 0xaaaa0000L); + HPERM_OP(c,t, 8, 0x00ff0000L); + HPERM_OP(c,t,-1, 0xaaaa0000L); + HPERM_OP(d,t,-8, 0xff000000L); + HPERM_OP(d,t, 8, 0x00ff0000L); + HPERM_OP(d,t, 2, 0x33330000L); + d=((d&0x00aa00aaL)<<7L)|((d&0x55005500L)>>7L)|(d&0xaa55aa55L); + d=(d>>8)|((c&0xf0000000L)>>4); + c&=0x0fffffffL; */ + + /* I now do it in 47 simple operations :-) + * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) + * for the inspiration. :-) */ + PERM_OP (d,c,t,4,0x0f0f0f0fL); + HPERM_OP(c,t,-2,0xcccc0000L); + HPERM_OP(d,t,-2,0xcccc0000L); + PERM_OP (d,c,t,1,0x55555555L); + PERM_OP (c,d,t,8,0x00ff00ffL); + PERM_OP (d,c,t,1,0x55555555L); + d= (((d&0x000000ffL)<<16L)| (d&0x0000ff00L) | + ((d&0x00ff0000L)>>16L)|((c&0xf0000000L)>>4L)); + c&=0x0fffffffL; + + for (i=0; i>2L)|(c<<26L)); d=((d>>2L)|(d<<26L)); } + else + { c=((c>>1L)|(c<<27L)); d=((d>>1L)|(d<<27L)); } + c&=0x0fffffffL; + d&=0x0fffffffL; + /* could be a few less shifts but I am to lazy at this + * point in time to investigate */ + s= des_skb[0][ (c )&0x3f ]| + des_skb[1][((c>> 6)&0x03)|((c>> 7L)&0x3c)]| + des_skb[2][((c>>13)&0x0f)|((c>>14L)&0x30)]| + des_skb[3][((c>>20)&0x01)|((c>>21L)&0x06) | + ((c>>22L)&0x38)]; + t= des_skb[4][ (d )&0x3f ]| + des_skb[5][((d>> 7L)&0x03)|((d>> 8L)&0x3c)]| + des_skb[6][ (d>>15L)&0x3f ]| + des_skb[7][((d>>21L)&0x0f)|((d>>22L)&0x30)]; + + /* table contained 0213 4657 */ + t2=((t<<16L)|(s&0x0000ffffL))&0xffffffffL; + *(k++)=ROTATE(t2,30)&0xffffffffL; + + t2=((s>>16L)|(t&0xffff0000L)); + *(k++)=ROTATE(t2,26)&0xffffffffL; + } + return(0); + } + +int des_key_sched(key, schedule) +des_cblock (*key); +des_key_schedule schedule; + { + return(des_set_key(key,schedule)); + } diff --git a/src/libcrypto/libdes/sk.h b/src/libcrypto/libdes/sk.h new file mode 100644 index 000000000..240703070 --- /dev/null +++ b/src/libcrypto/libdes/sk.h @@ -0,0 +1,204 @@ +/* crypto/des/sk.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +static const DES_LONG des_skb[8][64]={ +{ +/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +0x00000000L,0x00000010L,0x20000000L,0x20000010L, +0x00010000L,0x00010010L,0x20010000L,0x20010010L, +0x00000800L,0x00000810L,0x20000800L,0x20000810L, +0x00010800L,0x00010810L,0x20010800L,0x20010810L, +0x00000020L,0x00000030L,0x20000020L,0x20000030L, +0x00010020L,0x00010030L,0x20010020L,0x20010030L, +0x00000820L,0x00000830L,0x20000820L,0x20000830L, +0x00010820L,0x00010830L,0x20010820L,0x20010830L, +0x00080000L,0x00080010L,0x20080000L,0x20080010L, +0x00090000L,0x00090010L,0x20090000L,0x20090010L, +0x00080800L,0x00080810L,0x20080800L,0x20080810L, +0x00090800L,0x00090810L,0x20090800L,0x20090810L, +0x00080020L,0x00080030L,0x20080020L,0x20080030L, +0x00090020L,0x00090030L,0x20090020L,0x20090030L, +0x00080820L,0x00080830L,0x20080820L,0x20080830L, +0x00090820L,0x00090830L,0x20090820L,0x20090830L, +},{ +/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ +0x00000000L,0x02000000L,0x00002000L,0x02002000L, +0x00200000L,0x02200000L,0x00202000L,0x02202000L, +0x00000004L,0x02000004L,0x00002004L,0x02002004L, +0x00200004L,0x02200004L,0x00202004L,0x02202004L, +0x00000400L,0x02000400L,0x00002400L,0x02002400L, +0x00200400L,0x02200400L,0x00202400L,0x02202400L, +0x00000404L,0x02000404L,0x00002404L,0x02002404L, +0x00200404L,0x02200404L,0x00202404L,0x02202404L, +0x10000000L,0x12000000L,0x10002000L,0x12002000L, +0x10200000L,0x12200000L,0x10202000L,0x12202000L, +0x10000004L,0x12000004L,0x10002004L,0x12002004L, +0x10200004L,0x12200004L,0x10202004L,0x12202004L, +0x10000400L,0x12000400L,0x10002400L,0x12002400L, +0x10200400L,0x12200400L,0x10202400L,0x12202400L, +0x10000404L,0x12000404L,0x10002404L,0x12002404L, +0x10200404L,0x12200404L,0x10202404L,0x12202404L, +},{ +/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ +0x00000000L,0x00000001L,0x00040000L,0x00040001L, +0x01000000L,0x01000001L,0x01040000L,0x01040001L, +0x00000002L,0x00000003L,0x00040002L,0x00040003L, +0x01000002L,0x01000003L,0x01040002L,0x01040003L, +0x00000200L,0x00000201L,0x00040200L,0x00040201L, +0x01000200L,0x01000201L,0x01040200L,0x01040201L, +0x00000202L,0x00000203L,0x00040202L,0x00040203L, +0x01000202L,0x01000203L,0x01040202L,0x01040203L, +0x08000000L,0x08000001L,0x08040000L,0x08040001L, +0x09000000L,0x09000001L,0x09040000L,0x09040001L, +0x08000002L,0x08000003L,0x08040002L,0x08040003L, +0x09000002L,0x09000003L,0x09040002L,0x09040003L, +0x08000200L,0x08000201L,0x08040200L,0x08040201L, +0x09000200L,0x09000201L,0x09040200L,0x09040201L, +0x08000202L,0x08000203L,0x08040202L,0x08040203L, +0x09000202L,0x09000203L,0x09040202L,0x09040203L, +},{ +/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ +0x00000000L,0x00100000L,0x00000100L,0x00100100L, +0x00000008L,0x00100008L,0x00000108L,0x00100108L, +0x00001000L,0x00101000L,0x00001100L,0x00101100L, +0x00001008L,0x00101008L,0x00001108L,0x00101108L, +0x04000000L,0x04100000L,0x04000100L,0x04100100L, +0x04000008L,0x04100008L,0x04000108L,0x04100108L, +0x04001000L,0x04101000L,0x04001100L,0x04101100L, +0x04001008L,0x04101008L,0x04001108L,0x04101108L, +0x00020000L,0x00120000L,0x00020100L,0x00120100L, +0x00020008L,0x00120008L,0x00020108L,0x00120108L, +0x00021000L,0x00121000L,0x00021100L,0x00121100L, +0x00021008L,0x00121008L,0x00021108L,0x00121108L, +0x04020000L,0x04120000L,0x04020100L,0x04120100L, +0x04020008L,0x04120008L,0x04020108L,0x04120108L, +0x04021000L,0x04121000L,0x04021100L,0x04121100L, +0x04021008L,0x04121008L,0x04021108L,0x04121108L, +},{ +/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ +0x00000000L,0x10000000L,0x00010000L,0x10010000L, +0x00000004L,0x10000004L,0x00010004L,0x10010004L, +0x20000000L,0x30000000L,0x20010000L,0x30010000L, +0x20000004L,0x30000004L,0x20010004L,0x30010004L, +0x00100000L,0x10100000L,0x00110000L,0x10110000L, +0x00100004L,0x10100004L,0x00110004L,0x10110004L, +0x20100000L,0x30100000L,0x20110000L,0x30110000L, +0x20100004L,0x30100004L,0x20110004L,0x30110004L, +0x00001000L,0x10001000L,0x00011000L,0x10011000L, +0x00001004L,0x10001004L,0x00011004L,0x10011004L, +0x20001000L,0x30001000L,0x20011000L,0x30011000L, +0x20001004L,0x30001004L,0x20011004L,0x30011004L, +0x00101000L,0x10101000L,0x00111000L,0x10111000L, +0x00101004L,0x10101004L,0x00111004L,0x10111004L, +0x20101000L,0x30101000L,0x20111000L,0x30111000L, +0x20101004L,0x30101004L,0x20111004L,0x30111004L, +},{ +/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ +0x00000000L,0x08000000L,0x00000008L,0x08000008L, +0x00000400L,0x08000400L,0x00000408L,0x08000408L, +0x00020000L,0x08020000L,0x00020008L,0x08020008L, +0x00020400L,0x08020400L,0x00020408L,0x08020408L, +0x00000001L,0x08000001L,0x00000009L,0x08000009L, +0x00000401L,0x08000401L,0x00000409L,0x08000409L, +0x00020001L,0x08020001L,0x00020009L,0x08020009L, +0x00020401L,0x08020401L,0x00020409L,0x08020409L, +0x02000000L,0x0A000000L,0x02000008L,0x0A000008L, +0x02000400L,0x0A000400L,0x02000408L,0x0A000408L, +0x02020000L,0x0A020000L,0x02020008L,0x0A020008L, +0x02020400L,0x0A020400L,0x02020408L,0x0A020408L, +0x02000001L,0x0A000001L,0x02000009L,0x0A000009L, +0x02000401L,0x0A000401L,0x02000409L,0x0A000409L, +0x02020001L,0x0A020001L,0x02020009L,0x0A020009L, +0x02020401L,0x0A020401L,0x02020409L,0x0A020409L, +},{ +/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ +0x00000000L,0x00000100L,0x00080000L,0x00080100L, +0x01000000L,0x01000100L,0x01080000L,0x01080100L, +0x00000010L,0x00000110L,0x00080010L,0x00080110L, +0x01000010L,0x01000110L,0x01080010L,0x01080110L, +0x00200000L,0x00200100L,0x00280000L,0x00280100L, +0x01200000L,0x01200100L,0x01280000L,0x01280100L, +0x00200010L,0x00200110L,0x00280010L,0x00280110L, +0x01200010L,0x01200110L,0x01280010L,0x01280110L, +0x00000200L,0x00000300L,0x00080200L,0x00080300L, +0x01000200L,0x01000300L,0x01080200L,0x01080300L, +0x00000210L,0x00000310L,0x00080210L,0x00080310L, +0x01000210L,0x01000310L,0x01080210L,0x01080310L, +0x00200200L,0x00200300L,0x00280200L,0x00280300L, +0x01200200L,0x01200300L,0x01280200L,0x01280300L, +0x00200210L,0x00200310L,0x00280210L,0x00280310L, +0x01200210L,0x01200310L,0x01280210L,0x01280310L, +},{ +/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ +0x00000000L,0x04000000L,0x00040000L,0x04040000L, +0x00000002L,0x04000002L,0x00040002L,0x04040002L, +0x00002000L,0x04002000L,0x00042000L,0x04042000L, +0x00002002L,0x04002002L,0x00042002L,0x04042002L, +0x00000020L,0x04000020L,0x00040020L,0x04040020L, +0x00000022L,0x04000022L,0x00040022L,0x04040022L, +0x00002020L,0x04002020L,0x00042020L,0x04042020L, +0x00002022L,0x04002022L,0x00042022L,0x04042022L, +0x00000800L,0x04000800L,0x00040800L,0x04040800L, +0x00000802L,0x04000802L,0x00040802L,0x04040802L, +0x00002800L,0x04002800L,0x00042800L,0x04042800L, +0x00002802L,0x04002802L,0x00042802L,0x04042802L, +0x00000820L,0x04000820L,0x00040820L,0x04040820L, +0x00000822L,0x04000822L,0x00040822L,0x04040822L, +0x00002820L,0x04002820L,0x00042820L,0x04042820L, +0x00002822L,0x04002822L,0x00042822L,0x04042822L, +}}; diff --git a/src/libcrypto/libdes/speed.c b/src/libcrypto/libdes/speed.c new file mode 100644 index 000000000..e3d753b2e --- /dev/null +++ b/src/libcrypto/libdes/speed.c @@ -0,0 +1,329 @@ +/* crypto/des/speed.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* 11-Sep-92 Andrew Daviel Support for Silicon Graphics IRIX added */ +/* 06-Apr-92 Luke Brennan Support for VMS and add extra signal calls */ + +#ifndef MSDOS +#define TIMES +#endif + +#include +#ifndef MSDOS +#include +#else +#include +extern int exit(); +#endif +#include +#ifndef VMS +#ifndef _IRIX +#include +#endif +#ifdef TIMES +#include +#include +#endif +#else /* VMS */ +#include +struct tms { + time_t tms_utime; + time_t tms_stime; + time_t tms_uchild; /* I dunno... */ + time_t tms_uchildsys; /* so these names are a guess :-) */ + } +#endif +#ifndef TIMES +#include +#endif + +#ifdef sun +#include +#include +#endif + +#include "des_locl.h" + +/* The following if from times(3) man page. It may need to be changed */ +#ifndef HZ +# ifndef CLK_TCK +# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */ +# ifndef VMS +# define HZ 100.0 +# else /* VMS */ +# define HZ 100.0 +# endif +# else /* _BSD_CLK_TCK_ */ +# define HZ ((double)_BSD_CLK_TCK_) +# endif +# else /* CLK_TCK */ +# define HZ ((double)CLK_TCK) +# endif +#endif + +#define BUFSIZE ((long)1024) +long run=0; + +#ifndef NOPROTO +double Time_F(int s); +#else +double Time_F(); +#endif + +#ifdef SIGALRM +#if defined(__STDC__) || defined(sgi) || defined(_AIX) +#define SIGRETTYPE void +#else +#define SIGRETTYPE int +#endif + +#ifndef NOPROTO +SIGRETTYPE sig_done(int sig); +#else +SIGRETTYPE sig_done(); +#endif + +SIGRETTYPE sig_done(sig) +int sig; + { + signal(SIGALRM,sig_done); + run=0; +#ifdef LINT + sig=sig; +#endif + } +#endif + +#define START 0 +#define STOP 1 + +double Time_F(s) +int s; + { + double ret; +#ifdef TIMES + static struct tms tstart,tend; + + if (s == START) + { + times(&tstart); + return(0); + } + else + { + times(&tend); + ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ; + return((ret == 0.0)?1e-6:ret); + } +#else /* !times() */ + static struct timeb tstart,tend; + long i; + + if (s == START) + { + ftime(&tstart); + return(0); + } + else + { + ftime(&tend); + i=(long)tend.millitm-(long)tstart.millitm; + ret=((double)(tend.time-tstart.time))+((double)i)/1e3; + return((ret == 0.0)?1e-6:ret); + } +#endif + } + +int main(argc,argv) +int argc; +char **argv; + { + long count; + static unsigned char buf[BUFSIZE]; + static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; + static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12}; + static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34}; + des_key_schedule sch,sch2,sch3; + double a,b,c,d,e; +#ifndef SIGALRM + long ca,cb,cc,cd,ce; +#endif + +#ifndef TIMES + printf("To get the most acurate results, try to run this\n"); + printf("program when this computer is idle.\n"); +#endif + + des_set_key((C_Block *)key2,sch2); + des_set_key((C_Block *)key3,sch3); + +#ifndef SIGALRM + printf("First we calculate the approximate speed ...\n"); + des_set_key((C_Block *)key,sch); + count=10; + do { + long i; + DES_LONG data[2]; + + count*=2; + Time_F(START); + for (i=count; i; i--) + des_encrypt(data,&(sch[0]),DES_ENCRYPT); + d=Time_F(STOP); + } while (d < 3.0); + ca=count; + cb=count*3; + cc=count*3*8/BUFSIZE+1; + cd=count*8/BUFSIZE+1; + ce=count/20+1; + printf("Doing set_key %ld times\n",ca); +#define COND(d) (count != (d)) +#define COUNT(d) (d) +#else +#define COND(c) (run) +#define COUNT(d) (count) + signal(SIGALRM,sig_done); + printf("Doing set_key for 10 seconds\n"); + alarm(10); +#endif + + Time_F(START); + for (count=0,run=1; COND(ca); count++) + des_set_key((C_Block *)key,sch); + d=Time_F(STOP); + printf("%ld set_key's in %.2f seconds\n",count,d); + a=((double)COUNT(ca))/d; + +#ifdef SIGALRM + printf("Doing des_encrypt's for 10 seconds\n"); + alarm(10); +#else + printf("Doing des_encrypt %ld times\n",cb); +#endif + Time_F(START); + for (count=0,run=1; COND(cb); count++) + { + DES_LONG data[2]; + + des_encrypt(data,&(sch[0]),DES_ENCRYPT); + } + d=Time_F(STOP); + printf("%ld des_encrypt's in %.2f second\n",count,d); + b=((double)COUNT(cb)*8)/d; + +#ifdef SIGALRM + printf("Doing des_cbc_encrypt on %ld byte blocks for 10 seconds\n", + BUFSIZE); + alarm(10); +#else + printf("Doing des_cbc_encrypt %ld times on %ld byte blocks\n",cc, + BUFSIZE); +#endif + Time_F(START); + for (count=0,run=1; COND(cc); count++) + des_ncbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE,&(sch[0]), + (C_Block *)&(key[0]),DES_ENCRYPT); + d=Time_F(STOP); + printf("%ld des_cbc_encrypt's of %ld byte blocks in %.2f second\n", + count,BUFSIZE,d); + c=((double)COUNT(cc)*BUFSIZE)/d; + +#ifdef SIGALRM + printf("Doing des_ede_cbc_encrypt on %ld byte blocks for 10 seconds\n", + BUFSIZE); + alarm(10); +#else + printf("Doing des_ede_cbc_encrypt %ld times on %ld byte blocks\n",cd, + BUFSIZE); +#endif + Time_F(START); + for (count=0,run=1; COND(cd); count++) + des_ede3_cbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE, + &(sch[0]), + &(sch2[0]), + &(sch3[0]), + (C_Block *)&(key[0]), + DES_ENCRYPT); + d=Time_F(STOP); + printf("%ld des_ede_cbc_encrypt's of %ld byte blocks in %.2f second\n", + count,BUFSIZE,d); + d=((double)COUNT(cd)*BUFSIZE)/d; + +#ifdef SIGALRM + printf("Doing crypt for 10 seconds\n"); + alarm(10); +#else + printf("Doing crypt %ld times\n",ce); +#endif + Time_F(START); + for (count=0,run=1; COND(ce); count++) + crypt("testing1","ef"); + e=Time_F(STOP); + printf("%ld crypts in %.2f second\n",count,e); + e=((double)COUNT(ce))/e; + + printf("set_key per sec = %12.2f (%9.3fuS)\n",a,1.0e6/a); + printf("DES raw ecb bytes per sec = %12.2f (%9.3fuS)\n",b,8.0e6/b); + printf("DES cbc bytes per sec = %12.2f (%9.3fuS)\n",c,8.0e6/c); + printf("DES ede cbc bytes per sec = %12.2f (%9.3fuS)\n",d,8.0e6/d); + printf("crypt per sec = %12.2f (%9.3fuS)\n",e,1.0e6/e); + exit(0); +#if defined(LINT) || defined(MSDOS) + return(0); +#endif + } diff --git a/src/libcrypto/libdes/spr.h b/src/libcrypto/libdes/spr.h new file mode 100644 index 000000000..a84d6a723 --- /dev/null +++ b/src/libcrypto/libdes/spr.h @@ -0,0 +1,204 @@ +/* crypto/des/spr.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +const DES_LONG des_SPtrans[8][64]={ +{ +/* nibble 0 */ +0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L, +0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L, +0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L, +0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L, +0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L, +0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L, +0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L, +0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L, +0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L, +0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L, +0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L, +0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L, +0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L, +0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L, +0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L, +0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L, +},{ +/* nibble 1 */ +0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L, +0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L, +0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L, +0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L, +0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L, +0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L, +0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L, +0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L, +0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L, +0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L, +0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L, +0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L, +0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L, +0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L, +0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L, +0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L, +},{ +/* nibble 2 */ +0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L, +0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L, +0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L, +0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L, +0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L, +0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L, +0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L, +0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L, +0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L, +0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L, +0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L, +0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L, +0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L, +0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L, +0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L, +0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L, +},{ +/* nibble 3 */ +0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L, +0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L, +0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L, +0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L, +0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L, +0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L, +0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L, +0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L, +0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L, +0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L, +0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L, +0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L, +0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L, +0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L, +0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L, +0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L, +},{ +/* nibble 4 */ +0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L, +0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L, +0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L, +0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L, +0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L, +0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L, +0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L, +0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L, +0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L, +0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L, +0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L, +0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L, +0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L, +0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L, +0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L, +0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L, +},{ +/* nibble 5 */ +0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L, +0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L, +0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L, +0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L, +0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L, +0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L, +0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L, +0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L, +0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L, +0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L, +0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L, +0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L, +0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L, +0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L, +0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L, +0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L, +},{ +/* nibble 6 */ +0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L, +0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L, +0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L, +0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L, +0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L, +0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L, +0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L, +0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L, +0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L, +0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L, +0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L, +0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L, +0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L, +0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L, +0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L, +0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L, +},{ +/* nibble 7 */ +0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L, +0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L, +0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L, +0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L, +0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L, +0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L, +0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L, +0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L, +0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L, +0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L, +0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L, +0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L, +0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L, +0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L, +0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L, +0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L, +}}; diff --git a/src/libcrypto/libserpent/serpent.c b/src/libcrypto/libserpent/serpent.c new file mode 100644 index 000000000..f2cea250e --- /dev/null +++ b/src/libcrypto/libserpent/serpent.c @@ -0,0 +1,995 @@ + +/* Optimized implementation of the Serpent AES candidate algorithm + * Designed by Anderson, Biham and Knudsen and Implemented by + * Gisle Sælensminde 2000. + * + * The implementation is based on the pentium optimised sboxes of + * Dag Arne Osvik. Even these sboxes are designed to be optimal for x86 + * processors they are efficient on other processors as well, but the speedup + * isn't so impressive compared to other implementations. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifdef __KERNEL__ +#include +#include + +#include +#else +#include +#include +#endif + +#include "serpent.h" + +#define rotl(reg, val) ((reg << val) | (reg >> (32 - val))) +#define rotr(reg, val) ((reg >> val) | (reg << (32 - val))) + +#ifdef __cpu_to_be32 +#define BLOCK_SWAP +#define io_swap(x) __cpu_to_be32(x) +#else +#undef BLOCK_SWAP +#endif + +/* The sbox functions. The first four parameters is the input bits, and + * the last is a tempoary. These parameters are also used for output, but + * the bit order is permuted. The output bit order from S0 is + * (1 4 2 0 3), where 3 is the (now useless) tempoary. + */ + +#define S0(r0,r1,r2,r3,r4) \ + r3 = r3 ^ r0; \ + r4 = r1; \ + r1 = r1 & r3; \ + r4 = r4 ^ r2; \ + r1 = r1 ^ r0; \ + r0 = r0 | r3; \ + r0 = r0 ^ r4; \ + r4 = r4 ^ r3; \ + r3 = r3 ^ r2; \ + r2 = r2 | r1; \ + r2 = r2 ^ r4; \ + r4 = -1 ^ r4; \ + r4 = r4 | r1; \ + r1 = r1 ^ r3; \ + r1 = r1 ^ r4; \ + r3 = r3 | r0; \ + r1 = r1 ^ r3; \ + r4 = r4 ^ r3; + +#define S1(r0,r1,r2,r3,r4) \ + r1 = -1 ^ r1; \ + r4 = r0; \ + r0 = r0 ^ r1; \ + r4 = r4 | r1; \ + r4 = r4 ^ r3; \ + r3 = r3 & r0; \ + r2 = r2 ^ r4; \ + r3 = r3 ^ r1; \ + r3 = r3 | r2; \ + r0 = r0 ^ r4; \ + r3 = r3 ^ r0; \ + r1 = r1 & r2; \ + r0 = r0 | r1; \ + r1 = r1 ^ r4; \ + r0 = r0 ^ r2; \ + r4 = r4 | r3; \ + r0 = r0 ^ r4; \ + r4 = -1 ^ r4; \ + r1 = r1 ^ r3; \ + r4 = r4 & r2; \ + r1 = -1 ^ r1; \ + r4 = r4 ^ r0; \ + r1 = r1 ^ r4; + +#define S2(r0,r1,r2,r3,r4) \ + r4 = r0; \ + r0 = r0 & r2; \ + r0 = r0 ^ r3; \ + r2 = r2 ^ r1; \ + r2 = r2 ^ r0; \ + r3 = r3 | r4; \ + r3 = r3 ^ r1; \ + r4 = r4 ^ r2; \ + r1 = r3; \ + r3 = r3 | r4; \ + r3 = r3 ^ r0; \ + r0 = r0 & r1; \ + r4 = r4 ^ r0; \ + r1 = r1 ^ r3; \ + r1 = r1 ^ r4; \ + r4 = -1 ^ r4; + +#define S3(r0,r1,r2,r3,r4) \ + r4 = r0 ; \ + r0 = r0 | r3; \ + r3 = r3 ^ r1; \ + r1 = r1 & r4; \ + r4 = r4 ^ r2; \ + r2 = r2 ^ r3; \ + r3 = r3 & r0; \ + r4 = r4 | r1; \ + r3 = r3 ^ r4; \ + r0 = r0 ^ r1; \ + r4 = r4 & r0; \ + r1 = r1 ^ r3; \ + r4 = r4 ^ r2; \ + r1 = r1 | r0; \ + r1 = r1 ^ r2; \ + r0 = r0 ^ r3; \ + r2 = r1; \ + r1 = r1 | r3; \ + r1 = r1 ^ r0; + +#define S4(r0,r1,r2,r3,r4) \ + r1 = r1 ^ r3; \ + r3 = -1 ^ r3; \ + r2 = r2 ^ r3; \ + r3 = r3 ^ r0; \ + r4 = r1; \ + r1 = r1 & r3; \ + r1 = r1 ^ r2; \ + r4 = r4 ^ r3; \ + r0 = r0 ^ r4; \ + r2 = r2 & r4; \ + r2 = r2 ^ r0; \ + r0 = r0 & r1; \ + r3 = r3 ^ r0; \ + r4 = r4 | r1; \ + r4 = r4 ^ r0; \ + r0 = r0 | r3; \ + r0 = r0 ^ r2; \ + r2 = r2 & r3; \ + r0 = -1 ^ r0; \ + r4 = r4 ^ r2; + +#define S5(r0,r1,r2,r3,r4) \ + r0 = r0 ^ r1; \ + r1 = r1 ^ r3; \ + r3 = -1 ^ r3; \ + r4 = r1; \ + r1 = r1 & r0; \ + r2 = r2 ^ r3; \ + r1 = r1 ^ r2; \ + r2 = r2 | r4; \ + r4 = r4 ^ r3; \ + r3 = r3 & r1; \ + r3 = r3 ^ r0; \ + r4 = r4 ^ r1; \ + r4 = r4 ^ r2; \ + r2 = r2 ^ r0; \ + r0 = r0 & r3; \ + r2 = -1 ^ r2; \ + r0 = r0 ^ r4; \ + r4 = r4 | r3; \ + r2 = r2 ^ r4; + +#define S6(r0,r1,r2,r3,r4) \ + r2 = -1 ^ r2; \ + r4 = r3; \ + r3 = r3 & r0; \ + r0 = r0 ^ r4; \ + r3 = r3 ^ r2; \ + r2 = r2 | r4; \ + r1 = r1 ^ r3; \ + r2 = r2 ^ r0; \ + r0 = r0 | r1; \ + r2 = r2 ^ r1; \ + r4 = r4 ^ r0; \ + r0 = r0 | r3; \ + r0 = r0 ^ r2; \ + r4 = r4 ^ r3; \ + r4 = r4 ^ r0; \ + r3 = -1 ^ r3; \ + r2 = r2 & r4; \ + r2 = r2 ^ r3; + +#define S7(r0,r1,r2,r3,r4) \ + r4 = r2; \ + r2 = r2 & r1; \ + r2 = r2 ^ r3; \ + r3 = r3 & r1; \ + r4 = r4 ^ r2; \ + r2 = r2 ^ r1; \ + r1 = r1 ^ r0; \ + r0 = r0 | r4; \ + r0 = r0 ^ r2; \ + r3 = r3 ^ r1; \ + r2 = r2 ^ r3; \ + r3 = r3 & r0; \ + r3 = r3 ^ r4; \ + r4 = r4 ^ r2; \ + r2 = r2 & r0; \ + r4 = -1 ^ r4; \ + r2 = r2 ^ r4; \ + r4 = r4 & r0; \ + r1 = r1 ^ r3; \ + r4 = r4 ^ r1; + +/* The inverse sboxes */ + +#define I0(r0,r1,r2,r3,r4) \ + r2 = r2 ^ -1; \ + r4 = r1; \ + r1 = r1 | r0; \ + r4 = r4 ^ -1; \ + r1 = r1 ^ r2; \ + r2 = r2 | r4; \ + r1 = r1 ^ r3; \ + r0 = r0 ^ r4; \ + r2 = r2 ^ r0; \ + r0 = r0 & r3; \ + r4 = r4 ^ r0; \ + r0 = r0 | r1; \ + r0 = r0 ^ r2; \ + r3 = r3 ^ r4; \ + r2 = r2 ^ r1; \ + r3 = r3 ^ r0; \ + r3 = r3 ^ r1; \ + r2 = r2 & r3; \ + r4 = r4 ^ r2; + +#define I1(r0,r1,r2,r3,r4) \ + r4 = r1; \ + r1 = r1 ^ r3; \ + r3 = r3 & r1; \ + r4 = r4 ^ r2; \ + r3 = r3 ^ r0; \ + r0 = r0 | r1; \ + r2 = r2 ^ r3; \ + r0 = r0 ^ r4; \ + r0 = r0 | r2; \ + r1 = r1 ^ r3; \ + r0 = r0 ^ r1; \ + r1 = r1 | r3; \ + r1 = r1 ^ r0; \ + r4 = r4 ^ -1; \ + r4 = r4 ^ r1; \ + r1 = r1 | r0; \ + r1 = r1 ^ r0; \ + r1 = r1 | r4; \ + r3 = r3 ^ r1; + +#define I2(r0,r1,r2,r3,r4) \ + r2 = r2 ^ r3; \ + r3 = r3 ^ r0; \ + r4 = r3; \ + r3 = r3 & r2; \ + r3 = r3 ^ r1; \ + r1 = r1 | r2; \ + r1 = r1 ^ r4; \ + r4 = r4 & r3; \ + r2 = r2 ^ r3; \ + r4 = r4 & r0; \ + r4 = r4 ^ r2; \ + r2 = r2 & r1; \ + r2 = r2 | r0; \ + r3 = r3 ^ -1; \ + r2 = r2 ^ r3; \ + r0 = r0 ^ r3; \ + r0 = r0 & r1; \ + r3 = r3 ^ r4; \ + r3 = r3 ^ r0; + +#define I3(r0,r1,r2,r3,r4) \ + r4 = r2; \ + r2 = r2 ^ r1; \ + r0 = r0 ^ r2; \ + r4 = r4 & r2; \ + r4 = r4 ^ r0; \ + r0 = r0 & r1; \ + r1 = r1 ^ r3; \ + r3 = r3 | r4; \ + r2 = r2 ^ r3; \ + r0 = r0 ^ r3; \ + r1 = r1 ^ r4; \ + r3 = r3 & r2; \ + r3 = r3 ^ r1; \ + r1 = r1 ^ r0; \ + r1 = r1 | r2; \ + r0 = r0 ^ r3; \ + r1 = r1 ^ r4; \ + r0 = r0 ^ r1; + +#define I4(r0,r1,r2,r3,r4) \ + r4 = r2; \ + r2 = r2 & r3; \ + r2 = r2 ^ r1; \ + r1 = r1 | r3; \ + r1 = r1 & r0; \ + r4 = r4 ^ r2; \ + r4 = r4 ^ r1; \ + r1 = r1 & r2; \ + r0 = r0 ^ -1; \ + r3 = r3 ^ r4; \ + r1 = r1 ^ r3; \ + r3 = r3 & r0; \ + r3 = r3 ^ r2; \ + r0 = r0 ^ r1; \ + r2 = r2 & r0; \ + r3 = r3 ^ r0; \ + r2 = r2 ^ r4; \ + r2 = r2 | r3; \ + r3 = r3 ^ r0; \ + r2 = r2 ^ r1; + +#define I5(r0,r1,r2,r3,r4) \ + r1 = r1 ^ -1; \ + r4 = r3; \ + r2 = r2 ^ r1; \ + r3 = r3 | r0; \ + r3 = r3 ^ r2; \ + r2 = r2 | r1; \ + r2 = r2 & r0; \ + r4 = r4 ^ r3; \ + r2 = r2 ^ r4; \ + r4 = r4 | r0; \ + r4 = r4 ^ r1; \ + r1 = r1 & r2; \ + r1 = r1 ^ r3; \ + r4 = r4 ^ r2; \ + r3 = r3 & r4; \ + r4 = r4 ^ r1; \ + r3 = r3 ^ r0; \ + r3 = r3 ^ r4; \ + r4 = r4 ^ -1; + + +#define I6(r0,r1,r2,r3,r4) \ + r0 = r0 ^ r2; \ + r4 = r2; \ + r2 = r2 & r0; \ + r4 = r4 ^ r3; \ + r2 = r2 ^ -1; \ + r3 = r3 ^ r1; \ + r2 = r2 ^ r3; \ + r4 = r4 | r0; \ + r0 = r0 ^ r2; \ + r3 = r3 ^ r4; \ + r4 = r4 ^ r1; \ + r1 = r1 & r3; \ + r1 = r1 ^ r0; \ + r0 = r0 ^ r3; \ + r0 = r0 | r2; \ + r3 = r3 ^ r1; \ + r4 = r4 ^ r0; + +#define I7(r0,r1,r2,r3,r4) \ + r4 = r2; \ + r2 = r2 ^ r0; \ + r0 = r0 & r3; \ + r4 = r4 | r3; \ + r2 = r2 ^ -1; \ + r3 = r3 ^ r1; \ + r1 = r1 | r0; \ + r0 = r0 ^ r2; \ + r2 = r2 & r4; \ + r3 = r3 & r4; \ + r1 = r1 ^ r2; \ + r2 = r2 ^ r0; \ + r0 = r0 | r2; \ + r4 = r4 ^ r1; \ + r0 = r0 ^ r3; \ + r3 = r3 ^ r4; \ + r4 = r4 | r0; \ + r3 = r3 ^ r2; \ + r4 = r4 ^ r2; + +/* forward and inverse linear transformations */ + +#define LINTRANS(r0,r1,r2,r3,r4) \ + r0 = rotl(r0, 13); \ + r2 = rotl(r2, 3); \ + r3 = r3 ^ r2; \ + r4 = r0 << 3; \ + r1 = r1 ^ r0; \ + r3 = r3 ^ r4; \ + r1 = r1 ^ r2; \ + r3 = rotl(r3, 7); \ + r1 = rotl(r1, 1); \ + r2 = r2 ^ r3; \ + r4 = r1 << 7; \ + r0 = r0 ^ r1; \ + r2 = r2 ^ r4; \ + r0 = r0 ^ r3; \ + r2 = rotl(r2, 22); \ + r0 = rotl(r0, 5); + +#define ILINTRANS(r0,r1,r2,r3,r4) \ + r2 = rotr(r2, 22); \ + r0 = rotr(r0, 5); \ + r2 = r2 ^ r3; \ + r4 = r1 << 7; \ + r0 = r0 ^ r1; \ + r2 = r2 ^ r4; \ + r0 = r0 ^ r3; \ + r3 = rotr(r3, 7); \ + r1 = rotr(r1, 1); \ + r3 = r3 ^ r2; \ + r4 = r0 << 3; \ + r1 = r1 ^ r0; \ + r3 = r3 ^ r4; \ + r1 = r1 ^ r2; \ + r2 = rotr(r2, 3); \ + r0 = rotr(r0, 13); + + +#define KEYMIX(r0,r1,r2,r3,r4,IN) \ + r0 = r0 ^ l_key[IN+8]; \ + r1 = r1 ^ l_key[IN+9]; \ + r2 = r2 ^ l_key[IN+10]; \ + r3 = r3 ^ l_key[IN+11]; + +#define GETKEY(r0, r1, r2, r3, IN) \ + r0 = l_key[IN+8]; \ + r1 = l_key[IN+9]; \ + r2 = l_key[IN+10]; \ + r3 = l_key[IN+11]; + +#define SETKEY(r0, r1, r2, r3, IN) \ + l_key[IN+8] = r0; \ + l_key[IN+9] = r1; \ + l_key[IN+10] = r2; \ + l_key[IN+11] = r3; + +/* initialise the key schedule from the user supplied key */ + +int serpent_set_key(serpent_context *cx, const unsigned char *key, int key_len) +{ const u32 *in_key = (const u32 *)key; + /* l_key - storage for the key schedule */ + u32 *l_key = cx->keyinfo; + u32 i,lk,r0,r1,r2,r3,r4; + + if (key_len != 16 && key_len != 24 && key_len != 32) + return -1; /* unsupported key length */ + + key_len *= 8; + + i = 0; lk = (key_len + 31) / 32; + + while(i < lk) + { +#ifdef BLOCK_SWAP + l_key[i] = io_swap(in_key[lk - i - 1]); +#else + l_key[i] = in_key[i]; +#endif + i++; + } + + if (key_len < 256) + { + while(i < 8) + + l_key[i++] = 0; + + i = key_len / 32; lk = 1 << key_len % 32; + + l_key[i] &= lk - 1; + l_key[i] |= lk; + } + + for(i = 0; i < 132; ++i) + { + lk = l_key[i] ^ l_key[i + 3] ^ l_key[i + 5] + ^ l_key[i + 7] ^ 0x9e3779b9 ^ i; + + l_key[i + 8] = (lk << 11) | (lk >> 21); + } + + GETKEY(r0, r1, r2, r3, 0); + S3(r0,r1,r2,r3,r4); + SETKEY(r1, r2, r3, r4, 0) + + GETKEY(r0, r1, r2, r3, 4); + S2(r0,r1,r2,r3,r4); + SETKEY(r2, r3, r1, r4, 4) + + GETKEY(r0, r1, r2, r3, 8); + S1(r0,r1,r2,r3,r4); + SETKEY(r3, r1, r2, r0, 8) + + GETKEY(r0, r1, r2, r3, 12); + S0(r0,r1,r2,r3,r4); + SETKEY(r1, r4, r2, r0, 12) + + GETKEY(r0, r1, r2, r3, 16); + S7(r0,r1,r2,r3,r4); + SETKEY(r2, r4, r3, r0, 16) + + GETKEY(r0, r1, r2, r3, 20); + S6(r0,r1,r2,r3,r4) + SETKEY(r0, r1, r4, r2, 20) + + GETKEY(r0, r1, r2, r3, 24); + S5(r0,r1,r2,r3,r4); + SETKEY(r1, r3, r0, r2, 24) + + GETKEY(r0, r1, r2, r3, 28); + S4(r0,r1,r2,r3,r4) + SETKEY(r1, r4, r0, r3, 28) + + GETKEY(r0, r1, r2, r3, 32); + S3(r0,r1,r2,r3,r4); + SETKEY(r1, r2, r3, r4, 32) + + GETKEY(r0, r1, r2, r3, 36); + S2(r0,r1,r2,r3,r4); + SETKEY(r2, r3, r1, r4, 36) + + GETKEY(r0, r1, r2, r3, 40); + S1(r0,r1,r2,r3,r4); + SETKEY(r3, r1, r2, r0, 40) + + GETKEY(r0, r1, r2, r3, 44); + S0(r0,r1,r2,r3,r4); + SETKEY(r1, r4, r2, r0, 44) + + GETKEY(r0, r1, r2, r3, 48); + S7(r0,r1,r2,r3,r4); + SETKEY(r2, r4, r3, r0, 48) + + GETKEY(r0, r1, r2, r3, 52); + S6(r0,r1,r2,r3,r4) + SETKEY(r0, r1, r4, r2, 52) + + GETKEY(r0, r1, r2, r3, 56); + S5(r0,r1,r2,r3,r4); + SETKEY(r1, r3, r0, r2, 56) + + GETKEY(r0, r1, r2, r3, 60); + S4(r0,r1,r2,r3,r4) + SETKEY(r1, r4, r0, r3, 60) + + GETKEY(r0, r1, r2, r3, 64); + S3(r0,r1,r2,r3,r4); + SETKEY(r1, r2, r3, r4, 64) + + GETKEY(r0, r1, r2, r3, 68); + S2(r0,r1,r2,r3,r4); + SETKEY(r2, r3, r1, r4, 68) + + GETKEY(r0, r1, r2, r3, 72); + S1(r0,r1,r2,r3,r4); + SETKEY(r3, r1, r2, r0, 72) + + GETKEY(r0, r1, r2, r3, 76); + S0(r0,r1,r2,r3,r4); + SETKEY(r1, r4, r2, r0, 76) + + GETKEY(r0, r1, r2, r3, 80); + S7(r0,r1,r2,r3,r4); + SETKEY(r2, r4, r3, r0, 80) + + GETKEY(r0, r1, r2, r3, 84); + S6(r0,r1,r2,r3,r4) + SETKEY(r0, r1, r4, r2, 84) + + GETKEY(r0, r1, r2, r3, 88); + S5(r0,r1,r2,r3,r4); + SETKEY(r1, r3, r0, r2, 88) + + GETKEY(r0, r1, r2, r3, 92); + S4(r0,r1,r2,r3,r4) + SETKEY(r1, r4, r0, r3, 92) + + GETKEY(r0, r1, r2, r3, 96); + S3(r0,r1,r2,r3,r4); + SETKEY(r1, r2, r3, r4, 96) + + GETKEY(r0, r1, r2, r3, 100); + S2(r0,r1,r2,r3,r4); + SETKEY(r2, r3, r1, r4, 100) + + GETKEY(r0, r1, r2, r3, 104); + S1(r0,r1,r2,r3,r4); + SETKEY(r3, r1, r2, r0, 104) + + GETKEY(r0, r1, r2, r3, 108); + S0(r0,r1,r2,r3,r4); + SETKEY(r1, r4, r2, r0, 108) + + GETKEY(r0, r1, r2, r3, 112); + S7(r0,r1,r2,r3,r4); + SETKEY(r2, r4, r3, r0, 112) + + GETKEY(r0, r1, r2, r3, 116); + S6(r0,r1,r2,r3,r4) + SETKEY(r0, r1, r4, r2, 116) + + GETKEY(r0, r1, r2, r3, 120); + S5(r0,r1,r2,r3,r4); + SETKEY(r1, r3, r0, r2, 120) + + GETKEY(r0, r1, r2, r3, 124); + S4(r0,r1,r2,r3,r4) + SETKEY(r1, r4, r0, r3, 124) + + GETKEY(r0, r1, r2, r3, 128); + S3(r0,r1,r2,r3,r4); + SETKEY(r1, r2, r3, r4, 128) + + return 0; +}; + +/* Encryption and decryption functions. The rounds are fully inlined. + * The sboxes alters the bit order of the output, and the altered + * bit ordrer is used progressivly. */ + +/* encrypt a block of text */ + +int serpent_encrypt(serpent_context *cx, const u8 *in, + u8 *out) +{ u32 *l_key = cx->keyinfo; + const u32 *in_blk = (const u32 *) in; + u32 *out_blk = (u32 *) out; + u32 r0,r1,r2,r3,r4; + +#ifdef BLOCK_SWAP + r0 = io_swap(in_blk[3]); r1 = io_swap(in_blk[2]); + r2 = io_swap(in_blk[1]); r3 = io_swap(in_blk[0]); +#else + r0 = in_blk[0]; r1 = in_blk[1]; r2 = in_blk[2]; r3 = in_blk[3]; +#endif + + /* round 1 */ + KEYMIX(r0,r1,r2,r3,r4,0); + S0(r0,r1,r2,r3,r4); + LINTRANS(r1,r4,r2,r0,r3); + + /* round 2 */ + KEYMIX(r1,r4,r2,r0,r3,4); + S1(r1,r4,r2,r0,r3); + LINTRANS(r0,r4,r2,r1,r3); + + /* round 3 */ + KEYMIX(r0,r4,r2,r1,r3,8); + S2(r0,r4,r2,r1,r3); + LINTRANS(r2,r1,r4,r3,r0); + + /* round 4 */ + KEYMIX(r2,r1,r4,r3,r0,12); + S3(r2,r1,r4,r3,r0); + LINTRANS(r1,r4,r3,r0,r2); + + /* round 5 */ + KEYMIX(r1,r4,r3,r0,r2,16); + S4(r1,r4,r3,r0,r2) + LINTRANS(r4,r2,r1,r0,r3); + + /* round 6 */ + KEYMIX(r4,r2,r1,r0,r3,20); + S5(r4,r2,r1,r0,r3); + LINTRANS(r2,r0,r4,r1,r3); + + /* round 7 */ + KEYMIX(r2,r0,r4,r1,r3,24); + S6(r2,r0,r4,r1,r3) + LINTRANS(r2,r0,r3,r4,r1); + + /* round 8 */ + KEYMIX(r2,r0,r3,r4,r1,28); + S7(r2,r0,r3,r4,r1); + LINTRANS(r3,r1,r4,r2,r0); + + /* round 9 */ + KEYMIX(r3,r1,r4,r2,r0,32); + S0(r3,r1,r4,r2,r0); + LINTRANS(r1,r0,r4,r3,r2); + + /* round 10 */ + KEYMIX(r1,r0,r4,r3,r2,36); + S1(r1,r0,r4,r3,r2); + LINTRANS(r3,r0,r4,r1,r2); + + /* round 11 */ + KEYMIX(r3,r0,r4,r1,r2,40); + S2(r3,r0,r4,r1,r2); + LINTRANS(r4,r1,r0,r2,r3); + + /* round 12 */ + KEYMIX(r4,r1,r0,r2,r3,44); + S3(r4,r1,r0,r2,r3); + LINTRANS(r1,r0,r2,r3,r4); + + /* round 13 */ + KEYMIX(r1,r0,r2,r3,r4,48); + S4(r1,r0,r2,r3,r4) + LINTRANS(r0,r4,r1,r3,r2); + + /* round 14 */ + KEYMIX(r0,r4,r1,r3,r2,52); + S5(r0,r4,r1,r3,r2); + LINTRANS(r4,r3,r0,r1,r2); + + /* round 15 */ + KEYMIX(r4,r3,r0,r1,r2,56); + S6(r4,r3,r0,r1,r2) + LINTRANS(r4,r3,r2,r0,r1); + + /* round 16 */ + KEYMIX(r4,r3,r2,r0,r1,60); + S7(r4,r3,r2,r0,r1); + LINTRANS(r2,r1,r0,r4,r3); + + /* round 17 */ + KEYMIX(r2,r1,r0,r4,r3,64); + S0(r2,r1,r0,r4,r3); + LINTRANS(r1,r3,r0,r2,r4); + + /* round 18 */ + KEYMIX(r1,r3,r0,r2,r4,68); + S1(r1,r3,r0,r2,r4); + LINTRANS(r2,r3,r0,r1,r4); + + /* round 19 */ + KEYMIX(r2,r3,r0,r1,r4,72); + S2(r2,r3,r0,r1,r4); + LINTRANS(r0,r1,r3,r4,r2); + + /* round 20 */ + KEYMIX(r0,r1,r3,r4,r2,76); + S3(r0,r1,r3,r4,r2); + LINTRANS(r1,r3,r4,r2,r0); + + /* round 21 */ + KEYMIX(r1,r3,r4,r2,r0,80); + S4(r1,r3,r4,r2,r0) + LINTRANS(r3,r0,r1,r2,r4); + + /* round 22 */ + KEYMIX(r3,r0,r1,r2,r4,84); + S5(r3,r0,r1,r2,r4); + LINTRANS(r0,r2,r3,r1,r4); + + /* round 23 */ + KEYMIX(r0,r2,r3,r1,r4,88); + S6(r0,r2,r3,r1,r4) + LINTRANS(r0,r2,r4,r3,r1); + + /* round 24 */ + KEYMIX(r0,r2,r4,r3,r1,92); + S7(r0,r2,r4,r3,r1); + LINTRANS(r4,r1,r3,r0,r2); + + /* round 25 */ + KEYMIX(r4,r1,r3,r0,r2,96); + S0(r4,r1,r3,r0,r2); + LINTRANS(r1,r2,r3,r4,r0); + + /* round 26 */ + KEYMIX(r1,r2,r3,r4,r0,100); + S1(r1,r2,r3,r4,r0); + LINTRANS(r4,r2,r3,r1,r0); + + /* round 27 */ + KEYMIX(r4,r2,r3,r1,r0,104); + S2(r4,r2,r3,r1,r0); + LINTRANS(r3,r1,r2,r0,r4); + + /* round 28 */ + KEYMIX(r3,r1,r2,r0,r4,108); + S3(r3,r1,r2,r0,r4); + LINTRANS(r1,r2,r0,r4,r3); + + /* round 29 */ + KEYMIX(r1,r2,r0,r4,r3,112); + S4(r1,r2,r0,r4,r3) + LINTRANS(r2,r3,r1,r4,r0); + + /* round 30 */ + KEYMIX(r2,r3,r1,r4,r0,116); + S5(r2,r3,r1,r4,r0); + LINTRANS(r3,r4,r2,r1,r0); + + /* round 31 */ + KEYMIX(r3,r4,r2,r1,r0,120); + S6(r3,r4,r2,r1,r0) + LINTRANS(r3,r4,r0,r2,r1); + + /* round 32 */ + KEYMIX(r3,r4,r0,r2,r1,124); + S7(r3,r4,r0,r2,r1); + KEYMIX(r0,r1,r2,r3,r4,128); + + +#ifdef BLOCK_SWAP + out_blk[3] = io_swap(r0); out_blk[2] = io_swap(r1); + out_blk[1] = io_swap(r2); out_blk[0] = io_swap(r3); +#else + out_blk[0] = r0; out_blk[1] = r1; out_blk[2] = r2; out_blk[3] = r3; +#endif + return 0; +}; + +/* decrypt a block of text */ + +int serpent_decrypt(serpent_context *cx, const u8 *in, + u8 *out) +{ u32 *l_key = cx->keyinfo; + const u32 *in_blk = (const u32 *)in; + u32 *out_blk = (u32 *)out; + u32 r0,r1,r2,r3,r4; + +#ifdef BLOCK_SWAP + r0 = io_swap(in_blk[3]); r1 = io_swap(in_blk[2]); + r2 = io_swap(in_blk[1]); r3 = io_swap(in_blk[0]); +#else + r0 = in_blk[0]; r1 = in_blk[1]; r2 = in_blk[2]; r3 = in_blk[3]; +#endif + + /* round 1 */ + KEYMIX(r0,r1,r2,r3,r4,128); + I7(r0,r1,r2,r3,r4); + KEYMIX(r3,r0,r1,r4,r2,124); + + /* round 2 */ + ILINTRANS(r3,r0,r1,r4,r2); + I6(r3,r0,r1,r4,r2); + KEYMIX(r0,r1,r2,r4,r3,120); + + /* round 3 */ + ILINTRANS(r0,r1,r2,r4,r3); + I5(r0,r1,r2,r4,r3); + KEYMIX(r1,r3,r4,r2,r0,116); + + /* round 4 */ + ILINTRANS(r1,r3,r4,r2,r0); + I4(r1,r3,r4,r2,r0); + KEYMIX(r1,r2,r4,r0,r3,112); + + /* round 5 */ + ILINTRANS(r1,r2,r4,r0,r3); + I3(r1,r2,r4,r0,r3); + KEYMIX(r4,r2,r0,r1,r3,108); + + /* round 6 */ + ILINTRANS(r4,r2,r0,r1,r3); + I2(r4,r2,r0,r1,r3); + KEYMIX(r2,r3,r0,r1,r4,104); + + /* round 7 */ + ILINTRANS(r2,r3,r0,r1,r4); + I1(r2,r3,r0,r1,r4); + KEYMIX(r4,r2,r1,r0,r3,100); + + /* round 8 */ + ILINTRANS(r4,r2,r1,r0,r3); + I0(r4,r2,r1,r0,r3); + KEYMIX(r4,r3,r2,r0,r1,96); + + /* round 9 */ + ILINTRANS(r4,r3,r2,r0,r1); + I7(r4,r3,r2,r0,r1); + KEYMIX(r0,r4,r3,r1,r2,92); + + /* round 10 */ + ILINTRANS(r0,r4,r3,r1,r2); + I6(r0,r4,r3,r1,r2); + KEYMIX(r4,r3,r2,r1,r0,88); + + /* round 11 */ + ILINTRANS(r4,r3,r2,r1,r0); + I5(r4,r3,r2,r1,r0); + KEYMIX(r3,r0,r1,r2,r4,84); + + /* round 12 */ + ILINTRANS(r3,r0,r1,r2,r4); + I4(r3,r0,r1,r2,r4); + KEYMIX(r3,r2,r1,r4,r0,80); + + /* round 13 */ + ILINTRANS(r3,r2,r1,r4,r0); + I3(r3,r2,r1,r4,r0); + KEYMIX(r1,r2,r4,r3,r0,76); + + /* round 14 */ + ILINTRANS(r1,r2,r4,r3,r0); + I2(r1,r2,r4,r3,r0); + KEYMIX(r2,r0,r4,r3,r1,72); + + /* round 15 */ + ILINTRANS(r2,r0,r4,r3,r1); + I1(r2,r0,r4,r3,r1); + KEYMIX(r1,r2,r3,r4,r0,68); + + /* round 16 */ + ILINTRANS(r1,r2,r3,r4,r0); + I0(r1,r2,r3,r4,r0); + KEYMIX(r1,r0,r2,r4,r3,64); + + /* round 17 */ + ILINTRANS(r1,r0,r2,r4,r3); + I7(r1,r0,r2,r4,r3); + KEYMIX(r4,r1,r0,r3,r2,60); + + /* round 18 */ + ILINTRANS(r4,r1,r0,r3,r2); + I6(r4,r1,r0,r3,r2); + KEYMIX(r1,r0,r2,r3,r4,56); + + /* round 19 */ + ILINTRANS(r1,r0,r2,r3,r4); + I5(r1,r0,r2,r3,r4); + KEYMIX(r0,r4,r3,r2,r1,52); + + /* round 20 */ + ILINTRANS(r0,r4,r3,r2,r1); + I4(r0,r4,r3,r2,r1); + KEYMIX(r0,r2,r3,r1,r4,48); + + /* round 21 */ + ILINTRANS(r0,r2,r3,r1,r4); + I3(r0,r2,r3,r1,r4); + KEYMIX(r3,r2,r1,r0,r4,44); + + /* round 22 */ + ILINTRANS(r3,r2,r1,r0,r4); + I2(r3,r2,r1,r0,r4); + KEYMIX(r2,r4,r1,r0,r3,40); + + /* round 23 */ + ILINTRANS(r2,r4,r1,r0,r3); + I1(r2,r4,r1,r0,r3); + KEYMIX(r3,r2,r0,r1,r4,36); + + /* round 24 */ + ILINTRANS(r3,r2,r0,r1,r4); + I0(r3,r2,r0,r1,r4); + KEYMIX(r3,r4,r2,r1,r0,32); + + /* round 25 */ + ILINTRANS(r3,r4,r2,r1,r0); + I7(r3,r4,r2,r1,r0); + KEYMIX(r1,r3,r4,r0,r2,28); + + /* round 26 */ + ILINTRANS(r1,r3,r4,r0,r2); + I6(r1,r3,r4,r0,r2); + KEYMIX(r3,r4,r2,r0,r1,24); + + /* round 27 */ + ILINTRANS(r3,r4,r2,r0,r1); + I5(r3,r4,r2,r0,r1); + KEYMIX(r4,r1,r0,r2,r3,20); + + /* round 28 */ + ILINTRANS(r4,r1,r0,r2,r3); + I4(r4,r1,r0,r2,r3); + KEYMIX(r4,r2,r0,r3,r1,16); + + /* round 29 */ + ILINTRANS(r4,r2,r0,r3,r1); + I3(r4,r2,r0,r3,r1); + KEYMIX(r0,r2,r3,r4,r1,12); + + /* round 30 */ + ILINTRANS(r0,r2,r3,r4,r1); + I2(r0,r2,r3,r4,r1); + KEYMIX(r2,r1,r3,r4,r0,8); + + /* round 31 */ + ILINTRANS(r2,r1,r3,r4,r0); + I1(r2,r1,r3,r4,r0); + KEYMIX(r0,r2,r4,r3,r1,4); + + /* round 32 */ + ILINTRANS(r0,r2,r4,r3,r1); + I0(r0,r2,r4,r3,r1); + KEYMIX(r0,r1,r2,r3,r4,0); + +#ifdef BLOCK_SWAP + out_blk[3] = io_swap(r0); out_blk[2] = io_swap(r1); + out_blk[1] = io_swap(r2); out_blk[0] = io_swap(r3); +#else + out_blk[0] = r0; out_blk[1] = r1; out_blk[2] = r2; out_blk[3] = r3; +#endif + return 0; +}; + + diff --git a/src/libcrypto/libserpent/serpent.h b/src/libcrypto/libserpent/serpent.h new file mode 100644 index 000000000..6357f5bfa --- /dev/null +++ b/src/libcrypto/libserpent/serpent.h @@ -0,0 +1,17 @@ +#ifndef SERPENT_H +#define SERPENT_H +#ifdef __KERNEL__ +#include +#else +#include +#define u32 u_int32_t +#define u8 u_int8_t +#endif +struct serpent_context { + u32 keyinfo[140]; /* storage for the key schedule */ +}; +typedef struct serpent_context serpent_context; +int serpent_set_key(serpent_context *ctx, const u8 * in_key, int key_len); +int serpent_decrypt(serpent_context *ctx, const u8 * in_blk, u8 * out_blk); +int serpent_encrypt(serpent_context *ctx, const u8 * in_blk, u8 * out_blk); +#endif /* SERPENT_H */ diff --git a/src/libcrypto/libserpent/serpent_cbc.c b/src/libcrypto/libserpent/serpent_cbc.c new file mode 100644 index 000000000..3b546278a --- /dev/null +++ b/src/libcrypto/libserpent/serpent_cbc.c @@ -0,0 +1,8 @@ +#ifdef __KERNEL__ +#include +#else +#include +#endif +#include "serpent_cbc.h" +#include "cbc_generic.h" +CBC_IMPL_BLK16(serpent_cbc_encrypt, serpent_context, u_int8_t *, serpent_encrypt, serpent_decrypt); diff --git a/src/libcrypto/libserpent/serpent_cbc.h b/src/libcrypto/libserpent/serpent_cbc.h new file mode 100644 index 000000000..3064fa3bc --- /dev/null +++ b/src/libcrypto/libserpent/serpent_cbc.h @@ -0,0 +1,3 @@ +/* Glue header */ +#include "serpent.h" +int serpent_cbc_encrypt(serpent_context *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt); diff --git a/src/libcrypto/libsha2/hmac_sha2.c b/src/libcrypto/libsha2/hmac_sha2.c new file mode 100644 index 000000000..ad107eb62 --- /dev/null +++ b/src/libcrypto/libsha2/hmac_sha2.c @@ -0,0 +1,32 @@ +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif +#include "hmac_generic.h" +#include "sha2.h" +#include "hmac_sha2.h" + +void inline sha256_result(sha256_context *ctx, u_int8_t * hash, int hashlen) { + sha256_final(ctx); + memcpy(hash, &ctx->sha_out[0], hashlen); +} +void inline sha512_result(sha512_context *ctx, u_int8_t * hash, int hashlen) { + sha512_final(ctx); + memcpy(hash, &ctx->sha_out[0], hashlen); +} +HMAC_SET_KEY_IMPL (sha256_hmac_set_key, + sha256_hmac_context, SHA256_BLOCKSIZE, + sha256_init, sha256_write) +HMAC_HASH_IMPL (sha256_hmac_hash, + sha256_hmac_context, sha256_context, SHA256_HASHLEN, + sha256_write, sha256_result) + +HMAC_SET_KEY_IMPL (sha512_hmac_set_key, + sha512_hmac_context, SHA512_BLOCKSIZE, + sha512_init, sha512_write) +HMAC_HASH_IMPL (sha512_hmac_hash, + sha512_hmac_context, sha512_context, SHA512_HASHLEN, + sha512_write, sha512_result) diff --git a/src/libcrypto/libsha2/hmac_sha2.h b/src/libcrypto/libsha2/hmac_sha2.h new file mode 100644 index 000000000..b7f8c747c --- /dev/null +++ b/src/libcrypto/libsha2/hmac_sha2.h @@ -0,0 +1,17 @@ +typedef struct { + sha256_context ictx,octx; +} sha256_hmac_context; +typedef struct { + sha512_context ictx,octx; +} sha512_hmac_context; +#define SHA256_BLOCKSIZE 64 +#define SHA256_HASHLEN 32 +#define SHA384_BLOCKSIZE 128 /* XXX ok? */ +#define SHA384_HASHLEN 48 +#define SHA512_BLOCKSIZE 128 +#define SHA512_HASHLEN 64 + +void sha256_hmac_hash(sha256_hmac_context *hctx, const u_int8_t * dat, int len, u_int8_t * hash, int hashlen); +void sha256_hmac_set_key(sha256_hmac_context *hctx, const u_int8_t * key, int keylen); +void sha512_hmac_hash(sha512_hmac_context *hctx, const u_int8_t * dat, int len, u_int8_t * hash, int hashlen); +void sha512_hmac_set_key(sha512_hmac_context *hctx, const u_int8_t * key, int keylen); diff --git a/src/libcrypto/libsha2/sha2.c b/src/libcrypto/libsha2/sha2.c new file mode 100644 index 000000000..4debdad67 --- /dev/null +++ b/src/libcrypto/libsha2/sha2.c @@ -0,0 +1,437 @@ +/* + * sha512.c + * + * Written by Jari Ruusu, April 16 2001 + * + * Copyright 2001 by Jari Ruusu. + * Redistribution of this file is permitted under the GNU Public License. + */ + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif +#include "sha2.h" + +/* Define one or more of these. If none is defined, you get all of them */ +#if !defined(SHA256_NEEDED)&&!defined(SHA512_NEEDED)&&!defined(SHA384_NEEDED) +# define SHA256_NEEDED 1 +# define SHA512_NEEDED 1 +# define SHA384_NEEDED 1 +#endif + +#if defined(SHA256_NEEDED) +static const u_int32_t sha256_hashInit[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19 +}; +static const u_int32_t sha256_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; +#endif + +#if defined(SHA512_NEEDED) +static const u_int64_t sha512_hashInit[8] = { + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; +#endif + +#if defined(SHA384_NEEDED) +static const u_int64_t sha384_hashInit[8] = { + 0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL +}; +#endif + +#if defined(SHA512_NEEDED) || defined(SHA384_NEEDED) +static const u_int64_t sha512_K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; +#endif + +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define R(x,y) ((y) >> (x)) + +#if defined(SHA256_NEEDED) +void sha256_init(sha256_context *ctx) +{ + memcpy(&ctx->sha_H[0], &sha256_hashInit[0], sizeof(ctx->sha_H)); + ctx->sha_blocks = 0; + ctx->sha_bufCnt = 0; +} + +#define S(x,y) (((y) >> (x)) | ((y) << (32 - (x)))) +#define uSig0(x) ((S(2,(x))) ^ (S(13,(x))) ^ (S(22,(x)))) +#define uSig1(x) ((S(6,(x))) ^ (S(11,(x))) ^ (S(25,(x)))) +#define lSig0(x) ((S(7,(x))) ^ (S(18,(x))) ^ (R(3,(x)))) +#define lSig1(x) ((S(17,(x))) ^ (S(19,(x))) ^ (R(10,(x)))) + +static void sha256_transform(sha256_context *ctx, const unsigned char *datap) +{ + register int j; + u_int32_t a, b, c, d, e, f, g, h; + u_int32_t T1, T2, W[64], Wm2, Wm15; + + /* read the data, big endian byte order */ + j = 0; + do { + W[j] = (((u_int32_t)(datap[0]))<<24) | (((u_int32_t)(datap[1]))<<16) | + (((u_int32_t)(datap[2]))<<8 ) | ((u_int32_t)(datap[3])); + datap += 4; + } while(++j < 16); + + /* initialize variables a...h */ + a = ctx->sha_H[0]; + b = ctx->sha_H[1]; + c = ctx->sha_H[2]; + d = ctx->sha_H[3]; + e = ctx->sha_H[4]; + f = ctx->sha_H[5]; + g = ctx->sha_H[6]; + h = ctx->sha_H[7]; + + /* apply compression function */ + j = 0; + do { + if(j >= 16) { + Wm2 = W[j - 2]; + Wm15 = W[j - 15]; + W[j] = lSig1(Wm2) + W[j - 7] + lSig0(Wm15) + W[j - 16]; + } + T1 = h + uSig1(e) + Ch(e,f,g) + sha256_K[j] + W[j]; + T2 = uSig0(a) + Maj(a,b,c); + h = g; g = f; f = e; + e = d + T1; + d = c; c = b; b = a; + a = T1 + T2; + } while(++j < 64); + + /* compute intermediate hash value */ + ctx->sha_H[0] += a; + ctx->sha_H[1] += b; + ctx->sha_H[2] += c; + ctx->sha_H[3] += d; + ctx->sha_H[4] += e; + ctx->sha_H[5] += f; + ctx->sha_H[6] += g; + ctx->sha_H[7] += h; + + ctx->sha_blocks++; +} + +void sha256_write(sha256_context *ctx, const unsigned char *datap, int length) +{ + while(length > 0) { + if(!ctx->sha_bufCnt) { + while(length >= sizeof(ctx->sha_out)) { + sha256_transform(ctx, datap); + datap += sizeof(ctx->sha_out); + length -= sizeof(ctx->sha_out); + } + if(!length) return; + } + ctx->sha_out[ctx->sha_bufCnt] = *datap++; + length--; + if(++ctx->sha_bufCnt == sizeof(ctx->sha_out)) { + sha256_transform(ctx, &ctx->sha_out[0]); + ctx->sha_bufCnt = 0; + } + } +} + +void sha256_final(sha256_context *ctx) +{ + register int j; + u_int64_t bitLength; + u_int32_t i; + unsigned char padByte, *datap; + + bitLength = (ctx->sha_blocks << 9) | (ctx->sha_bufCnt << 3); + padByte = 0x80; + sha256_write(ctx, &padByte, 1); + + /* pad extra space with zeroes */ + padByte = 0; + while(ctx->sha_bufCnt != 56) { + sha256_write(ctx, &padByte, 1); + } + + /* write bit length, big endian byte order */ + ctx->sha_out[56] = bitLength >> 56; + ctx->sha_out[57] = bitLength >> 48; + ctx->sha_out[58] = bitLength >> 40; + ctx->sha_out[59] = bitLength >> 32; + ctx->sha_out[60] = bitLength >> 24; + ctx->sha_out[61] = bitLength >> 16; + ctx->sha_out[62] = bitLength >> 8; + ctx->sha_out[63] = bitLength; + sha256_transform(ctx, &ctx->sha_out[0]); + + /* return results in ctx->sha_out[0...31] */ + datap = &ctx->sha_out[0]; + j = 0; + do { + i = ctx->sha_H[j]; + datap[0] = i >> 24; + datap[1] = i >> 16; + datap[2] = i >> 8; + datap[3] = i; + datap += 4; + } while(++j < 8); + + /* clear sensitive information */ + memset(&ctx->sha_out[32], 0, sizeof(sha256_context) - 32); +} + +void sha256_hash_buffer(unsigned char *ib, int ile, unsigned char *ob, int ole) +{ + sha256_context ctx; + + if(ole < 1) return; + memset(ob, 0, ole); + if(ole > 32) ole = 32; + sha256_init(&ctx); + sha256_write(&ctx, ib, ile); + sha256_final(&ctx); + memcpy(ob, &ctx.sha_out[0], ole); + memset(&ctx, 0, sizeof(ctx)); +} + +#endif + +#if defined(SHA512_NEEDED) +void sha512_init(sha512_context *ctx) +{ + memcpy(&ctx->sha_H[0], &sha512_hashInit[0], sizeof(ctx->sha_H)); + ctx->sha_blocks = 0; + ctx->sha_blocksMSB = 0; + ctx->sha_bufCnt = 0; +} +#endif + +#if defined(SHA512_NEEDED) || defined(SHA384_NEEDED) +#undef S +#undef uSig0 +#undef uSig1 +#undef lSig0 +#undef lSig1 +#define S(x,y) (((y) >> (x)) | ((y) << (64 - (x)))) +#define uSig0(x) ((S(28,(x))) ^ (S(34,(x))) ^ (S(39,(x)))) +#define uSig1(x) ((S(14,(x))) ^ (S(18,(x))) ^ (S(41,(x)))) +#define lSig0(x) ((S(1,(x))) ^ (S(8,(x))) ^ (R(7,(x)))) +#define lSig1(x) ((S(19,(x))) ^ (S(61,(x))) ^ (R(6,(x)))) + +static void sha512_transform(sha512_context *ctx, const unsigned char *datap) +{ + register int j; + u_int64_t a, b, c, d, e, f, g, h; + u_int64_t T1, T2, W[80], Wm2, Wm15; + + /* read the data, big endian byte order */ + j = 0; + do { + W[j] = (((u_int64_t)(datap[0]))<<56) | (((u_int64_t)(datap[1]))<<48) | + (((u_int64_t)(datap[2]))<<40) | (((u_int64_t)(datap[3]))<<32) | + (((u_int64_t)(datap[4]))<<24) | (((u_int64_t)(datap[5]))<<16) | + (((u_int64_t)(datap[6]))<<8 ) | ((u_int64_t)(datap[7])); + datap += 8; + } while(++j < 16); + + /* initialize variables a...h */ + a = ctx->sha_H[0]; + b = ctx->sha_H[1]; + c = ctx->sha_H[2]; + d = ctx->sha_H[3]; + e = ctx->sha_H[4]; + f = ctx->sha_H[5]; + g = ctx->sha_H[6]; + h = ctx->sha_H[7]; + + /* apply compression function */ + j = 0; + do { + if(j >= 16) { + Wm2 = W[j - 2]; + Wm15 = W[j - 15]; + W[j] = lSig1(Wm2) + W[j - 7] + lSig0(Wm15) + W[j - 16]; + } + T1 = h + uSig1(e) + Ch(e,f,g) + sha512_K[j] + W[j]; + T2 = uSig0(a) + Maj(a,b,c); + h = g; g = f; f = e; + e = d + T1; + d = c; c = b; b = a; + a = T1 + T2; + } while(++j < 80); + + /* compute intermediate hash value */ + ctx->sha_H[0] += a; + ctx->sha_H[1] += b; + ctx->sha_H[2] += c; + ctx->sha_H[3] += d; + ctx->sha_H[4] += e; + ctx->sha_H[5] += f; + ctx->sha_H[6] += g; + ctx->sha_H[7] += h; + + ctx->sha_blocks++; + if(!ctx->sha_blocks) ctx->sha_blocksMSB++; +} + +void sha512_write(sha512_context *ctx, const unsigned char *datap, int length) +{ + while(length > 0) { + if(!ctx->sha_bufCnt) { + while(length >= sizeof(ctx->sha_out)) { + sha512_transform(ctx, datap); + datap += sizeof(ctx->sha_out); + length -= sizeof(ctx->sha_out); + } + if(!length) return; + } + ctx->sha_out[ctx->sha_bufCnt] = *datap++; + length--; + if(++ctx->sha_bufCnt == sizeof(ctx->sha_out)) { + sha512_transform(ctx, &ctx->sha_out[0]); + ctx->sha_bufCnt = 0; + } + } +} + +void sha512_final(sha512_context *ctx) +{ + register int j; + u_int64_t bitLength, bitLengthMSB; + u_int64_t i; + unsigned char padByte, *datap; + + bitLength = (ctx->sha_blocks << 10) | (ctx->sha_bufCnt << 3); + bitLengthMSB = (ctx->sha_blocksMSB << 10) | (ctx->sha_blocks >> 54); + padByte = 0x80; + sha512_write(ctx, &padByte, 1); + + /* pad extra space with zeroes */ + padByte = 0; + while(ctx->sha_bufCnt != 112) { + sha512_write(ctx, &padByte, 1); + } + + /* write bit length, big endian byte order */ + ctx->sha_out[112] = bitLengthMSB >> 56; + ctx->sha_out[113] = bitLengthMSB >> 48; + ctx->sha_out[114] = bitLengthMSB >> 40; + ctx->sha_out[115] = bitLengthMSB >> 32; + ctx->sha_out[116] = bitLengthMSB >> 24; + ctx->sha_out[117] = bitLengthMSB >> 16; + ctx->sha_out[118] = bitLengthMSB >> 8; + ctx->sha_out[119] = bitLengthMSB; + ctx->sha_out[120] = bitLength >> 56; + ctx->sha_out[121] = bitLength >> 48; + ctx->sha_out[122] = bitLength >> 40; + ctx->sha_out[123] = bitLength >> 32; + ctx->sha_out[124] = bitLength >> 24; + ctx->sha_out[125] = bitLength >> 16; + ctx->sha_out[126] = bitLength >> 8; + ctx->sha_out[127] = bitLength; + sha512_transform(ctx, &ctx->sha_out[0]); + + /* return results in ctx->sha_out[0...63] */ + datap = &ctx->sha_out[0]; + j = 0; + do { + i = ctx->sha_H[j]; + datap[0] = i >> 56; + datap[1] = i >> 48; + datap[2] = i >> 40; + datap[3] = i >> 32; + datap[4] = i >> 24; + datap[5] = i >> 16; + datap[6] = i >> 8; + datap[7] = i; + datap += 8; + } while(++j < 8); + + /* clear sensitive information */ + memset(&ctx->sha_out[64], 0, sizeof(sha512_context) - 64); +} + +void sha512_hash_buffer(unsigned char *ib, int ile, unsigned char *ob, int ole) +{ + sha512_context ctx; + + if(ole < 1) return; + memset(ob, 0, ole); + if(ole > 64) ole = 64; + sha512_init(&ctx); + sha512_write(&ctx, ib, ile); + sha512_final(&ctx); + memcpy(ob, &ctx.sha_out[0], ole); + memset(&ctx, 0, sizeof(ctx)); +} +#endif + +#if defined(SHA384_NEEDED) +void sha384_init(sha512_context *ctx) +{ + memcpy(&ctx->sha_H[0], &sha384_hashInit[0], sizeof(ctx->sha_H)); + ctx->sha_blocks = 0; + ctx->sha_blocksMSB = 0; + ctx->sha_bufCnt = 0; +} + +void sha384_hash_buffer(unsigned char *ib, int ile, unsigned char *ob, int ole) +{ + sha512_context ctx; + + if(ole < 1) return; + memset(ob, 0, ole); + if(ole > 48) ole = 48; + sha384_init(&ctx); + sha512_write(&ctx, ib, ile); + sha512_final(&ctx); + memcpy(ob, &ctx.sha_out[0], ole); + memset(&ctx, 0, sizeof(ctx)); +} +#endif diff --git a/src/libcrypto/libsha2/sha2.h b/src/libcrypto/libsha2/sha2.h new file mode 100644 index 000000000..2dc03cfa8 --- /dev/null +++ b/src/libcrypto/libsha2/sha2.h @@ -0,0 +1,52 @@ +#ifndef _SHA2_H +#define _SHA2_H +/* + * sha512.h + * + * Written by Jari Ruusu, April 16 2001 + * + * Copyright 2001 by Jari Ruusu. + * Redistribution of this file is permitted under the GNU Public License. + */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +typedef struct { + unsigned char sha_out[64]; /* results are here, bytes 0...31 */ + u_int32_t sha_H[8]; + u_int64_t sha_blocks; + int sha_bufCnt; +} sha256_context; + +typedef struct { + unsigned char sha_out[128]; /* results are here, bytes 0...63 */ + u_int64_t sha_H[8]; + u_int64_t sha_blocks; + u_int64_t sha_blocksMSB; + int sha_bufCnt; +} sha512_context; + +/* no sha384_context, use sha512_context */ + +/* 256 bit hash, provides 128 bits of security against collision attacks */ +extern void sha256_init(sha256_context *); +extern void sha256_write(sha256_context *, const unsigned char *, int); +extern void sha256_final(sha256_context *); +extern void sha256_hash_buffer(unsigned char *, int, unsigned char *, int); + +/* 512 bit hash, provides 256 bits of security against collision attacks */ +extern void sha512_init(sha512_context *); +extern void sha512_write(sha512_context *, const unsigned char *, int); +extern void sha512_final(sha512_context *); +extern void sha512_hash_buffer(unsigned char *, int, unsigned char *, int); + +/* 384 bit hash, provides 192 bits of security against collision attacks */ +extern void sha384_init(sha512_context *); +/* no sha384_write(), use sha512_write() */ +/* no sha384_final(), use sha512_final(), result in ctx->sha_out[0...47] */ +extern void sha384_hash_buffer(unsigned char *, int, unsigned char *, int); +#endif /* _SHA2_H */ diff --git a/src/libcrypto/libtwofish/twofish.c b/src/libcrypto/libtwofish/twofish.c new file mode 100644 index 000000000..0e01a92d2 --- /dev/null +++ b/src/libcrypto/libtwofish/twofish.c @@ -0,0 +1,861 @@ +/* NOTE: This implementation has been changed from the original + * source. See ChangeLog for more information. + * Maintained by Marc Mutz + */ + +/* Twofish for GPG + * By Matthew Skala , July 26, 1998 + * 256-bit key length added March 20, 1999 + * Some modifications to reduce the text size by Werner Koch, April, 1998 + * + * The original author has disclaimed all copyright interest in this + * code and thus putting it in the public domain. + * + * This code is a "clean room" implementation, written from the paper + * _Twofish: A 128-Bit Block Cipher_ by Bruce Schneier, John Kelsey, + * Doug Whiting, David Wagner, Chris Hall, and Niels Ferguson, available + * through http://www.counterpane.com/twofish.html + * + * For background information on multiplication in finite fields, used for + * the matrix operations in the key schedule, see the book _Contemporary + * Abstract Algebra_ by Joseph A. Gallian, especially chapter 22 in the + * Third Edition. + * + * Only the 128- and 256-bit key sizes are supported. This code is intended + * for GNU C on a 32-bit system, but it should work almost anywhere. Loops + * are unrolled, precomputation tables are used, etc., for maximum speed at + * some cost in memory consumption. */ + +#ifdef __KERNEL__ +#include +#include +#else +#include +#define u8 u_int8_t +#define u32 u_int32_t +#endif + +#if 0 /* shouldn't this be #ifdef rotl32 ? + * Look at wordops.h: It includes asm/wordops.h. + * Anyway, we have to search in the macros for rot's, + * since they seem to be defined in a generic way. */ +#define rotl rotl32 +#define rotr rotr32 +#else +#define rotl generic_rotl32 +#define rotr generic_rotr32 +#endif + +#include "twofish.h" +/* The large precomputed tables for the Twofish cipher (twofish.c) + * Taken from the same source as twofish.c + * Marc Mutz + */ + +/* These two tables are the q0 and q1 permutations, exactly as described in + * the Twofish paper. */ + +static const u8 q0[256] = { + 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, + 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, + 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30, + 0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, + 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE, + 0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, + 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45, + 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, + 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, + 0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, + 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED, + 0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, + 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B, + 0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, + 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F, + 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, + 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, + 0x55, 0x1F, 0x8A, 0x7D, 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, + 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, + 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, + 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, + 0x4A, 0x5E, 0xC1, 0xE0 +}; + +static const u8 q1[256] = { + 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, + 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, + 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B, + 0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, + 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54, + 0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, + 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7, + 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, + 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, + 0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, + 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D, + 0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, + 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21, + 0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, + 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E, + 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, + 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, + 0xE0, 0x4D, 0x43, 0x69, 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, + 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B, + 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, + 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, + 0x55, 0x09, 0xBE, 0x91 +}; + +/* These MDS tables are actually tables of MDS composed with q0 and q1, + * because it is only ever used that way and we can save some time by + * precomputing. Of course the main saving comes from precomputing the + * GF(2^8) multiplication involved in the MDS matrix multiply; by looking + * things up in these tables we reduce the matrix multiply to four lookups + * and three XORs. Semi-formally, the definition of these tables is: + * mds[0][i] = MDS (q1[i] 0 0 0)^T mds[1][i] = MDS (0 q0[i] 0 0)^T + * mds[2][i] = MDS (0 0 q1[i] 0)^T mds[3][i] = MDS (0 0 0 q0[i])^T + * where ^T means "transpose", the matrix multiply is performed in GF(2^8) + * represented as GF(2)[x]/v(x) where v(x)=x^8+x^6+x^5+x^3+1 as described + * by Schneier et al, and I'm casually glossing over the byte/word + * conversion issues. */ + +static const u32 mds[4][256] = { + {0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, + 0xE2E22BFB, 0x9E9EFAC8, 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, + 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, 0x3C3C57D6, 0x93938A32, + 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, + 0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, + 0xB0B0B306, 0x7575DE3F, 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, + 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, 0xAEAE2C6D, 0x7F7FABC1, + 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, + 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, + 0x3131272C, 0x808065A3, 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, + 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, 0x2A2A3638, 0xC4C49CB0, + 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, + 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, + 0x6767C027, 0xE9E9AF8C, 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, + 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, 0x29294CCA, 0xF0F035E3, + 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, + 0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, + 0xC8C81DC3, 0x9999FFCC, 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, + 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, 0xB5B53D79, 0x09090F0C, + 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9, + 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, + 0xEDEDD07A, 0x4343FC17, 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, + 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, 0x5656E70B, 0xE3E3DA72, + 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, + 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, + 0x8181942A, 0x91910149, 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, + 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, 0x7878AEC5, 0xC5C56D39, + 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, + 0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, + 0x55559DF9, 0x7E7E5A48, 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, + 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, 0x0606F48D, 0x404086E5, + 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, + 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, + 0x2D2D333C, 0x3030D6A5, 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, + 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, 0xD9D97929, 0x8686912E, + 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, + 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, + 0xC1C112CF, 0x8585EBDC, 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, + 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, 0xABABA212, 0x6F6F3EA2, + 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, + 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, + 0x04047FF6, 0x272746C2, 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, + 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91}, + + {0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, + 0xA3658080, 0x76DFE4E4, 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, + 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, 0x0D54E6E6, 0xC6432020, + 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, + 0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, + 0x94B1FBFB, 0x485A7E7E, 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, + 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, 0x1945FDFD, 0x5BA33A3A, + 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757, + 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, + 0x9B53AAAA, 0x7C635D5D, 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, + 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, 0xC0F09090, 0x8CAFE9E9, + 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, + 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, + 0xB499C3C3, 0xF1975B5B, 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, + 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, 0xCCFF9999, 0x95EA1414, + 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, + 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, + 0xBF7E9595, 0xBA207D7D, 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, + 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, 0x81FB0F0F, 0x793DB5B5, + 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282, + 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, + 0x86135050, 0xE730F7F7, 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, + 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, 0x410B9F9F, 0x7B8B0202, + 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, + 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, + 0xB1C72B2B, 0xAB6F8E8E, 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, + 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, 0x91EF1313, 0x85FE0808, + 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, + 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, + 0x6929A9A9, 0x647D4F4F, 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, + 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, 0xAC87D1D1, 0x7F8E0505, + 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, + 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, + 0x4C5F7979, 0x02B6B7B7, 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, + 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, 0x57AC3333, 0xC718CFCF, + 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, + 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, + 0x99E51D1D, 0x34392323, 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, + 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, 0xC8FA9E9E, 0xA882D6D6, + 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, + 0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, + 0x0FE25151, 0x00000000, 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, + 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8}, + + {0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, + 0xE2FBE22B, 0x9EC89EFA, 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, + 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, 0x3CD63C57, 0x9332938A, + 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, + 0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, + 0xB006B0B3, 0x753F75DE, 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, + 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, 0xAE6DAE2C, 0x7FC17FAB, + 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, + 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, + 0x312C3127, 0x80A38065, 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, + 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, 0x2A382A36, 0xC4B0C49C, + 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, + 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, + 0x672767C0, 0xE98CE9AF, 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, + 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, 0x29CA294C, 0xF0E3F035, + 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, + 0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, + 0xC8C3C81D, 0x99CC99FF, 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, + 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, 0xB579B53D, 0x090C090F, + 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD, + 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, + 0xED7AEDD0, 0x431743FC, 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, + 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, 0x560B56E7, 0xE372E3DA, + 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, + 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, + 0x812A8194, 0x91499101, 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, + 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, 0x78C578AE, 0xC539C56D, + 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, + 0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, + 0x55F9559D, 0x7E487E5A, 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, + 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, 0x068D06F4, 0x40E54086, + 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D, + 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, + 0x2D3C2D33, 0x30A530D6, 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, + 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, 0xD929D979, 0x862E8691, + 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, + 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, + 0xC1CFC112, 0x85DC85EB, 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, + 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, 0xAB12ABA2, 0x6FA26F3E, + 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, + 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, + 0x04F6047F, 0x27C22746, 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, + 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF}, + + {0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, + 0x6580A365, 0xDFE476DF, 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, + 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, 0x54E60D54, 0x4320C643, + 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, + 0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, + 0xB1FB94B1, 0x5A7E485A, 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, + 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, 0x45FD1945, 0xA33A5BA3, + 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216, + 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, + 0x53AA9B53, 0x635D7C63, 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, + 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, 0xF090C0F0, 0xAFE98CAF, + 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, + 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, + 0x99C3B499, 0x975BF197, 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, + 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, 0xFF99CCFF, 0xEA1495EA, + 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, + 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, + 0x7E95BF7E, 0x207DBA20, 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, + 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, 0xFB0F81FB, 0x3DB5793D, + 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, + 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, + 0x13508613, 0x30F7E730, 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, + 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, 0x0B9F410B, 0x8B027B8B, + 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, + 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, + 0xC72BB1C7, 0x6F8EAB6F, 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, + 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, 0xEF1391EF, 0xFE0885FE, + 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, + 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, + 0x29A96929, 0x7D4F647D, 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, + 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, 0x87D1AC87, 0x8E057F8E, + 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, + 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, + 0x5F794C5F, 0xB6B702B6, 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, + 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, 0xAC3357AC, 0x18CFC718, + 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, + 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, + 0xE51D99E5, 0x39233439, 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, + 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, 0xFA9EC8FA, 0x82D6A882, + 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, + 0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, + 0xE2510FE2, 0x00000000, 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, + 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8} +}; + +/* The exp_to_poly and poly_to_exp tables are used to perform efficient + * operations in GF(2^8) represented as GF(2)[x]/w(x) where + * w(x)=x^8+x^6+x^3+x^2+1. We care about doing that because it's part of the + * definition of the RS matrix in the key schedule. Elements of that field + * are polynomials of degree not greater than 7 and all coefficients 0 or 1, + * which can be represented naturally by bytes (just substitute x=2). In that + * form, GF(2^8) addition is the same as bitwise XOR, but GF(2^8) + * multiplication is inefficient without hardware support. To multiply + * faster, I make use of the fact x is a generator for the nonzero elements, + * so that every element p of GF(2)[x]/w(x) is either 0 or equal to (x)^n for + * some n in 0..254. Note that that caret is exponentiation in GF(2^8), + * *not* polynomial notation. So if I want to compute pq where p and q are + * in GF(2^8), I can just say: + * 1. if p=0 or q=0 then pq=0 + * 2. otherwise, find m and n such that p=x^m and q=x^n + * 3. pq=(x^m)(x^n)=x^(m+n), so add m and n and find pq + * The translations in steps 2 and 3 are looked up in the tables + * poly_to_exp (for step 2) and exp_to_poly (for step 3). To see this + * in action, look at the CALC_S macro. As additional wrinkles, note that + * one of my operands is always a constant, so the poly_to_exp lookup on it + * is done in advance; I included the original values in the comments so + * readers can have some chance of recognizing that this *is* the RS matrix + * from the Twofish paper. I've only included the table entries I actually + * need; I never do a lookup on a variable input of zero and the biggest + * exponents I'll ever see are 254 (variable) and 237 (constant), so they'll + * never sum to more than 491. I'm repeating part of the exp_to_poly table + * so that I don't have to do mod-255 reduction in the exponent arithmetic. + * Since I know my constant operands are never zero, I only have to worry + * about zero values in the variable operand, and I do it with a simple + * conditional branch. I know conditionals are expensive, but I couldn't + * see a non-horrible way of avoiding them, and I did manage to group the + * statements so that each if covers four group multiplications. */ + +static const u8 poly_to_exp[255] = { + 0x00, 0x01, 0x17, 0x02, 0x2E, 0x18, 0x53, 0x03, 0x6A, 0x2F, 0x93, 0x19, + 0x34, 0x54, 0x45, 0x04, 0x5C, 0x6B, 0xB6, 0x30, 0xA6, 0x94, 0x4B, 0x1A, + 0x8C, 0x35, 0x81, 0x55, 0xAA, 0x46, 0x0D, 0x05, 0x24, 0x5D, 0x87, 0x6C, + 0x9B, 0xB7, 0xC1, 0x31, 0x2B, 0xA7, 0xA3, 0x95, 0x98, 0x4C, 0xCA, 0x1B, + 0xE6, 0x8D, 0x73, 0x36, 0xCD, 0x82, 0x12, 0x56, 0x62, 0xAB, 0xF0, 0x47, + 0x4F, 0x0E, 0xBD, 0x06, 0xD4, 0x25, 0xD2, 0x5E, 0x27, 0x88, 0x66, 0x6D, + 0xD6, 0x9C, 0x79, 0xB8, 0x08, 0xC2, 0xDF, 0x32, 0x68, 0x2C, 0xFD, 0xA8, + 0x8A, 0xA4, 0x5A, 0x96, 0x29, 0x99, 0x22, 0x4D, 0x60, 0xCB, 0xE4, 0x1C, + 0x7B, 0xE7, 0x3B, 0x8E, 0x9E, 0x74, 0xF4, 0x37, 0xD8, 0xCE, 0xF9, 0x83, + 0x6F, 0x13, 0xB2, 0x57, 0xE1, 0x63, 0xDC, 0xAC, 0xC4, 0xF1, 0xAF, 0x48, + 0x0A, 0x50, 0x42, 0x0F, 0xBA, 0xBE, 0xC7, 0x07, 0xDE, 0xD5, 0x78, 0x26, + 0x65, 0xD3, 0xD1, 0x5F, 0xE3, 0x28, 0x21, 0x89, 0x59, 0x67, 0xFC, 0x6E, + 0xB1, 0xD7, 0xF8, 0x9D, 0xF3, 0x7A, 0x3A, 0xB9, 0xC6, 0x09, 0x41, 0xC3, + 0xAE, 0xE0, 0xDB, 0x33, 0x44, 0x69, 0x92, 0x2D, 0x52, 0xFE, 0x16, 0xA9, + 0x0C, 0x8B, 0x80, 0xA5, 0x4A, 0x5B, 0xB5, 0x97, 0xC9, 0x2A, 0xA2, 0x9A, + 0xC0, 0x23, 0x86, 0x4E, 0xBC, 0x61, 0xEF, 0xCC, 0x11, 0xE5, 0x72, 0x1D, + 0x3D, 0x7C, 0xEB, 0xE8, 0xE9, 0x3C, 0xEA, 0x8F, 0x7D, 0x9F, 0xEC, 0x75, + 0x1E, 0xF5, 0x3E, 0x38, 0xF6, 0xD9, 0x3F, 0xCF, 0x76, 0xFA, 0x1F, 0x84, + 0xA0, 0x70, 0xED, 0x14, 0x90, 0xB3, 0x7E, 0x58, 0xFB, 0xE2, 0x20, 0x64, + 0xD0, 0xDD, 0x77, 0xAD, 0xDA, 0xC5, 0x40, 0xF2, 0x39, 0xB0, 0xF7, 0x49, + 0xB4, 0x0B, 0x7F, 0x51, 0x15, 0x43, 0x91, 0x10, 0x71, 0xBB, 0xEE, 0xBF, + 0x85, 0xC8, 0xA1 +}; + +static const u8 exp_to_poly[492] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x4D, 0x9A, 0x79, 0xF2, + 0xA9, 0x1F, 0x3E, 0x7C, 0xF8, 0xBD, 0x37, 0x6E, 0xDC, 0xF5, 0xA7, 0x03, + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xCD, 0xD7, 0xE3, 0x8B, 0x5B, 0xB6, + 0x21, 0x42, 0x84, 0x45, 0x8A, 0x59, 0xB2, 0x29, 0x52, 0xA4, 0x05, 0x0A, + 0x14, 0x28, 0x50, 0xA0, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xED, 0x97, 0x63, + 0xC6, 0xC1, 0xCF, 0xD3, 0xEB, 0x9B, 0x7B, 0xF6, 0xA1, 0x0F, 0x1E, 0x3C, + 0x78, 0xF0, 0xAD, 0x17, 0x2E, 0x5C, 0xB8, 0x3D, 0x7A, 0xF4, 0xA5, 0x07, + 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x8D, 0x57, 0xAE, 0x11, 0x22, 0x44, 0x88, + 0x5D, 0xBA, 0x39, 0x72, 0xE4, 0x85, 0x47, 0x8E, 0x51, 0xA2, 0x09, 0x12, + 0x24, 0x48, 0x90, 0x6D, 0xDA, 0xF9, 0xBF, 0x33, 0x66, 0xCC, 0xD5, 0xE7, + 0x83, 0x4B, 0x96, 0x61, 0xC2, 0xC9, 0xDF, 0xF3, 0xAB, 0x1B, 0x36, 0x6C, + 0xD8, 0xFD, 0xB7, 0x23, 0x46, 0x8C, 0x55, 0xAA, 0x19, 0x32, 0x64, 0xC8, + 0xDD, 0xF7, 0xA3, 0x0B, 0x16, 0x2C, 0x58, 0xB0, 0x2D, 0x5A, 0xB4, 0x25, + 0x4A, 0x94, 0x65, 0xCA, 0xD9, 0xFF, 0xB3, 0x2B, 0x56, 0xAC, 0x15, 0x2A, + 0x54, 0xA8, 0x1D, 0x3A, 0x74, 0xE8, 0x9D, 0x77, 0xEE, 0x91, 0x6F, 0xDE, + 0xF1, 0xAF, 0x13, 0x26, 0x4C, 0x98, 0x7D, 0xFA, 0xB9, 0x3F, 0x7E, 0xFC, + 0xB5, 0x27, 0x4E, 0x9C, 0x75, 0xEA, 0x99, 0x7F, 0xFE, 0xB1, 0x2F, 0x5E, + 0xBC, 0x35, 0x6A, 0xD4, 0xE5, 0x87, 0x43, 0x86, 0x41, 0x82, 0x49, 0x92, + 0x69, 0xD2, 0xE9, 0x9F, 0x73, 0xE6, 0x81, 0x4F, 0x9E, 0x71, 0xE2, 0x89, + 0x5F, 0xBE, 0x31, 0x62, 0xC4, 0xC5, 0xC7, 0xC3, 0xCB, 0xDB, 0xFB, 0xBB, + 0x3B, 0x76, 0xEC, 0x95, 0x67, 0xCE, 0xD1, 0xEF, 0x93, 0x6B, 0xD6, 0xE1, + 0x8F, 0x53, 0xA6, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x4D, + 0x9A, 0x79, 0xF2, 0xA9, 0x1F, 0x3E, 0x7C, 0xF8, 0xBD, 0x37, 0x6E, 0xDC, + 0xF5, 0xA7, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xCD, 0xD7, 0xE3, + 0x8B, 0x5B, 0xB6, 0x21, 0x42, 0x84, 0x45, 0x8A, 0x59, 0xB2, 0x29, 0x52, + 0xA4, 0x05, 0x0A, 0x14, 0x28, 0x50, 0xA0, 0x0D, 0x1A, 0x34, 0x68, 0xD0, + 0xED, 0x97, 0x63, 0xC6, 0xC1, 0xCF, 0xD3, 0xEB, 0x9B, 0x7B, 0xF6, 0xA1, + 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0xAD, 0x17, 0x2E, 0x5C, 0xB8, 0x3D, 0x7A, + 0xF4, 0xA5, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x8D, 0x57, 0xAE, 0x11, + 0x22, 0x44, 0x88, 0x5D, 0xBA, 0x39, 0x72, 0xE4, 0x85, 0x47, 0x8E, 0x51, + 0xA2, 0x09, 0x12, 0x24, 0x48, 0x90, 0x6D, 0xDA, 0xF9, 0xBF, 0x33, 0x66, + 0xCC, 0xD5, 0xE7, 0x83, 0x4B, 0x96, 0x61, 0xC2, 0xC9, 0xDF, 0xF3, 0xAB, + 0x1B, 0x36, 0x6C, 0xD8, 0xFD, 0xB7, 0x23, 0x46, 0x8C, 0x55, 0xAA, 0x19, + 0x32, 0x64, 0xC8, 0xDD, 0xF7, 0xA3, 0x0B, 0x16, 0x2C, 0x58, 0xB0, 0x2D, + 0x5A, 0xB4, 0x25, 0x4A, 0x94, 0x65, 0xCA, 0xD9, 0xFF, 0xB3, 0x2B, 0x56, + 0xAC, 0x15, 0x2A, 0x54, 0xA8, 0x1D, 0x3A, 0x74, 0xE8, 0x9D, 0x77, 0xEE, + 0x91, 0x6F, 0xDE, 0xF1, 0xAF, 0x13, 0x26, 0x4C, 0x98, 0x7D, 0xFA, 0xB9, + 0x3F, 0x7E, 0xFC, 0xB5, 0x27, 0x4E, 0x9C, 0x75, 0xEA, 0x99, 0x7F, 0xFE, + 0xB1, 0x2F, 0x5E, 0xBC, 0x35, 0x6A, 0xD4, 0xE5, 0x87, 0x43, 0x86, 0x41, + 0x82, 0x49, 0x92, 0x69, 0xD2, 0xE9, 0x9F, 0x73, 0xE6, 0x81, 0x4F, 0x9E, + 0x71, 0xE2, 0x89, 0x5F, 0xBE, 0x31, 0x62, 0xC4, 0xC5, 0xC7, 0xC3, 0xCB +}; + + +/* The table constants are indices of + * S-box entries, preprocessed through q0 and q1. */ +static const u8 calc_sb_tbl[512] = { + 0xA9, 0x75, 0x67, 0xF3, 0xB3, 0xC6, 0xE8, 0xF4, + 0x04, 0xDB, 0xFD, 0x7B, 0xA3, 0xFB, 0x76, 0xC8, + 0x9A, 0x4A, 0x92, 0xD3, 0x80, 0xE6, 0x78, 0x6B, + 0xE4, 0x45, 0xDD, 0x7D, 0xD1, 0xE8, 0x38, 0x4B, + 0x0D, 0xD6, 0xC6, 0x32, 0x35, 0xD8, 0x98, 0xFD, + 0x18, 0x37, 0xF7, 0x71, 0xEC, 0xF1, 0x6C, 0xE1, + 0x43, 0x30, 0x75, 0x0F, 0x37, 0xF8, 0x26, 0x1B, + 0xFA, 0x87, 0x13, 0xFA, 0x94, 0x06, 0x48, 0x3F, + 0xF2, 0x5E, 0xD0, 0xBA, 0x8B, 0xAE, 0x30, 0x5B, + 0x84, 0x8A, 0x54, 0x00, 0xDF, 0xBC, 0x23, 0x9D, + 0x19, 0x6D, 0x5B, 0xC1, 0x3D, 0xB1, 0x59, 0x0E, + 0xF3, 0x80, 0xAE, 0x5D, 0xA2, 0xD2, 0x82, 0xD5, + 0x63, 0xA0, 0x01, 0x84, 0x83, 0x07, 0x2E, 0x14, + 0xD9, 0xB5, 0x51, 0x90, 0x9B, 0x2C, 0x7C, 0xA3, + 0xA6, 0xB2, 0xEB, 0x73, 0xA5, 0x4C, 0xBE, 0x54, + 0x16, 0x92, 0x0C, 0x74, 0xE3, 0x36, 0x61, 0x51, + 0xC0, 0x38, 0x8C, 0xB0, 0x3A, 0xBD, 0xF5, 0x5A, + 0x73, 0xFC, 0x2C, 0x60, 0x25, 0x62, 0x0B, 0x96, + 0xBB, 0x6C, 0x4E, 0x42, 0x89, 0xF7, 0x6B, 0x10, + 0x53, 0x7C, 0x6A, 0x28, 0xB4, 0x27, 0xF1, 0x8C, + 0xE1, 0x13, 0xE6, 0x95, 0xBD, 0x9C, 0x45, 0xC7, + 0xE2, 0x24, 0xF4, 0x46, 0xB6, 0x3B, 0x66, 0x70, + 0xCC, 0xCA, 0x95, 0xE3, 0x03, 0x85, 0x56, 0xCB, + 0xD4, 0x11, 0x1C, 0xD0, 0x1E, 0x93, 0xD7, 0xB8, + 0xFB, 0xA6, 0xC3, 0x83, 0x8E, 0x20, 0xB5, 0xFF, + 0xE9, 0x9F, 0xCF, 0x77, 0xBF, 0xC3, 0xBA, 0xCC, + 0xEA, 0x03, 0x77, 0x6F, 0x39, 0x08, 0xAF, 0xBF, + 0x33, 0x40, 0xC9, 0xE7, 0x62, 0x2B, 0x71, 0xE2, + 0x81, 0x79, 0x79, 0x0C, 0x09, 0xAA, 0xAD, 0x82, + 0x24, 0x41, 0xCD, 0x3A, 0xF9, 0xEA, 0xD8, 0xB9, + 0xE5, 0xE4, 0xC5, 0x9A, 0xB9, 0xA4, 0x4D, 0x97, + 0x44, 0x7E, 0x08, 0xDA, 0x86, 0x7A, 0xE7, 0x17, + 0xA1, 0x66, 0x1D, 0x94, 0xAA, 0xA1, 0xED, 0x1D, + 0x06, 0x3D, 0x70, 0xF0, 0xB2, 0xDE, 0xD2, 0xB3, + 0x41, 0x0B, 0x7B, 0x72, 0xA0, 0xA7, 0x11, 0x1C, + 0x31, 0xEF, 0xC2, 0xD1, 0x27, 0x53, 0x90, 0x3E, + 0x20, 0x8F, 0xF6, 0x33, 0x60, 0x26, 0xFF, 0x5F, + 0x96, 0xEC, 0x5C, 0x76, 0xB1, 0x2A, 0xAB, 0x49, + 0x9E, 0x81, 0x9C, 0x88, 0x52, 0xEE, 0x1B, 0x21, + 0x5F, 0xC4, 0x93, 0x1A, 0x0A, 0xEB, 0xEF, 0xD9, + 0x91, 0xC5, 0x85, 0x39, 0x49, 0x99, 0xEE, 0xCD, + 0x2D, 0xAD, 0x4F, 0x31, 0x8F, 0x8B, 0x3B, 0x01, + 0x47, 0x18, 0x87, 0x23, 0x6D, 0xDD, 0x46, 0x1F, + 0xD6, 0x4E, 0x3E, 0x2D, 0x69, 0xF9, 0x64, 0x48, + 0x2A, 0x4F, 0xCE, 0xF2, 0xCB, 0x65, 0x2F, 0x8E, + 0xFC, 0x78, 0x97, 0x5C, 0x05, 0x58, 0x7A, 0x19, + 0xAC, 0x8D, 0x7F, 0xE5, 0xD5, 0x98, 0x1A, 0x57, + 0x4B, 0x67, 0x0E, 0x7F, 0xA7, 0x05, 0x5A, 0x64, + 0x28, 0xAF, 0x14, 0x63, 0x3F, 0xB6, 0x29, 0xFE, + 0x88, 0xF5, 0x3C, 0xB7, 0x4C, 0x3C, 0x02, 0xA5, + 0xB8, 0xCE, 0xDA, 0xE9, 0xB0, 0x68, 0x17, 0x44, + 0x55, 0xE0, 0x1F, 0x4D, 0x8A, 0x43, 0x7D, 0x69, + 0x57, 0x29, 0xC7, 0x2E, 0x8D, 0xAC, 0x74, 0x15, + 0xB7, 0x59, 0xC4, 0xA8, 0x9F, 0x0A, 0x72, 0x9E, + 0x7E, 0x6E, 0x15, 0x47, 0x22, 0xDF, 0x12, 0x34, + 0x58, 0x35, 0x07, 0x6A, 0x99, 0xCF, 0x34, 0xDC, + 0x6E, 0x22, 0x50, 0xC9, 0xDE, 0xC0, 0x68, 0x9B, + 0x65, 0x89, 0xBC, 0xD4, 0xDB, 0xED, 0xF8, 0xAB, + 0xC8, 0x12, 0xA8, 0xA2, 0x2B, 0x0D, 0x40, 0x52, + 0xDC, 0xBB, 0xFE, 0x02, 0x32, 0x2F, 0xA4, 0xA9, + 0xCA, 0xD7, 0x10, 0x61, 0x21, 0x1E, 0xF0, 0xB4, + 0xD3, 0x50, 0x5D, 0x04, 0x0F, 0xF6, 0x00, 0xC2, + 0x6F, 0x16, 0x9D, 0x25, 0x36, 0x86, 0x42, 0x56, + 0x4A, 0x55, 0x5E, 0x09, 0xC1, 0xBE, 0xE0, 0x91 +}; + +/* Macro to perform one column of the RS matrix multiplication. The + * parameters a, b, c, and d are the four bytes of output; i is the index + * of the key bytes, and w, x, y, and z, are the column of constants from + * the RS matrix, preprocessed through the poly_to_exp table. */ + +#define CALC_S(a, b, c, d, i, w, x, y, z) \ + if (key[i]) { \ + tmp = poly_to_exp[key[i] - 1]; \ + (a) ^= exp_to_poly[tmp + (w)]; \ + (b) ^= exp_to_poly[tmp + (x)]; \ + (c) ^= exp_to_poly[tmp + (y)]; \ + (d) ^= exp_to_poly[tmp + (z)]; \ + } + +/* Macros to calculate the key-dependent S-boxes for a 128-bit key using + * the S vector from CALC_S. CALC_SB_2 computes a single entry in all + * four S-boxes, where i is the index of the entry to compute, and a and b + * are the index numbers preprocessed through the q0 and q1 tables + * respectively. */ + +#define CALC_SB_2(i, a, b) \ + ctx->s[0][i] = mds[0][q0[(a) ^ sa] ^ se]; \ + ctx->s[1][i] = mds[1][q0[(b) ^ sb] ^ sf]; \ + ctx->s[2][i] = mds[2][q1[(a) ^ sc] ^ sg]; \ + ctx->s[3][i] = mds[3][q1[(b) ^ sd] ^ sh] + +/* Macro exactly like CALC_SB_2, but for 192-bit keys. */ + +#define CALC_SB192_2(i, a, b) \ + ctx->s[0][i] = mds[0][q0[q0[(b) ^ sa] ^ se] ^ si]; \ + ctx->s[1][i] = mds[1][q0[q1[(b) ^ sb] ^ sf] ^ sj]; \ + ctx->s[2][i] = mds[2][q1[q0[(a) ^ sc] ^ sg] ^ sk]; \ + ctx->s[3][i] = mds[3][q1[q1[(a) ^ sd] ^ sh] ^ sl]; + +/* Macro exactly like CALC_SB_2, but for 256-bit keys. */ + +#define CALC_SB256_2(i, a, b) \ + ctx->s[0][i] = mds[0][q0[q0[q1[(b) ^ sa] ^ se] ^ si] ^ sm]; \ + ctx->s[1][i] = mds[1][q0[q1[q1[(a) ^ sb] ^ sf] ^ sj] ^ sn]; \ + ctx->s[2][i] = mds[2][q1[q0[q0[(a) ^ sc] ^ sg] ^ sk] ^ so]; \ + ctx->s[3][i] = mds[3][q1[q1[q0[(b) ^ sd] ^ sh] ^ sl] ^ sp]; + +/* Macros to calculate the whitening and round subkeys. CALC_K_2 computes the + * last two stages of the h() function for a given index (either 2i or 2i+1). + * a, b, c, and d are the four bytes going into the last two stages. For + * 128-bit keys, this is the entire h() function and a and c are the index + * preprocessed through q0 and q1 respectively; for longer keys they are the + * output of previous stages. j is the index of the first key byte to use. + * CALC_K computes a pair of subkeys for 128-bit Twofish, by calling CALC_K_2 + * twice, doing the Psuedo-Hadamard Transform, and doing the necessary + * rotations. Its parameters are: a, the array to write the results into, + * j, the index of the first output entry, k and l, the preprocessed indices + * for index 2i, and m and n, the preprocessed indices for index 2i+1. + * CALC_K192_2 expands CALC_K_2 to handle 192-bit keys, by doing an + * additional lookup-and-XOR stage. The parameters a, b, c and d are the + * four bytes going into the last three stages. For 192-bit keys, c = d + * are the index preprocessed through q0, and a = b are the index + * preprocessed through q1; j is the index of the first key byte to use. + * CALC_K192 is identical to CALC_K but for using the CALC_K192_2 macro + * instead of CALC_K_2. + * CALC_K256_2 expands CALC_K192_2 to handle 256-bit keys, by doing an + * additional lookup-and-XOR stage. The parameters a and b are the index + * preprocessed through q0 and q1 respectively; j is the index of the first + * key byte to use. CALC_K256 is identical to CALC_K but for using the + * CALC_K256_2 macro instead of CALC_K_2. */ + +#define CALC_K_2(a, b, c, d, j) \ + mds[0][q0[a ^ key[(j) + 8]] ^ key[j]] \ + ^ mds[1][q0[b ^ key[(j) + 9]] ^ key[(j) + 1]] \ + ^ mds[2][q1[c ^ key[(j) + 10]] ^ key[(j) + 2]] \ + ^ mds[3][q1[d ^ key[(j) + 11]] ^ key[(j) + 3]] + +#define CALC_K(a, j, k, l, m, n) \ + x = CALC_K_2 (k, l, k, l, 0); \ + y = CALC_K_2 (m, n, m, n, 4); \ + y = (y << 8) + (y >> 24); \ + x += y; y += x; ctx->a[j] = x; \ + ctx->a[(j) + 1] = (y << 9) + (y >> 23) + +#define CALC_K192_2(a, b, c, d, j) \ + CALC_K_2 (q0[a ^ key[(j) + 16]], \ + q1[b ^ key[(j) + 17]], \ + q0[c ^ key[(j) + 18]], \ + q1[d ^ key[(j) + 19]], j) + +#define CALC_K192(a, j, k, l, m, n) \ + x = CALC_K192_2 (l, l, k, k, 0); \ + y = CALC_K192_2 (n, n, m, m, 4); \ + y = (y << 8) + (y >> 24); \ + x += y; y += x; ctx->a[j] = x; \ + ctx->a[(j) + 1] = (y << 9) + (y >> 23) + +#define CALC_K256_2(a, b, j) \ + CALC_K192_2 (q1[b ^ key[(j) + 24]], \ + q1[a ^ key[(j) + 25]], \ + q0[a ^ key[(j) + 26]], \ + q0[b ^ key[(j) + 27]], j) + +#define CALC_K256(a, j, k, l, m, n) \ + x = CALC_K256_2 (k, l, 0); \ + y = CALC_K256_2 (m, n, 4); \ + y = (y << 8) + (y >> 24); \ + x += y; y += x; ctx->a[j] = x; \ + ctx->a[(j) + 1] = (y << 9) + (y >> 23) + +/* Perform the key setup. */ + +int twofish_set_key (TWOFISH_context *ctx, + const unsigned char *key, int key_len) +{ + + int i, j, k; + + /* Temporaries for CALC_K. */ + u32 x, y; + + /* The S vector used to key the S-boxes, split up into individual bytes. + * 128-bit keys use only sa through sh; 256-bit use all of them. */ + u8 sa = 0, sb = 0, sc = 0, sd = 0, se = 0, sf = 0, sg = 0, sh = 0; + u8 si = 0, sj = 0, sk = 0, sl = 0, sm = 0, sn = 0, so = 0, sp = 0; + + /* Temporary for CALC_S. */ + u8 tmp; + + /* Check key length. */ + if (key_len != 16 && key_len != 24 && key_len != 32) + return -1; /* unsupported key length */ + + /* Compute the first two words of the S vector. The magic numbers are + * the entries of the RS matrix, preprocessed through poly_to_exp. The + * numbers in the comments are the original (polynomial form) matrix + * entries. */ + CALC_S (sa, sb, sc, sd, 0, 0x00, 0x2D, 0x01, 0x2D); /* 01 A4 02 A4 */ + CALC_S (sa, sb, sc, sd, 1, 0x2D, 0xA4, 0x44, 0x8A); /* A4 56 A1 55 */ + CALC_S (sa, sb, sc, sd, 2, 0x8A, 0xD5, 0xBF, 0xD1); /* 55 82 FC 87 */ + CALC_S (sa, sb, sc, sd, 3, 0xD1, 0x7F, 0x3D, 0x99); /* 87 F3 C1 5A */ + CALC_S (sa, sb, sc, sd, 4, 0x99, 0x46, 0x66, 0x96); /* 5A 1E 47 58 */ + CALC_S (sa, sb, sc, sd, 5, 0x96, 0x3C, 0x5B, 0xED); /* 58 C6 AE DB */ + CALC_S (sa, sb, sc, sd, 6, 0xED, 0x37, 0x4F, 0xE0); /* DB 68 3D 9E */ + CALC_S (sa, sb, sc, sd, 7, 0xE0, 0xD0, 0x8C, 0x17); /* 9E E5 19 03 */ + CALC_S (se, sf, sg, sh, 8, 0x00, 0x2D, 0x01, 0x2D); /* 01 A4 02 A4 */ + CALC_S (se, sf, sg, sh, 9, 0x2D, 0xA4, 0x44, 0x8A); /* A4 56 A1 55 */ + CALC_S (se, sf, sg, sh, 10, 0x8A, 0xD5, 0xBF, 0xD1); /* 55 82 FC 87 */ + CALC_S (se, sf, sg, sh, 11, 0xD1, 0x7F, 0x3D, 0x99); /* 87 F3 C1 5A */ + CALC_S (se, sf, sg, sh, 12, 0x99, 0x46, 0x66, 0x96); /* 5A 1E 47 58 */ + CALC_S (se, sf, sg, sh, 13, 0x96, 0x3C, 0x5B, 0xED); /* 58 C6 AE DB */ + CALC_S (se, sf, sg, sh, 14, 0xED, 0x37, 0x4F, 0xE0); /* DB 68 3D 9E */ + CALC_S (se, sf, sg, sh, 15, 0xE0, 0xD0, 0x8C, 0x17); /* 9E E5 19 03 */ + + if (key_len == 24 || key_len == 32) { /* 192- or 256-bit key */ + /* Calculate the third word of the S vector */ + CALC_S (si, sj, sk, sl, 16, 0x00, 0x2D, 0x01, 0x2D); /* 01 A4 02 A4 */ + CALC_S (si, sj, sk, sl, 17, 0x2D, 0xA4, 0x44, 0x8A); /* A4 56 A1 55 */ + CALC_S (si, sj, sk, sl, 18, 0x8A, 0xD5, 0xBF, 0xD1); /* 55 82 FC 87 */ + CALC_S (si, sj, sk, sl, 19, 0xD1, 0x7F, 0x3D, 0x99); /* 87 F3 C1 5A */ + CALC_S (si, sj, sk, sl, 20, 0x99, 0x46, 0x66, 0x96); /* 5A 1E 47 58 */ + CALC_S (si, sj, sk, sl, 21, 0x96, 0x3C, 0x5B, 0xED); /* 58 C6 AE DB */ + CALC_S (si, sj, sk, sl, 22, 0xED, 0x37, 0x4F, 0xE0); /* DB 68 3D 9E */ + CALC_S (si, sj, sk, sl, 23, 0xE0, 0xD0, 0x8C, 0x17); /* 9E E5 19 03 */ + } + + if (key_len == 32) { /* 256-bit key */ + /* Calculate the fourth word of the S vector */ + CALC_S (sm, sn, so, sp, 24, 0x00, 0x2D, 0x01, 0x2D); /* 01 A4 02 A4 */ + CALC_S (sm, sn, so, sp, 25, 0x2D, 0xA4, 0x44, 0x8A); /* A4 56 A1 55 */ + CALC_S (sm, sn, so, sp, 26, 0x8A, 0xD5, 0xBF, 0xD1); /* 55 82 FC 87 */ + CALC_S (sm, sn, so, sp, 27, 0xD1, 0x7F, 0x3D, 0x99); /* 87 F3 C1 5A */ + CALC_S (sm, sn, so, sp, 28, 0x99, 0x46, 0x66, 0x96); /* 5A 1E 47 58 */ + CALC_S (sm, sn, so, sp, 29, 0x96, 0x3C, 0x5B, 0xED); /* 58 C6 AE DB */ + CALC_S (sm, sn, so, sp, 30, 0xED, 0x37, 0x4F, 0xE0); /* DB 68 3D 9E */ + CALC_S (sm, sn, so, sp, 31, 0xE0, 0xD0, 0x8C, 0x17); /* 9E E5 19 03 */ + + /* Compute the S-boxes. */ + for ( i = j = 0, k = 1; i < 256; i++, j += 2, k += 2 ) { + CALC_SB256_2( i, calc_sb_tbl[j], calc_sb_tbl[k] ); + } + + /* Calculate whitening and round subkeys. The constants are + * indices of subkeys, preprocessed through q0 and q1. */ + CALC_K256 (w, 0, 0xA9, 0x75, 0x67, 0xF3); + CALC_K256 (w, 2, 0xB3, 0xC6, 0xE8, 0xF4); + CALC_K256 (w, 4, 0x04, 0xDB, 0xFD, 0x7B); + CALC_K256 (w, 6, 0xA3, 0xFB, 0x76, 0xC8); + CALC_K256 (k, 0, 0x9A, 0x4A, 0x92, 0xD3); + CALC_K256 (k, 2, 0x80, 0xE6, 0x78, 0x6B); + CALC_K256 (k, 4, 0xE4, 0x45, 0xDD, 0x7D); + CALC_K256 (k, 6, 0xD1, 0xE8, 0x38, 0x4B); + CALC_K256 (k, 8, 0x0D, 0xD6, 0xC6, 0x32); + CALC_K256 (k, 10, 0x35, 0xD8, 0x98, 0xFD); + CALC_K256 (k, 12, 0x18, 0x37, 0xF7, 0x71); + CALC_K256 (k, 14, 0xEC, 0xF1, 0x6C, 0xE1); + CALC_K256 (k, 16, 0x43, 0x30, 0x75, 0x0F); + CALC_K256 (k, 18, 0x37, 0xF8, 0x26, 0x1B); + CALC_K256 (k, 20, 0xFA, 0x87, 0x13, 0xFA); + CALC_K256 (k, 22, 0x94, 0x06, 0x48, 0x3F); + CALC_K256 (k, 24, 0xF2, 0x5E, 0xD0, 0xBA); + CALC_K256 (k, 26, 0x8B, 0xAE, 0x30, 0x5B); + CALC_K256 (k, 28, 0x84, 0x8A, 0x54, 0x00); + CALC_K256 (k, 30, 0xDF, 0xBC, 0x23, 0x9D); + } else if (key_len == 24) { /* 192-bit key */ + /* Compute the S-boxes. */ + for ( i = j = 0, k = 1; i < 256; i++, j += 2, k += 2 ) { + CALC_SB192_2( i, calc_sb_tbl[j], calc_sb_tbl[k] ); + } + + /* Calculate whitening and round subkeys. The constants are + * indices of subkeys, preprocessed through q0 and q1. */ + CALC_K192 (w, 0, 0xA9, 0x75, 0x67, 0xF3); + CALC_K192 (w, 2, 0xB3, 0xC6, 0xE8, 0xF4); + CALC_K192 (w, 4, 0x04, 0xDB, 0xFD, 0x7B); + CALC_K192 (w, 6, 0xA3, 0xFB, 0x76, 0xC8); + CALC_K192 (k, 0, 0x9A, 0x4A, 0x92, 0xD3); + CALC_K192 (k, 2, 0x80, 0xE6, 0x78, 0x6B); + CALC_K192 (k, 4, 0xE4, 0x45, 0xDD, 0x7D); + CALC_K192 (k, 6, 0xD1, 0xE8, 0x38, 0x4B); + CALC_K192 (k, 8, 0x0D, 0xD6, 0xC6, 0x32); + CALC_K192 (k, 10, 0x35, 0xD8, 0x98, 0xFD); + CALC_K192 (k, 12, 0x18, 0x37, 0xF7, 0x71); + CALC_K192 (k, 14, 0xEC, 0xF1, 0x6C, 0xE1); + CALC_K192 (k, 16, 0x43, 0x30, 0x75, 0x0F); + CALC_K192 (k, 18, 0x37, 0xF8, 0x26, 0x1B); + CALC_K192 (k, 20, 0xFA, 0x87, 0x13, 0xFA); + CALC_K192 (k, 22, 0x94, 0x06, 0x48, 0x3F); + CALC_K192 (k, 24, 0xF2, 0x5E, 0xD0, 0xBA); + CALC_K192 (k, 26, 0x8B, 0xAE, 0x30, 0x5B); + CALC_K192 (k, 28, 0x84, 0x8A, 0x54, 0x00); + CALC_K192 (k, 30, 0xDF, 0xBC, 0x23, 0x9D); + } else { /* 128-bit key */ + /* Compute the S-boxes. */ + for ( i = j = 0, k = 1; i < 256; i++, j += 2, k += 2 ) { + CALC_SB_2( i, calc_sb_tbl[j], calc_sb_tbl[k] ); + } + + /* Calculate whitening and round subkeys. The constants are + * indices of subkeys, preprocessed through q0 and q1. */ + CALC_K (w, 0, 0xA9, 0x75, 0x67, 0xF3); + CALC_K (w, 2, 0xB3, 0xC6, 0xE8, 0xF4); + CALC_K (w, 4, 0x04, 0xDB, 0xFD, 0x7B); + CALC_K (w, 6, 0xA3, 0xFB, 0x76, 0xC8); + CALC_K (k, 0, 0x9A, 0x4A, 0x92, 0xD3); + CALC_K (k, 2, 0x80, 0xE6, 0x78, 0x6B); + CALC_K (k, 4, 0xE4, 0x45, 0xDD, 0x7D); + CALC_K (k, 6, 0xD1, 0xE8, 0x38, 0x4B); + CALC_K (k, 8, 0x0D, 0xD6, 0xC6, 0x32); + CALC_K (k, 10, 0x35, 0xD8, 0x98, 0xFD); + CALC_K (k, 12, 0x18, 0x37, 0xF7, 0x71); + CALC_K (k, 14, 0xEC, 0xF1, 0x6C, 0xE1); + CALC_K (k, 16, 0x43, 0x30, 0x75, 0x0F); + CALC_K (k, 18, 0x37, 0xF8, 0x26, 0x1B); + CALC_K (k, 20, 0xFA, 0x87, 0x13, 0xFA); + CALC_K (k, 22, 0x94, 0x06, 0x48, 0x3F); + CALC_K (k, 24, 0xF2, 0x5E, 0xD0, 0xBA); + CALC_K (k, 26, 0x8B, 0xAE, 0x30, 0x5B); + CALC_K (k, 28, 0x84, 0x8A, 0x54, 0x00); + CALC_K (k, 30, 0xDF, 0xBC, 0x23, 0x9D); + } + + return 0; +} + +/* Macros to compute the g() function in the encryption and decryption + * rounds. G1 is the straight g() function; G2 includes the 8-bit + * rotation for the high 32-bit word. */ + +#define G1(a) \ + (ctx->s[0][(a) & 0xFF]) ^ (ctx->s[1][((a) >> 8) & 0xFF]) \ + ^ (ctx->s[2][((a) >> 16) & 0xFF]) ^ (ctx->s[3][(a) >> 24]) + +#define G2(b) \ + (ctx->s[1][(b) & 0xFF]) ^ (ctx->s[2][((b) >> 8) & 0xFF]) \ + ^ (ctx->s[3][((b) >> 16) & 0xFF]) ^ (ctx->s[0][(b) >> 24]) + +/* Encryption and decryption Feistel rounds. Each one calls the two g() + * macros, does the PHT, and performs the XOR and the appropriate bit + * rotations. The parameters are the round number (used to select subkeys), + * and the four 32-bit chunks of the text. */ + +#define ENCROUND(n, a, b, c, d) \ + x = G1 (a); y = G2 (b); \ + x += y; y += x + ctx->k[2 * (n) + 1]; \ + (c) ^= x + ctx->k[2 * (n)]; \ + (c) = ((c) >> 1) + ((c) << 31); \ + (d) = (((d) << 1)+((d) >> 31)) ^ y + +#define DECROUND(n, a, b, c, d) \ + x = G1 (a); y = G2 (b); \ + x += y; y += x; \ + (d) ^= y + ctx->k[2 * (n) + 1]; \ + (d) = ((d) >> 1) + ((d) << 31); \ + (c) = (((c) << 1)+((c) >> 31)); \ + (c) ^= (x + ctx->k[2 * (n)]) + +/* Encryption and decryption cycles; each one is simply two Feistel rounds + * with the 32-bit chunks re-ordered to simulate the "swap" */ + +#define ENCCYCLE(n) \ + ENCROUND (2 * (n), a, b, c, d); \ + ENCROUND (2 * (n) + 1, c, d, a, b) + +#define DECCYCLE(n) \ + DECROUND (2 * (n) + 1, c, d, a, b); \ + DECROUND (2 * (n), a, b, c, d) + +/* Macros to convert the input and output bytes into 32-bit words, + * and simultaneously perform the whitening step. INPACK packs word + * number n into the variable named by x, using whitening subkey number m. + * OUTUNPACK unpacks word number n from the variable named by x, using + * whitening subkey number m. */ + +#define INPACK(n, x, m) \ + x = in[4 * (n)] ^ (in[4 * (n) + 1] << 8) \ + ^ (in[4 * (n) + 2] << 16) ^ (in[4 * (n) + 3] << 24) ^ ctx->w[m] + +#define OUTUNPACK(n, x, m) \ + x ^= ctx->w[m]; \ + out[4 * (n)] = x; out[4 * (n) + 1] = x >> 8; \ + out[4 * (n) + 2] = x >> 16; out[4 * (n) + 3] = x >> 24 + +/* Encrypt one block. in and out may be the same. */ + +int twofish_encrypt (TWOFISH_context *ctx, + const u8 *in, u8 *out) +{ + /* The four 32-bit chunks of the text. */ + u32 a, b, c, d; + + /* Temporaries used by the round function. */ + u32 x, y; + + /* Input whitening and packing. */ + INPACK (0, a, 0); + INPACK (1, b, 1); + INPACK (2, c, 2); + INPACK (3, d, 3); + + /* Encryption Feistel cycles. */ + ENCCYCLE (0); + ENCCYCLE (1); + ENCCYCLE (2); + ENCCYCLE (3); + ENCCYCLE (4); + ENCCYCLE (5); + ENCCYCLE (6); + ENCCYCLE (7); + + /* Output whitening and unpacking. */ + OUTUNPACK (0, c, 4); + OUTUNPACK (1, d, 5); + OUTUNPACK (2, a, 6); + OUTUNPACK (3, b, 7); + + return 0; +} + +/* Decrypt one block. in and out may be the same. */ + +int twofish_decrypt (TWOFISH_context *ctx, + const u8 *in, u8 *out) +{ + /* The four 32-bit chunks of the text. */ + u32 a, b, c, d; + + /* Temporaries used by the round function. */ + u32 x, y; + + /* Input whitening and packing. */ + INPACK (0, c, 4); + INPACK (1, d, 5); + INPACK (2, a, 6); + INPACK (3, b, 7); + + /* Encryption Feistel cycles. */ + DECCYCLE (7); + DECCYCLE (6); + DECCYCLE (5); + DECCYCLE (4); + DECCYCLE (3); + DECCYCLE (2); + DECCYCLE (1); + DECCYCLE (0); + + /* Output whitening and unpacking. */ + OUTUNPACK (0, a, 0); + OUTUNPACK (1, b, 1); + OUTUNPACK (2, c, 2); + OUTUNPACK (3, d, 3); + + return 0; +} + +/* eof */ diff --git a/src/libcrypto/libtwofish/twofish.h b/src/libcrypto/libtwofish/twofish.h new file mode 100644 index 000000000..9b289f265 --- /dev/null +++ b/src/libcrypto/libtwofish/twofish.h @@ -0,0 +1,20 @@ +#ifndef TWOFISH_H +#define TWOFISH_H +#ifdef __KERNEL__ +#include +#else +#include +#endif +/* Structure for an expanded Twofish key. s contains the key-dependent + * S-boxes composed with the MDS matrix; w contains the eight "whitening" + * subkeys, K[0] through K[7]. k holds the remaining, "round" subkeys. Note + * that k[i] corresponds to what the Twofish paper calls K[i+8]. */ +typedef struct { + u_int32_t s[4][256], w[8], k[32]; +} TWOFISH_context; + +typedef TWOFISH_context twofish_context; +int twofish_set_key(twofish_context *tf_ctx, const u_int8_t * in_key, int key_len); +int twofish_encrypt(twofish_context *tf_ctx, const u_int8_t * in, u_int8_t * out); +int twofish_decrypt(twofish_context * tf_ctx, const u_int8_t * in, u_int8_t * out); +#endif /* TWOFISH_H */ diff --git a/src/libcrypto/libtwofish/twofish_cbc.c b/src/libcrypto/libtwofish/twofish_cbc.c new file mode 100644 index 000000000..6e5cf9025 --- /dev/null +++ b/src/libcrypto/libtwofish/twofish_cbc.c @@ -0,0 +1,8 @@ +#ifdef __KERNEL__ +#include +#else +#include +#endif +#include "twofish_cbc.h" +#include "cbc_generic.h" +CBC_IMPL_BLK16(twofish_cbc_encrypt, twofish_context, u_int8_t *, twofish_encrypt, twofish_decrypt); diff --git a/src/libcrypto/libtwofish/twofish_cbc.h b/src/libcrypto/libtwofish/twofish_cbc.h new file mode 100644 index 000000000..9fdea3526 --- /dev/null +++ b/src/libcrypto/libtwofish/twofish_cbc.h @@ -0,0 +1,3 @@ +/* Glue header */ +#include "twofish.h" +int twofish_cbc_encrypt(twofish_context *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t* iv, int encrypt); diff --git a/src/libfreeswan/Makefile.am b/src/libfreeswan/Makefile.am new file mode 100644 index 000000000..d916fca17 --- /dev/null +++ b/src/libfreeswan/Makefile.am @@ -0,0 +1,19 @@ +noinst_LIBRARIES = libfreeswan.a +libfreeswan_a_SOURCES = addrtoa.c addrtot.c addrtypeof.c anyaddr.c atoaddr.c atoasr.c \ + atosa.c atosubnet.c atoul.c copyright.c datatot.c freeswan.h \ + goodmask.c initaddr.c initsaid.c initsubnet.c internal.h ipcomp.h \ + ipsec_ah.h ipsec_alg.h ipsec_encap.h ipsec_eroute.h ipsec_errs.h \ + ipsec_esp.h ipsec_ipe4.h ipsec_kversion.h ipsec_life.h ipsec_md5h.h \ + ipsec_param.h ipsec_policy.h ipsec_proto.h ipsec_radij.h ipsec_rcv.h \ + ipsec_sa.h ipsec_sha1.h ipsec_stats.h ipsec_tunnel.h ipsec_xform.h \ + ipsec_xmit.h keyblobtoid.c optionsfrom.c pfkey_v2_build.c pfkey_v2_debug.c \ + pfkey_v2_ext_bits.c pfkey_v2_parse.c portof.c prng.c radij.h rangetoa.c \ + pfkey.h pfkeyv2.h rangetosubnet.c sameaddr.c satoa.c \ + satot.c subnetof.c subnettoa.c subnettot.c \ + subnettypeof.c ttoaddr.c ttodata.c ttoprotoport.c ttosa.c ttosubnet.c ttoul.c \ + ultoa.c ultot.c version.c +INCLUDES = -I$(top_srcdir)/src/pluto +dist_man3_MANS = anyaddr.3 atoaddr.3 atoasr.3 atosa.3 atoul.3 goodmask.3 initaddr.3 initsubnet.3 \ + keyblobtoid.3 optionsfrom.3 portof.3 prng.3 rangetosubnet.3 sameaddr.3 subnetof.3 \ + ttoaddr.3 ttodata.3 ttosa.3 ttoul.3 version.3 + diff --git a/src/libfreeswan/Makefile.in b/src/libfreeswan/Makefile.in new file mode 100644 index 000000000..97b53d7c0 --- /dev/null +++ b/src/libfreeswan/Makefile.in @@ -0,0 +1,574 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/libfreeswan +DIST_COMMON = $(dist_man3_MANS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +libfreeswan_a_AR = $(AR) $(ARFLAGS) +libfreeswan_a_LIBADD = +am_libfreeswan_a_OBJECTS = addrtoa.$(OBJEXT) addrtot.$(OBJEXT) \ + addrtypeof.$(OBJEXT) anyaddr.$(OBJEXT) atoaddr.$(OBJEXT) \ + atoasr.$(OBJEXT) atosa.$(OBJEXT) atosubnet.$(OBJEXT) \ + atoul.$(OBJEXT) copyright.$(OBJEXT) datatot.$(OBJEXT) \ + goodmask.$(OBJEXT) initaddr.$(OBJEXT) initsaid.$(OBJEXT) \ + initsubnet.$(OBJEXT) keyblobtoid.$(OBJEXT) \ + optionsfrom.$(OBJEXT) pfkey_v2_build.$(OBJEXT) \ + pfkey_v2_debug.$(OBJEXT) pfkey_v2_ext_bits.$(OBJEXT) \ + pfkey_v2_parse.$(OBJEXT) portof.$(OBJEXT) prng.$(OBJEXT) \ + rangetoa.$(OBJEXT) rangetosubnet.$(OBJEXT) sameaddr.$(OBJEXT) \ + satoa.$(OBJEXT) satot.$(OBJEXT) subnetof.$(OBJEXT) \ + subnettoa.$(OBJEXT) subnettot.$(OBJEXT) subnettypeof.$(OBJEXT) \ + ttoaddr.$(OBJEXT) ttodata.$(OBJEXT) ttoprotoport.$(OBJEXT) \ + ttosa.$(OBJEXT) ttosubnet.$(OBJEXT) ttoul.$(OBJEXT) \ + ultoa.$(OBJEXT) ultot.$(OBJEXT) version.$(OBJEXT) +libfreeswan_a_OBJECTS = $(am_libfreeswan_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libfreeswan_a_SOURCES) +DIST_SOURCES = $(libfreeswan_a_SOURCES) +man3dir = $(mandir)/man3 +am__installdirs = "$(DESTDIR)$(man3dir)" +NROFF = nroff +MANS = $(dist_man3_MANS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +noinst_LIBRARIES = libfreeswan.a +libfreeswan_a_SOURCES = addrtoa.c addrtot.c addrtypeof.c anyaddr.c atoaddr.c atoasr.c \ + atosa.c atosubnet.c atoul.c copyright.c datatot.c freeswan.h \ + goodmask.c initaddr.c initsaid.c initsubnet.c internal.h ipcomp.h \ + ipsec_ah.h ipsec_alg.h ipsec_encap.h ipsec_eroute.h ipsec_errs.h \ + ipsec_esp.h ipsec_ipe4.h ipsec_kversion.h ipsec_life.h ipsec_md5h.h \ + ipsec_param.h ipsec_policy.h ipsec_proto.h ipsec_radij.h ipsec_rcv.h \ + ipsec_sa.h ipsec_sha1.h ipsec_stats.h ipsec_tunnel.h ipsec_xform.h \ + ipsec_xmit.h keyblobtoid.c optionsfrom.c pfkey_v2_build.c pfkey_v2_debug.c \ + pfkey_v2_ext_bits.c pfkey_v2_parse.c portof.c prng.c radij.h rangetoa.c \ + pfkey.h pfkeyv2.h rangetosubnet.c sameaddr.c satoa.c \ + satot.c subnetof.c subnettoa.c subnettot.c \ + subnettypeof.c ttoaddr.c ttodata.c ttoprotoport.c ttosa.c ttosubnet.c ttoul.c \ + ultoa.c ultot.c version.c + +INCLUDES = -I$(top_srcdir)/src/pluto +dist_man3_MANS = anyaddr.3 atoaddr.3 atoasr.3 atosa.3 atoul.3 goodmask.3 initaddr.3 initsubnet.3 \ + keyblobtoid.3 optionsfrom.3 portof.3 prng.3 rangetosubnet.3 sameaddr.3 subnetof.3 \ + ttoaddr.3 ttodata.3 ttosa.3 ttoul.3 version.3 + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libfreeswan/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libfreeswan/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libfreeswan.a: $(libfreeswan_a_OBJECTS) $(libfreeswan_a_DEPENDENCIES) + -rm -f libfreeswan.a + $(libfreeswan_a_AR) libfreeswan.a $(libfreeswan_a_OBJECTS) $(libfreeswan_a_LIBADD) + $(RANLIB) libfreeswan.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrtoa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrtot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrtypeof.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/anyaddr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atoaddr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atoasr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atosa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atosubnet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atoul.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copyright.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datatot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/goodmask.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initaddr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initsaid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initsubnet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyblobtoid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optionsfrom.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pfkey_v2_build.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pfkey_v2_debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pfkey_v2_ext_bits.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pfkey_v2_parse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/portof.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prng.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rangetoa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rangetosubnet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sameaddr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/satoa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/satot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnetof.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnettoa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnettot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnettypeof.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttoaddr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttodata.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttoprotoport.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttosa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttosubnet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttoul.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ultoa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ultot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man3: $(man3_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man3dir)" || $(mkdir_p) "$(DESTDIR)$(man3dir)" + @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.3*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 3*) ;; \ + *) ext='3' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst"; \ + done +uninstall-man3: + @$(NORMAL_UNINSTALL) + @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.3*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 3*) ;; \ + *) ext='3' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man3dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man3dir)/$$inst"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(man3dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-man + +install-exec-am: + +install-info: install-info-am + +install-man: install-man3 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-man + +uninstall-man: uninstall-man3 + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-man3 install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-info-am uninstall-man uninstall-man3 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/libfreeswan/addrtoa.c b/src/libfreeswan/addrtoa.c new file mode 100644 index 000000000..b1cc038ed --- /dev/null +++ b/src/libfreeswan/addrtoa.c @@ -0,0 +1,68 @@ +/* + * addresses to ASCII + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: addrtoa.c,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +#define NBYTES 4 /* bytes in an address */ +#define PERBYTE 4 /* three digits plus a dot or NUL */ +#define BUFLEN (NBYTES*PERBYTE) + +#if BUFLEN != ADDRTOA_BUF +#error "ADDRTOA_BUF in freeswan.h inconsistent with addrtoa() code" +#endif + +/* + - addrtoa - convert binary address to ASCII dotted decimal + */ +size_t /* space needed for full conversion */ +addrtoa(addr, format, dst, dstlen) +struct in_addr addr; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + unsigned long a = ntohl(addr.s_addr); + int i; + size_t n; + unsigned long byte; + char buf[BUFLEN]; + char *p; + + switch (format) { + case 0: + break; + default: + return 0; + break; + } + + p = buf; + for (i = NBYTES-1; i >= 0; i--) { + byte = (a >> (i*8)) & 0xff; + p += ultoa(byte, 10, p, PERBYTE); + if (i != 0) + *(p-1) = '.'; + } + n = p - buf; + + if (dstlen > 0) { + if (n > dstlen) + buf[dstlen - 1] = '\0'; + strcpy(dst, buf); + } + return n; +} diff --git a/src/libfreeswan/addrtot.c b/src/libfreeswan/addrtot.c new file mode 100644 index 000000000..f229789f0 --- /dev/null +++ b/src/libfreeswan/addrtot.c @@ -0,0 +1,302 @@ +/* + * addresses to text + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: addrtot.c,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +#define IP4BYTES 4 /* bytes in an IPv4 address */ +#define PERBYTE 4 /* three digits plus a dot or NUL */ +#define IP6BYTES 16 /* bytes in an IPv6 address */ + +/* forwards */ +static size_t normal4(const unsigned char *s, size_t len, char *b, char **dp); +static size_t normal6(const unsigned char *s, size_t len, char *b, char **dp, int squish); +static size_t reverse4(const unsigned char *s, size_t len, char *b, char **dp); +static size_t reverse6(const unsigned char *s, size_t len, char *b, char **dp); + +/* + - addrtot - convert binary address to text (dotted decimal or IPv6 string) + */ +size_t /* space needed for full conversion */ +addrtot(src, format, dst, dstlen) +const ip_address *src; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + const unsigned char *b; + size_t n; + char buf[1+ADDRTOT_BUF+1]; /* :address: */ + char *p; + int t = addrtypeof(src); +# define TF(t, f) (((t)<<8) | (f)) + + n = addrbytesptr(src, &b); + if (n == 0) + return 0; + + switch (TF(t, format)) { + case TF(AF_INET, 0): + n = normal4(b, n, buf, &p); + break; + case TF(AF_INET6, 0): + n = normal6(b, n, buf, &p, 1); + break; + case TF(AF_INET, 'Q'): + n = normal4(b, n, buf, &p); + break; + case TF(AF_INET6, 'Q'): + n = normal6(b, n, buf, &p, 0); + break; + case TF(AF_INET, 'r'): + n = reverse4(b, n, buf, &p); + break; + case TF(AF_INET6, 'r'): + n = reverse6(b, n, buf, &p); + break; + default: /* including (AF_INET, 'R') */ + return 0; + break; + } + + if (dstlen > 0) { + if (dstlen < n) + p[dstlen - 1] = '\0'; + strcpy(dst, p); + } + return n; +} + +/* + - normal4 - normal IPv4 address-text conversion + */ +static size_t /* size of text, including NUL */ +normal4(srcp, srclen, buf, dstp) +const unsigned char *srcp; +size_t srclen; +char *buf; /* guaranteed large enough */ +char **dstp; /* where to put result pointer */ +{ + int i; + char *p; + + if (srclen != IP4BYTES) /* "can't happen" */ + return 0; + p = buf; + for (i = 0; i < IP4BYTES; i++) { + p += ultot(srcp[i], 10, p, PERBYTE); + if (i != IP4BYTES - 1) + *(p-1) = '.'; /* overwrites the NUL */ + } + *dstp = buf; + return p - buf; +} + +/* + - normal6 - normal IPv6 address-text conversion + */ +static size_t /* size of text, including NUL */ +normal6(srcp, srclen, buf, dstp, squish) +const unsigned char *srcp; +size_t srclen; +char *buf; /* guaranteed large enough, plus 2 */ +char **dstp; /* where to put result pointer */ +int squish; /* whether to squish out 0:0 */ +{ + int i; + unsigned long piece; + char *p; + char *q; + + if (srclen != IP6BYTES) /* "can't happen" */ + return 0; + p = buf; + *p++ = ':'; + for (i = 0; i < IP6BYTES/2; i++) { + piece = (srcp[2*i] << 8) + srcp[2*i + 1]; + p += ultot(piece, 16, p, 5); /* 5 = abcd + NUL */ + *(p-1) = ':'; /* overwrites the NUL */ + } + *p = '\0'; + q = strstr(buf, ":0:0:"); + if (squish && q != NULL) { /* zero squishing is possible */ + p = q + 1; + while (*p == '0' && *(p+1) == ':') + p += 2; + q++; + *q++ = ':'; /* overwrite first 0 */ + while (*p != '\0') + *q++ = *p++; + *q = '\0'; + if (!(*(q-1) == ':' && *(q-2) == ':')) + *--q = '\0'; /* strip final : unless :: */ + p = buf; + if (!(*p == ':' && *(p+1) == ':')) + p++; /* skip initial : unless :: */ + } else { + q = p; + *--q = '\0'; /* strip final : */ + p = buf + 1; /* skip initial : */ + } + *dstp = p; + return q - p + 1; +} + +/* + - reverse4 - IPv4 reverse-lookup conversion + */ +static size_t /* size of text, including NUL */ +reverse4(srcp, srclen, buf, dstp) +const unsigned char *srcp; +size_t srclen; +char *buf; /* guaranteed large enough */ +char **dstp; /* where to put result pointer */ +{ + int i; + char *p; + + if (srclen != IP4BYTES) /* "can't happen" */ + return 0; + p = buf; + for (i = IP4BYTES-1; i >= 0; i--) { + p += ultot(srcp[i], 10, p, PERBYTE); + *(p-1) = '.'; /* overwrites the NUL */ + } + strcpy(p, "IN-ADDR.ARPA."); + *dstp = buf; + return strlen(buf) + 1; +} + +/* + - reverse6 - IPv6 reverse-lookup conversion (RFC 1886) + * A trifle inefficient, really shouldn't use ultot... + */ +static size_t /* size of text, including NUL */ +reverse6(srcp, srclen, buf, dstp) +const unsigned char *srcp; +size_t srclen; +char *buf; /* guaranteed large enough */ +char **dstp; /* where to put result pointer */ +{ + int i; + unsigned long piece; + char *p; + + if (srclen != IP6BYTES) /* "can't happen" */ + return 0; + p = buf; + for (i = IP6BYTES-1; i >= 0; i--) { + piece = srcp[i]; + p += ultot(piece&0xf, 16, p, 2); + *(p-1) = '.'; + p += ultot(piece>>4, 16, p, 2); + *(p-1) = '.'; + } + strcpy(p, "IP6.ARPA."); + *dstp = buf; + return strlen(buf) + 1; +} + +/* + - reverse6 - modern IPv6 reverse-lookup conversion (RFC 2874) + * this version removed as it was obsoleted in the end. + */ + +#ifdef ADDRTOT_MAIN + +#include +#include +#include +#include + +void regress(void); + +int +main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s {addr|net/mask|begin...end|-r}\n", + argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + exit(0); +} + +struct rtab { + char *input; + char format; + char *output; /* NULL means error expected */ +} rtab[] = { + {"1.2.3.0", 0, "1.2.3.0"}, + {"1:2::3:4", 0, "1:2::3:4"}, + {"1:2::3:4", 'Q', "1:2:0:0:0:0:3:4"}, + {"1:2:0:0:3:4:0:0", 0, "1:2::3:4:0:0"}, + {"1.2.3.4", 'r' , "4.3.2.1.IN-ADDR.ARPA."}, + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + {"1:2::3:4", 'r', "4.0.0.0.3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.1.0.0.0.IP6.ARPA."}, + {NULL, 0, NULL} +}; + +void +regress() +{ + struct rtab *r; + int status = 0; + ip_address a; + char in[100]; + char buf[100]; + const char *oops; + size_t n; + + for (r = rtab; r->input != NULL; r++) { + strcpy(in, r->input); + + /* convert it *to* internal format */ + oops = ttoaddr(in, strlen(in), 0, &a); + + /* now convert it back */ + + n = addrtot(&a, r->format, buf, sizeof(buf)); + + if (n == 0 && r->output == NULL) + {} /* okay, error expected */ + + else if (n == 0) { + printf("`%s' atoasr failed\n", r->input); + status = 1; + + } else if (r->output == NULL) { + printf("`%s' atoasr succeeded unexpectedly '%c'\n", + r->input, r->format); + status = 1; + } else { + if (strcasecmp(r->output, buf) != 0) { + printf("`%s' '%c' gave `%s', expected `%s'\n", + r->input, r->format, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* ADDRTOT_MAIN */ diff --git a/src/libfreeswan/addrtypeof.c b/src/libfreeswan/addrtypeof.c new file mode 100644 index 000000000..e63509911 --- /dev/null +++ b/src/libfreeswan/addrtypeof.c @@ -0,0 +1,94 @@ +/* + * extract parts of an ip_address + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: addrtypeof.c,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - addrtypeof - get the type of an ip_address + */ +int +addrtypeof(src) +const ip_address *src; +{ + return src->u.v4.sin_family; +} + +/* + - addrbytesptr - get pointer to the address bytes of an ip_address + */ +size_t /* 0 for error */ +addrbytesptr(src, dstp) +const ip_address *src; +const unsigned char **dstp; /* NULL means just a size query */ +{ + const unsigned char *p; + size_t n; + + switch (src->u.v4.sin_family) { + case AF_INET: + p = (const unsigned char *)&src->u.v4.sin_addr.s_addr; + n = 4; + break; + case AF_INET6: + p = (const unsigned char *)&src->u.v6.sin6_addr; + n = 16; + break; + default: + return 0; + break; + } + + if (dstp != NULL) + *dstp = p; + return n; +} + +/* + - addrlenof - get length of the address bytes of an ip_address + */ +size_t /* 0 for error */ +addrlenof(src) +const ip_address *src; +{ + return addrbytesptr(src, NULL); +} + +/* + - addrbytesof - get the address bytes of an ip_address + */ +size_t /* 0 for error */ +addrbytesof(src, dst, dstlen) +const ip_address *src; +unsigned char *dst; +size_t dstlen; +{ + const unsigned char *p; + size_t n; + size_t ncopy; + + n = addrbytesptr(src, &p); + if (n == 0) + return 0; + + if (dstlen > 0) { + ncopy = n; + if (ncopy > dstlen) + ncopy = dstlen; + memcpy(dst, p, ncopy); + } + return n; +} diff --git a/src/libfreeswan/anyaddr.3 b/src/libfreeswan/anyaddr.3 new file mode 100644 index 000000000..4594a9ff9 --- /dev/null +++ b/src/libfreeswan/anyaddr.3 @@ -0,0 +1,87 @@ +.TH IPSEC_ANYADDR 3 "8 Sept 2000" +.\" RCSID $Id: anyaddr.3,v 1.1 2004/03/15 20:35:25 as Exp $ +.SH NAME +ipsec anyaddr \- get "any" address +.br +ipsec isanyaddr \- test address for equality to "any" address +.br +ipsec unspecaddr \- get "unspecified" address +.br +ipsec isunspecaddr \- test address for equality to "unspecified" address +.br +ipsec loopbackaddr \- get loopback address +.br +ipsec isloopbackaddr \- test address for equality to loopback address +.SH SYNOPSIS +.B "#include +.sp +.B "const char *anyaddr(int af, ip_address *dst);" +.br +.B "int isanyaddr(const ip_address *src);" +.br +.B "const char *unspecaddr(int af, ip_address *dst);" +.br +.B "int isunspecaddr(const ip_address *src);" +.br +.B "const char *loopbackaddr(int af, ip_address *dst);" +.br +.B "int isloopbackaddr(const ip_address *src);" +.SH DESCRIPTION +These functions fill in, and test for, special values of the +.I ip_address +type. +.PP +.I Anyaddr +fills in the destination +.I *dst +with the ``any'' address of address family +.IR af +(normally +.B AF_INET +or +.BR AF_INET6 ). +The IPv4 ``any'' address is the one embodied in the old +.B INADDR_ANY +macro. +.PP +.I Isanyaddr +returns +.B 1 +if the +.I src +address equals the ``any'' address, +and +.B 0 +otherwise. +.PP +Similarly, +.I unspecaddr +supplies, and +.I isunspecaddr +tests for, +the ``unspecified'' address, +which may be the same as the ``any'' address. +.PP +Similarly, +.I loopbackaddr +supplies, and +.I islookbackaddr +tests for, +the loopback address. +.PP +.IR Anyaddr , +.IR unspecaddr , +and +.I loopbackaddr +return +.B NULL +for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.SH SEE ALSO +inet(3), ipsec_addrtot(3), ipsec_sameaddr(3) +.SH DIAGNOSTICS +Fatal errors in the address-supplying functions are: +unknown address family. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. diff --git a/src/libfreeswan/anyaddr.c b/src/libfreeswan/anyaddr.c new file mode 100644 index 000000000..08aae6334 --- /dev/null +++ b/src/libfreeswan/anyaddr.c @@ -0,0 +1,146 @@ +/* + * special addresses + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: anyaddr.c,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* these are mostly fallbacks for the no-IPv6-support-in-library case */ +#ifndef IN6ADDR_ANY_INIT +#define IN6ADDR_ANY_INIT {{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }} +#endif +#ifndef IN6ADDR_LOOPBACK_INIT +#define IN6ADDR_LOOPBACK_INIT {{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }} +#endif + +static struct in6_addr v6any = IN6ADDR_ANY_INIT; +static struct in6_addr v6loop = IN6ADDR_LOOPBACK_INIT; + +/* + - anyaddr - initialize to the any-address value + */ +err_t /* NULL for success, else string literal */ +anyaddr(af, dst) +int af; /* address family */ +ip_address *dst; +{ + uint32_t v4any = htonl(INADDR_ANY); + + switch (af) { + case AF_INET: + return initaddr((unsigned char *)&v4any, sizeof(v4any), af, dst); + break; + case AF_INET6: + return initaddr((unsigned char *)&v6any, sizeof(v6any), af, dst); + break; + default: + return "unknown address family in anyaddr/unspecaddr"; + break; + } +} + +/* + - unspecaddr - initialize to the unspecified-address value + */ +err_t /* NULL for success, else string literal */ +unspecaddr(af, dst) +int af; /* address family */ +ip_address *dst; +{ + return anyaddr(af, dst); +} + +/* + - loopbackaddr - initialize to the loopback-address value + */ +err_t /* NULL for success, else string literal */ +loopbackaddr(af, dst) +int af; /* address family */ +ip_address *dst; +{ + uint32_t v4loop = htonl(INADDR_LOOPBACK); + + switch (af) { + case AF_INET: + return initaddr((unsigned char *)&v4loop, sizeof(v4loop), af, dst); + break; + case AF_INET6: + return initaddr((unsigned char *)&v6loop, sizeof(v6loop), af, dst); + break; + default: + return "unknown address family in loopbackaddr"; + break; + } +} + +/* + - isanyaddr - test for the any-address value + */ +int +isanyaddr(src) +const ip_address *src; +{ + uint32_t v4any = htonl(INADDR_ANY); + int cmp; + + switch (src->u.v4.sin_family) { + case AF_INET: + cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4any, sizeof(v4any)); + break; + case AF_INET6: + cmp = memcmp(&src->u.v6.sin6_addr, &v6any, sizeof(v6any)); + break; + default: + return 0; + break; + } + + return (cmp == 0) ? 1 : 0; +} + +/* + - isunspecaddr - test for the unspecified-address value + */ +int +isunspecaddr(src) +const ip_address *src; +{ + return isanyaddr(src); +} + +/* + - isloopbackaddr - test for the loopback-address value + */ +int +isloopbackaddr(src) +const ip_address *src; +{ + uint32_t v4loop = htonl(INADDR_LOOPBACK); + int cmp; + + switch (src->u.v4.sin_family) { + case AF_INET: + cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4loop, sizeof(v4loop)); + break; + case AF_INET6: + cmp = memcmp(&src->u.v6.sin6_addr, &v6loop, sizeof(v6loop)); + break; + default: + return 0; + break; + } + + return (cmp == 0) ? 1 : 0; +} diff --git a/src/libfreeswan/atoaddr.3 b/src/libfreeswan/atoaddr.3 new file mode 100644 index 000000000..a7dc8dca3 --- /dev/null +++ b/src/libfreeswan/atoaddr.3 @@ -0,0 +1,294 @@ +.TH IPSEC_ATOADDR 3 "11 June 2001" +.\" RCSID $Id: atoaddr.3,v 1.1 2004/03/15 20:35:25 as Exp $ +.SH NAME +ipsec atoaddr, addrtoa \- convert Internet addresses to and from ASCII +.br +ipsec atosubnet, subnettoa \- convert subnet/mask ASCII form to and from addresses +.SH SYNOPSIS +.B "#include +.sp +.B "const char *atoaddr(const char *src, size_t srclen," +.ti +1c +.B "struct in_addr *addr);" +.br +.B "size_t addrtoa(struct in_addr addr, int format," +.ti +1c +.B "char *dst, size_t dstlen);" +.sp +.B "const char *atosubnet(const char *src, size_t srclen," +.ti +1c +.B "struct in_addr *addr, struct in_addr *mask);" +.br +.B "size_t subnettoa(struct in_addr addr, struct in_addr mask," +.ti +1c +.B "int format, char *dst, size_t dstlen);" +.SH DESCRIPTION +These functions are obsolete; see +.IR ipsec_ttoaddr (3) +for their replacements. +.PP +.I Atoaddr +converts an ASCII name or dotted-decimal address into a binary address +(in network byte order). +.I Addrtoa +does the reverse conversion, back to an ASCII dotted-decimal address. +.I Atosubnet +and +.I subnettoa +do likewise for the ``address/mask'' ASCII form used to write a +specification of a subnet. +.PP +An address is specified in ASCII as a +dotted-decimal address (e.g. +.BR 1.2.3.4 ), +an eight-digit network-order hexadecimal number with the usual C prefix (e.g. +.BR 0x01020304 , +which is synonymous with +.BR 1.2.3.4 ), +an eight-digit host-order hexadecimal number with a +.B 0h +prefix (e.g. +.BR 0h01020304 , +which is synonymous with +.B 1.2.3.4 +on a big-endian host and +.B 4.3.2.1 +on a little-endian host), +a DNS name to be looked up via +.IR gethostbyname (3), +or an old-style network name to be looked up via +.IR getnetbyname (3). +.PP +A dotted-decimal address may be incomplete, in which case +ASCII-to-binary conversion implicitly appends +as many instances of +.B .0 +as necessary to bring it up to four components. +The components of a dotted-decimal address are always taken as +decimal, and leading zeros are ignored. +For example, +.B 10 +is synonymous with +.BR 10.0.0.0 , +and +.B 128.009.000.032 +is synonymous with +.BR 128.9.0.32 +(the latter example is verbatim from RFC 1166). +The result of +.I addrtoa +is always complete and does not contain leading zeros. +.PP +The letters in +a hexadecimal address may be uppercase or lowercase or any mixture thereof. +Use of hexadecimal addresses is +.B strongly +.BR discouraged ; +they are included only to save hassles when dealing with +the handful of perverted programs which already print +network addresses in hexadecimal. +.PP +DNS names may be complete (optionally terminated with a ``.'') +or incomplete, and are looked up as specified by local system configuration +(see +.IR resolver (5)). +The +.I h_addr +value returned by +.IR gethostbyname (3) +is used, +so with current DNS implementations, +the result when the name corresponds to more than one address is +difficult to predict. +Name lookup resorts to +.IR getnetbyname (3) +only if +.IR gethostbyname (3) +fails. +.PP +A subnet specification is of the form \fInetwork\fB/\fImask\fR. +The +.I network +and +.I mask +can be any form acceptable to +.IR atoaddr . +In addition, the +.I mask +can be a decimal integer (leading zeros ignored) giving a bit count, +in which case +it stands for a mask with that number of high bits on and all others off +(e.g., +.B 24 +means +.BR 255.255.255.0 ). +In any case, the mask must be contiguous +(a sequence of high bits on and all remaining low bits off). +As a special case, the subnet specification +.B %default +is a synonym for +.BR 0.0.0.0/0 . +.PP +.I Atosubnet +ANDs the mask with the address before returning, +so that any non-network bits in the address are turned off +(e.g., +.B 10.1.2.3/24 +is synonymous with +.BR 10.1.2.0/24 ). +.I Subnettoa +generates the decimal-integer-bit-count +form of the mask, +with no leading zeros, +unless the mask is non-contiguous. +.PP +The +.I srclen +parameter of +.I atoaddr +and +.I atosubnet +specifies the length of the ASCII string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I dstlen +parameter of +.I addrtoa +and +.I subnettoa +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.I freeswan.h +header file defines constants, +.B ADDRTOA_BUF +and +.BR SUBNETTOA_BUF , +which are the sizes of buffers just large enough for worst-case results. +.PP +The +.I format +parameter of +.I addrtoa +and +.I subnettoa +specifies what format is to be used for the conversion. +The value +.B 0 +(not the ASCII character +.BR '0' , +but a zero value) +specifies a reasonable default, +and is in fact the only format currently available. +This parameter is a hedge against future needs. +.PP +The ASCII-to-binary functions return NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +The binary-to-ASCII functions return +.B 0 +for a failure, and otherwise +always return the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.SH SEE ALSO +inet(3) +.SH DIAGNOSTICS +Fatal errors in +.I atoaddr +are: +empty input; +attempt to allocate temporary storage for a very long name failed; +name lookup failed; +syntax error in dotted-decimal form; +dotted-decimal component too large to fit in 8 bits. +.PP +Fatal errors in +.I atosubnet +are: +no +.B / +in +.IR src ; +.I atoaddr +error in conversion of +.I network +or +.IR mask ; +bit-count mask too big; +mask non-contiguous. +.PP +Fatal errors in +.I addrtoa +and +.I subnettoa +are: +unknown format. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The interpretation of incomplete dotted-decimal addresses +(e.g. +.B 10/24 +means +.BR 10.0.0.0/24 ) +differs from that of some older conversion +functions, e.g. those of +.IR inet (3). +The behavior of the older functions has never been +particularly consistent or particularly useful. +.PP +Ignoring leading zeros in dotted-decimal components and bit counts +is arguably the most useful behavior in this application, +but it might occasionally cause confusion with the historical use of leading +zeros to denote octal numbers. +.PP +It is barely possible that somebody, somewhere, +might have a legitimate use for non-contiguous subnet masks. +.PP +.IR Getnetbyname (3) +is a historical dreg. +.PP +The restriction of ASCII-to-binary error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The ASCII-to-binary error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = atoaddr( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/atoaddr.c b/src/libfreeswan/atoaddr.c new file mode 100644 index 000000000..0c787b10d --- /dev/null +++ b/src/libfreeswan/atoaddr.c @@ -0,0 +1,238 @@ +/* + * conversion from ASCII forms of addresses to internal ones + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: atoaddr.c,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + * Define NOLEADINGZEROS to interpret 032 as an error, not as 32. There + * is deliberately no way to interpret it as 26 (i.e., as octal). + */ + +/* + * Legal characters in a domain name. Underscore technically is not, + * but is a common misunderstanding. + */ +static const char namechars[] = "abcdefghijklmnopqrstuvwxyz0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_."; + +static const char *try8hex(const char *, size_t, struct in_addr *); +static const char *try8hosthex(const char *, size_t, struct in_addr *); +static const char *trydotted(const char *, size_t, struct in_addr *); +static const char *getbyte(const char **, const char *, int *); + +/* + - atoaddr - convert ASCII name or dotted-decimal address to binary address + */ +const char * /* NULL for success, else string literal */ +atoaddr(src, srclen, addrp) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +struct in_addr *addrp; +{ + struct hostent *h; + struct netent *ne = NULL; + const char *oops; +# define HEXLEN 10 /* strlen("0x11223344") */ +# ifndef ATOADDRBUF +# define ATOADDRBUF 100 +# endif + char namebuf[ATOADDRBUF]; + char *p = namebuf; + char *q; + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + + /* might it be hex? */ + if (srclen == HEXLEN && *src == '0' && CIEQ(*(src+1), 'x')) + return try8hex(src+2, srclen-2, addrp); + if (srclen == HEXLEN && *src == '0' && CIEQ(*(src+1), 'h')) + return try8hosthex(src+2, srclen-2, addrp); + + /* try it as dotted decimal */ + oops = trydotted(src, srclen, addrp); + if (oops == NULL) + return NULL; /* it worked */ + if (*oops != '?') + return oops; /* it *was* probably meant as a d.q. */ + + /* try it as a name -- first, NUL-terminate it */ + if (srclen > sizeof(namebuf)-1) { + p = (char *) MALLOC(srclen+1); + if (p == NULL) + return "unable to allocate temporary space for name"; + } + p[0] = '\0'; + strncat(p, src, srclen); + + /* next, check that it's a vaguely legal name */ + for (q = p; *q != '\0'; q++) + if (!isprint(*q)) + return "unprintable character in name"; + if (strspn(p, namechars) != srclen) + return "illegal (non-DNS-name) character in name"; + + /* try as host name, failing that as /etc/networks network name */ + h = gethostbyname(p); + if (h == NULL) + ne = getnetbyname(p); + if (p != namebuf) + FREE(p); + if (h == NULL && ne == NULL) + return "name lookup failed"; + + if (h != NULL) + memcpy(&addrp->s_addr, h->h_addr, sizeof(addrp->s_addr)); + else + addrp->s_addr = htonl(ne->n_net); + return NULL; +} + +/* + - try8hosthex - try conversion as an eight-digit host-order hex number + */ +const char * /* NULL for success, else string literal */ +try8hosthex(src, srclen, addrp) +const char *src; +size_t srclen; /* should be 8 */ +struct in_addr *addrp; +{ + const char *oops; + unsigned long addr; + + if (srclen != 8) + return "internal error, try8hex called with bad length"; + + oops = atoul(src, srclen, 16, &addr); + if (oops != NULL) + return oops; + + addrp->s_addr = addr; + return NULL; +} + +/* + - try8hex - try conversion as an eight-digit network-order hex number + */ +const char * /* NULL for success, else string literal */ +try8hex(src, srclen, addrp) +const char *src; +size_t srclen; /* should be 8 */ +struct in_addr *addrp; +{ + const char *oops; + + oops = try8hosthex(src, srclen, addrp); + if (oops != NULL) + return oops; + + addrp->s_addr = htonl(addrp->s_addr); + return NULL; +} + +/* + - trydotted - try conversion as dotted decimal + * + * If the first char of a complaint is '?', that means "didn't look like + * dotted decimal at all". + */ +const char * /* NULL for success, else string literal */ +trydotted(src, srclen, addrp) +const char *src; +size_t srclen; +struct in_addr *addrp; +{ + const char *stop = src + srclen; /* just past end */ + int byte; + const char *oops; + unsigned long addr; + int i; +# define NBYTES 4 +# define BYTE 8 + + addr = 0; + for (i = 0; i < NBYTES && src < stop; i++) { + oops = getbyte(&src, stop, &byte); + if (oops != NULL) { + if (*oops != '?') + return oops; /* bad number */ + if (i > 1) + return oops+1; /* failed number */ + return oops; /* with leading '?' */ + } + addr = (addr << BYTE) | byte; + if (i < 3 && src < stop && *src++ != '.') { + if (i == 0) + return "?syntax error in dotted-decimal address"; + else + return "syntax error in dotted-decimal address"; + } + } + addr <<= (NBYTES - i) * BYTE; + if (src != stop) + return "extra garbage on end of dotted-decimal address"; + + addrp->s_addr = htonl(addr); + return NULL; +} + +/* + - getbyte - try to scan a byte in dotted decimal + * A subtlety here is that all this arithmetic on ASCII digits really is + * highly portable -- ANSI C guarantees that digits 0-9 are contiguous. + * It's easier to just do it ourselves than set up for a call to atoul(). + * + * If the first char of a complaint is '?', that means "didn't look like a + * number at all". + */ +const char * /* NULL for success, else string literal */ +getbyte(srcp, stop, retp) +const char **srcp; /* *srcp is updated */ +const char *stop; /* first untouchable char */ +int *retp; /* return-value pointer */ +{ + char c; + const char *p; + int no; + + if (*srcp >= stop) + return "?empty number in dotted-decimal address"; + + if (stop - *srcp >= 3 && **srcp == '0' && CIEQ(*(*srcp+1), 'x')) + return "hex numbers not supported in dotted-decimal addresses"; +#ifdef NOLEADINGZEROS + if (stop - *srcp >= 2 && **srcp == '0' && isdigit(*(*srcp+1))) + return "octal numbers not supported in dotted-decimal addresses"; +#endif /* NOLEADINGZEROS */ + + /* must be decimal, if it's numeric at all */ + no = 0; + p = *srcp; + while (p < stop && no <= 255 && (c = *p) >= '0' && c <= '9') { + no = no*10 + (c - '0'); + p++; + } + if (p == *srcp) + return "?non-numeric component in dotted-decimal address"; + *srcp = p; + if (no > 255) + return "byte overflow in dotted-decimal address"; + *retp = no; + return NULL; +} diff --git a/src/libfreeswan/atoasr.3 b/src/libfreeswan/atoasr.3 new file mode 100644 index 000000000..1bd805db1 --- /dev/null +++ b/src/libfreeswan/atoasr.3 @@ -0,0 +1,186 @@ +.TH IPSEC_ATOASR 3 "11 June 2001" +.\" RCSID $Id: atoasr.3,v 1.1 2004/03/15 20:35:25 as Exp $ +.SH NAME +ipsec atoasr \- convert ASCII to Internet address, subnet, or range +.br +ipsec rangetoa \- convert Internet address range to ASCII +.SH SYNOPSIS +.B "#include +.sp +.B "const char *atoasr(const char *src, size_t srclen," +.ti +1c +.B "char *type, struct in_addr *addrs);" +.br +.B "size_t rangetoa(struct in_addr *addrs, int format, +.ti +1c +.B "char *dst, size_t dstlen);" +.SH DESCRIPTION +These functions are obsolete; +there is no current equivalent, +because so far they have not proved useful. +.PP +.I Atoasr +converts an ASCII address, subnet, or address range +into a suitable combination of binary addresses +(in network byte order). +.I Rangetoa +converts an address range back into ASCII, +using dotted-decimal form for the addresses +(the other reverse conversions are handled by +.IR ipsec_addrtoa (3) +and +.IR ipsec_subnettoa (3)). +.PP +A single address can be any form acceptable to +.IR ipsec_atoaddr (3): +dotted decimal, DNS name, or hexadecimal number. +A subnet +specification uses the form \fInetwork\fB/\fImask\fR +interpreted by +.IR ipsec_atosubnet (3). +.PP +An address range is two +.IR ipsec_atoaddr (3) +addresses separated by a +.B ... +delimiter. +If there are four dots rather than three, the first is taken as +part of the begin address, +e.g. for a complete DNS name which ends with +.B . +to suppress completion attempts. +The begin address of a range must be +less than or equal to the end address. +.PP +The +.I srclen +parameter of +.I atoasr +specifies the length of the ASCII string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I type +parameter of +.I atoasr +must point to a +.B char +variable used to record which form was found. +The +.I addrs +parameter must point to a two-element array of +.B "struct in_addr" +which receives the results. +The values stored into +.BR *type , +and the corresponding values in the array, are: +.PP +.ta 3c +2c +3c + *type addrs[0] addrs[1] +.sp 0.8 +address \&\fB'a'\fR address - +.br +subnet \&\fB's'\fR network mask +.br +range \&\fB'r'\fR begin end +.PP +The +.I dstlen +parameter of +.I rangetoa +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.I freeswan.h +header file defines a constant, +.BR RANGETOA_BUF , +which is the size of a buffer just large enough for worst-case results. +.PP +The +.I format +parameter of +.I rangetoa +specifies what format is to be used for the conversion. +The value +.B 0 +(not the ASCII character +.BR '0' , +but a zero value) +specifies a reasonable default, +and is in fact the only format currently available. +This parameter is a hedge against future needs. +.PP +.I Atoasr +returns NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.I Rangetoa +returns +.B 0 +for a failure, and otherwise +always returns the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.SH SEE ALSO +ipsec_atoaddr(3), ipsec_atosubnet(3) +.SH DIAGNOSTICS +Fatal errors in +.I atoasr +are: +empty input; +error in +.IR ipsec_atoaddr (3) +or +.IR ipsec_atosubnet (3) +during conversion; +begin address of range exceeds end address. +.PP +Fatal errors in +.I rangetoa +are: +unknown format. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The restriction of error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = atoasr( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/atoasr.c b/src/libfreeswan/atoasr.c new file mode 100644 index 000000000..a68409bfb --- /dev/null +++ b/src/libfreeswan/atoasr.c @@ -0,0 +1,212 @@ +/* + * convert from ASCII form of address/subnet/range to binary + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: atoasr.c,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - atoasr - convert ASCII to address, subnet, or range + */ +const char * /* NULL for success, else string literal */ +atoasr(src, srclen, typep, addrsp) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +char *typep; /* return type code: 'a', 's', 'r' */ +struct in_addr addrsp[2]; +{ + const char *punct; + const char *stop; + const char *oops; + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + + /* subnet is easy to spot */ + punct = memchr(src, '/', srclen); + if (punct != NULL) { + *typep = 's'; + return atosubnet(src, srclen, &addrsp[0], &addrsp[1]); + } + + /* try for a range */ + stop = src + srclen; + for (punct = src; (punct = memchr(punct, '.', stop - punct)) != NULL; + punct++) + if (stop - punct > 3 && *(punct+1) == '.' && *(punct+2) == '.') + break; /* NOTE BREAK OUT */ + if (punct == NULL) { + /* didn't find the range delimiter, must be plain address */ + *typep = 'a'; + return atoaddr(src, srclen, &addrsp[0]); + } + + /* looks like a range */ + *typep = 'r'; + if (stop - punct > 4 && *(punct+3) == '.') + punct++; /* first dot is trailing dot of name */ + oops = atoaddr(src, punct - src, &addrsp[0]); + if (oops != NULL) + return oops; + oops = atoaddr(punct+3, stop - (punct+3), &addrsp[1]); + if (oops != NULL) + return oops; + if (ntohl(addrsp[0].s_addr) > ntohl(addrsp[1].s_addr)) + return "invalid range, begin > end"; + return NULL; +} + + + +#ifdef ATOASR_MAIN + +#include +#include +#include +#include + +void regress(void); + +int +main(int argc, char *argv[]) +{ + struct in_addr a[2]; + char buf[100]; + const char *oops; + size_t n; + char type; + + if (argc < 2) { + fprintf(stderr, "Usage: %s {addr|net/mask|begin...end|-r}\n", + argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + oops = atoasr(argv[1], 0, &type, a); + if (oops != NULL) { + fprintf(stderr, "%s: conversion failed: %s\n", argv[0], oops); + exit(1); + } + switch (type) { + case 'a': + n = addrtoa(a[0], 0, buf, sizeof(buf)); + break; + case 's': + n = subnettoa(a[0], a[1], 0, buf, sizeof(buf)); + break; + case 'r': + n = rangetoa(a, 0, buf, sizeof(buf)); + break; + default: + fprintf(stderr, "%s: unknown type '%c'\n", argv[0], type); + exit(1); + break; + } + if (n > sizeof(buf)) { + fprintf(stderr, "%s: reverse conversion of ", argv[0]); + fprintf(stderr, "%s ", inet_ntoa(a[0])); + fprintf(stderr, "%s", inet_ntoa(a[1])); + fprintf(stderr, " failed: need %ld bytes, have only %ld\n", + (long)n, (long)sizeof(buf)); + exit(1); + } + printf("%s\n", buf); + + exit(0); +} + +struct rtab { + char *input; + char *output; /* NULL means error expected */ +} rtab[] = { + {"1.2.3.0", "1.2.3.0"}, + {"1.2.3.0/255.255.255.0", "1.2.3.0/24"}, + {"1.2.3.0...1.2.3.5", "1.2.3.0...1.2.3.5"}, + {"1.2.3.4.5", NULL}, + {"1.2.3.4/", NULL}, + {"1.2.3.4...", NULL}, + {"1.2.3.4....", NULL}, + {"localhost/32", "127.0.0.1/32"}, + {"localhost...127.0.0.3", "127.0.0.1...127.0.0.3"}, + {"127.0.0.0...localhost", "127.0.0.0...127.0.0.1"}, + {"127.0.0.3...localhost", NULL}, + {NULL, NULL} +}; + +void +regress(void) +{ + struct rtab *r; + int status = 0; + struct in_addr a[2]; + char in[100]; + char buf[100]; + const char *oops; + size_t n; + char type; + + for (r = rtab; r->input != NULL; r++) { + strcpy(in, r->input); + oops = atoasr(in, 0, &type, a); + if (oops != NULL && r->output == NULL) + {} /* okay, error expected */ + else if (oops != NULL) { + printf("`%s' atoasr failed: %s\n", r->input, oops); + status = 1; + } else if (r->output == NULL) { + printf("`%s' atoasr succeeded unexpectedly '%c'\n", + r->input, type); + status = 1; + } else { + switch (type) { + case 'a': + n = addrtoa(a[0], 0, buf, sizeof(buf)); + break; + case 's': + n = subnettoa(a[0], a[1], 0, buf, sizeof(buf)); + break; + case 'r': + n = rangetoa(a, 0, buf, sizeof(buf)); + break; + default: + fprintf(stderr, "`%s' unknown type '%c'\n", + r->input, type); + n = 0; + status = 1; + break; + } + if (n > sizeof(buf)) { + printf("`%s' '%c' reverse failed: need %ld\n", + r->input, type, (long)n); + status = 1; + } else if (n > 0 && strcmp(r->output, buf) != 0) { + printf("`%s' '%c' gave `%s', expected `%s'\n", + r->input, type, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* ATOASR_MAIN */ diff --git a/src/libfreeswan/atosa.3 b/src/libfreeswan/atosa.3 new file mode 100644 index 000000000..116483a73 --- /dev/null +++ b/src/libfreeswan/atosa.3 @@ -0,0 +1,218 @@ +.TH IPSEC_ATOSA 3 "11 June 2001" +.\" RCSID $Id: atosa.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec atosa, satoa \- convert IPsec Security Association IDs to and from ASCII +.SH SYNOPSIS +.B "#include +.sp +.B "const char *atosa(const char *src, size_t srclen," +.ti +1c +.B "struct sa_id *sa); +.br +.B "size_t satoa(struct sa_id sa, int format," +.ti +1c +.B "char *dst, size_t dstlen);" +.sp +.B "struct sa_id {" +.ti +1c +.B "struct in_addr dst;" +.ti +1c +.B "ipsec_spi_t spi;" +.ti +1c +.B "int proto;" +.br +.B "};" +.SH DESCRIPTION +These functions are obsolete; see +.IR ipsec_ttosa (3) +for their replacements. +.PP +.I Atosa +converts an ASCII Security Association (SA) specifier into an +.B sa_id +structure (containing +a destination-host address +in network byte order, +an SPI number in network byte order, and +a protocol code). +.I Satoa +does the reverse conversion, back to an ASCII SA specifier. +.PP +An SA is specified in ASCII with a mail-like syntax, e.g. +.BR esp507@1.2.3.4 . +An SA specifier contains +a protocol prefix (currently +.BR ah , +.BR esp , +or +.BR tun ), +an unsigned integer SPI number, +and an IP address. +The SPI number can be decimal or hexadecimal +(with +.B 0x +prefix), as accepted by +.IR ipsec_atoul (3). +The IP address can be any form accepted by +.IR ipsec_atoaddr (3), +e.g. dotted-decimal address or DNS name. +.PP +As a special case, the SA specifier +.B %passthrough +signifies the special SA used to indicate that packets should be +passed through unaltered. +(At present, this is a synonym for +.BR tun0x0@0.0.0.0 , +but that is subject to change without notice.) +This form is known to both +.I atosa +and +.IR satoa , +so the internal form of +.B %passthrough +is never visible. +.PP +The +.B +header file supplies the +.B sa_id +structure, as well as a data type +.B ipsec_spi_t +which is an unsigned 32-bit integer. +(There is no consistency between kernel and user on what such a type +is called, hence the header hides the differences.) +.PP +The protocol code uses the same numbers that IP does. +For user convenience, given the difficulty in acquiring the exact set of +protocol names used by the kernel, +.B +defines the names +.BR SA_ESP , +.BR SA_AH , +and +.B SA_IPIP +to have the same values as the kernel names +.BR IPPROTO_ESP , +.BR IPPROTO_AH , +and +.BR IPPROTO_IPIP . +.PP +The +.I srclen +parameter of +.I atosa +specifies the length of the ASCII string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I dstlen +parameter of +.I satoa +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.I freeswan.h +header file defines a constant, +.BR SATOA_BUF , +which is the size of a buffer just large enough for worst-case results. +.PP +The +.I format +parameter of +.I satoa +specifies what format is to be used for the conversion. +The value +.B 0 +(not the ASCII character +.BR '0' , +but a zero value) +specifies a reasonable default +(currently +lowercase protocol prefix, lowercase hexadecimal SPI, dotted-decimal address). +The value +.B d +causes the SPI to be generated in decimal instead. +.PP +.I Atosa +returns +.B NULL +for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.I Satoa +returns +.B 0 +for a failure, and otherwise +always returns the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.SH SEE ALSO +ipsec_atoul(3), ipsec_atoaddr(3), inet(3) +.SH DIAGNOSTICS +Fatal errors in +.I atosa +are: +empty input; +input too small to be a legal SA specifier; +no +.B @ +in input; +unknown protocol prefix; +conversion error in +.I atoul +or +.IR atoaddr . +.PP +Fatal errors in +.I satoa +are: +unknown format; unknown protocol code. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The +.B tun +protocol code is a FreeS/WANism which may eventually disappear. +.PP +The restriction of ASCII-to-binary error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The ASCII-to-binary error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = atoaddr( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/atosa.c b/src/libfreeswan/atosa.c new file mode 100644 index 000000000..cc3b055d0 --- /dev/null +++ b/src/libfreeswan/atosa.c @@ -0,0 +1,200 @@ +/* + * convert from ASCII form of SA ID to binary + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: atosa.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static struct satype { + char *prefix; + size_t prelen; /* strlen(prefix) */ + int proto; +} satypes[] = { + { "ah", 2, SA_AH }, + { "esp", 3, SA_ESP }, + { "tun", 3, SA_IPIP }, + { "comp", 4, SA_COMP }, + { NULL, 0, 0, } +}; + +/* + - atosa - convert ASCII "ah507@10.0.0.1" to SA identifier + */ +const char * /* NULL for success, else string literal */ +atosa(src, srclen, sa) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +struct sa_id *sa; +{ + const char *at; + const char *addr; + const char *spi = NULL; + struct satype *sat; + unsigned long ul; + const char *oops; +# define MINLEN 5 /* ah0@0 is as short as it can get */ + static char ptname[] = PASSTHROUGHNAME; +# define PTNLEN (sizeof(ptname)-1) /* -1 for NUL */ + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + if (srclen < MINLEN) + return "string too short to be SA specifier"; + if (srclen == PTNLEN && memcmp(src, ptname, PTNLEN) == 0) { + src = PASSTHROUGHIS; + srclen = strlen(src); + } + + at = memchr(src, '@', srclen); + if (at == NULL) + return "no @ in SA specifier"; + + for (sat = satypes; sat->prefix != NULL; sat++) + if (sat->prelen < srclen && + strncmp(src, sat->prefix, sat->prelen) == 0) { + sa->proto = sat->proto; + spi = src + sat->prelen; + break; /* NOTE BREAK OUT */ + } + if (sat->prefix == NULL) + return "SA specifier lacks valid protocol prefix"; + + if (spi >= at) + return "no SPI in SA specifier"; + oops = atoul(spi, at - spi, 13, &ul); + if (oops != NULL) + return oops; + sa->spi = htonl(ul); + + addr = at + 1; + oops = atoaddr(addr, srclen - (addr - src), &sa->dst); + if (oops != NULL) + return oops; + + return NULL; +} + + + +#ifdef ATOSA_MAIN + +#include +#include +#include +#include + +void regress(void); + +int +main(int argc, char *argv[]) +{ + struct sa_id sa; + char buf[100]; + const char *oops; + size_t n; + + if (argc < 2) { + fprintf(stderr, "Usage: %s {ahnnn@aaa|-r}\n", argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + oops = atosa(argv[1], 0, &sa); + if (oops != NULL) { + fprintf(stderr, "%s: conversion failed: %s\n", argv[0], oops); + exit(1); + } + n = satoa(sa, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + fprintf(stderr, "%s: reverse conv of `%d'", argv[0], sa.proto); + fprintf(stderr, "%lu@", (long unsigned int)sa.spi); + fprintf(stderr, "%s", inet_ntoa(sa.dst)); + fprintf(stderr, " failed: need %ld bytes, have only %ld\n", + (long)n, (long)sizeof(buf)); + exit(1); + } + printf("%s\n", buf); + + exit(0); +} + +struct rtab { + char *input; + char *output; /* NULL means error expected */ +} rtab[] = { + {"esp257@1.2.3.0", "esp257@1.2.3.0"}, + {"ah0x20@1.2.3.4", "ah32@1.2.3.4"}, + {"tun011@111.2.3.99", "tun11@111.2.3.99"}, + {"", NULL}, + {"_", NULL}, + {"ah2.2", NULL}, + {"goo2@1.2.3.4", NULL}, + {"esp9@1.2.3.4", "esp9@1.2.3.4"}, + {"espp9@1.2.3.4", NULL}, + {"es9@1.2.3.4", NULL}, + {"ah@1.2.3.4", NULL}, + {"esp7x7@1.2.3.4", NULL}, + {"esp77@1.0x2.3.4", NULL}, + {PASSTHROUGHNAME, PASSTHROUGHNAME}, + {NULL, NULL} +}; + +void +regress(void) +{ + struct rtab *r; + int status = 0; + struct sa_id sa; + char in[100]; + char buf[100]; + const char *oops; + size_t n; + + for (r = rtab; r->input != NULL; r++) { + strcpy(in, r->input); + oops = atosa(in, 0, &sa); + if (oops != NULL && r->output == NULL) + {} /* okay, error expected */ + else if (oops != NULL) { + printf("`%s' atosa failed: %s\n", r->input, oops); + status = 1; + } else if (r->output == NULL) { + printf("`%s' atosa succeeded unexpectedly\n", + r->input); + status = 1; + } else { + n = satoa(sa, 'd', buf, sizeof(buf)); + if (n > sizeof(buf)) { + printf("`%s' satoa failed: need %ld\n", + r->input, (long)n); + status = 1; + } else if (strcmp(r->output, buf) != 0) { + printf("`%s' gave `%s', expected `%s'\n", + r->input, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* ATOSA_MAIN */ diff --git a/src/libfreeswan/atosubnet.c b/src/libfreeswan/atosubnet.c new file mode 100644 index 000000000..9300c2895 --- /dev/null +++ b/src/libfreeswan/atosubnet.c @@ -0,0 +1,216 @@ +/* + * convert from ASCII form of subnet specification to binary + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: atosubnet.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +#ifndef DEFAULTSUBNET +#define DEFAULTSUBNET "%default" +#endif + +/* + - atosubnet - convert ASCII "addr/mask" to address and mask + * Mask can be integer bit count. + */ +const char * /* NULL for success, else string literal */ +atosubnet(src, srclen, addrp, maskp) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +struct in_addr *addrp; +struct in_addr *maskp; +{ + const char *slash; + const char *mask; + size_t mlen; + const char *oops; + unsigned long bc; + static char def[] = DEFAULTSUBNET; +# define DEFLEN (sizeof(def) - 1) /* -1 for NUL */ + static char defis[] = "0/0"; +# define DEFILEN (sizeof(defis) - 1) + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + + if (srclen == DEFLEN && strncmp(src, def, srclen) == 0) { + src = defis; + srclen = DEFILEN; + } + + slash = memchr(src, '/', srclen); + if (slash == NULL) + return "no / in subnet specification"; + mask = slash + 1; + mlen = srclen - (mask - src); + + oops = atoaddr(src, slash-src, addrp); + if (oops != NULL) + return oops; + + oops = atoul(mask, mlen, 10, &bc); + if (oops == NULL) { + /* atoul succeeded, it's a bit-count mask */ + if (bc > ABITS) + return "bit-count mask too large"; +#ifdef NOLEADINGZEROS + if (mlen > 1 && *mask == '0') + return "octal not allowed in mask"; +#endif /* NOLEADINGZEROS */ + *maskp = bitstomask((int)bc); + } else { + oops = atoaddr(mask, mlen, maskp); + if (oops != NULL) + return oops; + if (!goodmask(*maskp)) + return "non-contiguous mask"; + } + + addrp->s_addr &= maskp->s_addr; + return NULL; +} + + + +#ifdef ATOSUBNET_MAIN + +#include +#include +#include +#include + +void regress(void); + +int +main(int argc, char *argv[]) +{ + struct in_addr a; + struct in_addr m; + char buf[100]; + const char *oops; + size_t n; + + if (argc < 2) { + fprintf(stderr, "Usage: %s {addr/mask|-r}\n", argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + oops = atosubnet(argv[1], 0, &a, &m); + if (oops != NULL) { + fprintf(stderr, "%s: conversion failed: %s\n", argv[0], oops); + exit(1); + } + n = subnettoa(a, m, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + fprintf(stderr, "%s: reverse conversion of ", argv[0]); + fprintf(stderr, "%s/", inet_ntoa(a)); + fprintf(stderr, "%s", inet_ntoa(m)); + fprintf(stderr, " failed: need %ld bytes, have only %ld\n", + (long)n, (long)sizeof(buf)); + exit(1); + } + printf("%s\n", buf); + + exit(0); +} + +struct rtab { + char *input; + char *output; /* NULL means error expected */ +} rtab[] = { + {"1.2.3.0/255.255.255.0", "1.2.3.0/24"}, + {"1.2.3.0/24", "1.2.3.0/24"}, + {"1.2.3.1/255.255.255.240", "1.2.3.0/28"}, + {"1.2.3.1/32", "1.2.3.1/32"}, + {"1.2.3.1/0", "0.0.0.0/0"}, +/* "1.2.3.1/255.255.127.0", "1.2.3.0/255.255.127.0", */ + {"1.2.3.1/255.255.127.0", NULL}, + {"128.009.000.032/32", "128.9.0.32/32"}, + {"128.0x9.0.32/32", NULL}, + {"0x80090020/32", "128.9.0.32/32"}, + {"0x800x0020/32", NULL}, + {"128.9.0.32/0xffFF0000", "128.9.0.0/16"}, + {"128.9.0.32/0xff0000FF", NULL}, + {"128.9.0.32/0x0000ffFF", NULL}, + {"128.9.0.32/0x00ffFF0000", NULL}, + {"128.9.0.32/0xffFF", NULL}, + {"128.9.0.32.27/32", NULL}, + {"128.9.0k32/32", NULL}, + {"328.9.0.32/32", NULL}, + {"128.9..32/32", NULL}, + {"10/8", "10.0.0.0/8"}, + {"10.0/8", "10.0.0.0/8"}, + {"10.0.0/8", "10.0.0.0/8"}, + {"10.0.1/24", "10.0.1.0/24"}, + {"_", NULL}, + {"_/_", NULL}, + {"1.2.3.1", NULL}, + {"1.2.3.1/_", NULL}, + {"1.2.3.1/24._", NULL}, + {"1.2.3.1/99", NULL}, + {"localhost/32", "127.0.0.1/32"}, + {"%default", "0.0.0.0/0"}, + {NULL, NULL} +}; + +void +regress() +{ + struct rtab *r; + int status = 0; + struct in_addr a; + struct in_addr m; + char in[100]; + char buf[100]; + const char *oops; + size_t n; + + for (r = rtab; r->input != NULL; r++) { + strcpy(in, r->input); + oops = atosubnet(in, 0, &a, &m); + if (oops != NULL && r->output == NULL) + {} /* okay, error expected */ + else if (oops != NULL) { + printf("`%s' atosubnet failed: %s\n", r->input, oops); + status = 1; + } else if (r->output == NULL) { + printf("`%s' atosubnet succeeded unexpectedly\n", + r->input); + status = 1; + } else { + n = subnettoa(a, m, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + printf("`%s' subnettoa failed: need %ld\n", + r->input, (long)n); + status = 1; + } else if (strcmp(r->output, buf) != 0) { + printf("`%s' gave `%s', expected `%s'\n", + r->input, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* ATOSUBNET_MAIN */ diff --git a/src/libfreeswan/atoul.3 b/src/libfreeswan/atoul.3 new file mode 100644 index 000000000..a606fa4a9 --- /dev/null +++ b/src/libfreeswan/atoul.3 @@ -0,0 +1,161 @@ +.TH IPSEC_ATOUL 3 "11 June 2001" +.\" RCSID $Id: atoul.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec atoul, ultoa \- convert unsigned-long numbers to and from ASCII +.SH SYNOPSIS +.B "#include +.sp +.B "const char *atoul(const char *src, size_t srclen," +.ti +1c +.B "int base, unsigned long *n);" +.br +.B "size_t ultoa(unsigned long n, int base, char *dst," +.ti +1c +.B "size_t dstlen);" +.SH DESCRIPTION +These functions are obsolete; see +.IR ipsec_ttoul (3) +for their replacements. +.PP +.I Atoul +converts an ASCII number into a binary +.B "unsigned long" +value. +.I Ultoa +does the reverse conversion, back to an ASCII version. +.PP +Numbers are specified in ASCII as +decimal (e.g. +.BR 123 ), +octal with a leading zero (e.g. +.BR 012 , +which has value 10), +or hexadecimal with a leading +.B 0x +(e.g. +.BR 0x1f , +which has value 31) +in either upper or lower case. +.PP +The +.I srclen +parameter of +.I atoul +specifies the length of the ASCII string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I base +parameter of +.I atoul +can be +.BR 8 , +.BR 10 , +or +.BR 16 , +in which case the number supplied is assumed to be of that form +(and in the case of +.BR 16 , +to lack any +.B 0x +prefix). +It can also be +.BR 0 , +in which case the number is examined for a leading zero +or a leading +.B 0x +to determine its base, +or +.B 13 +(halfway between 10 and 16), +which has the same effect as +.B 0 +except that a non-hexadecimal +number is considered decimal regardless of any leading zero. +.PP +The +.I dstlen +parameter of +.I ultoa +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +.PP +The +.I base +parameter of +.I ultoa +must be +.BR 8 , +.BR 10 , +or +.BR 16 . +.PP +.I Atoul +returns NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.I Ultoa +returns the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.SH SEE ALSO +atol(3), strtoul(3) +.SH DIAGNOSTICS +Fatal errors in +.I atoul +are: +empty input; +unknown +.IR base ; +non-digit character found; +number too large for an +.BR "unsigned long" . +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +There is no provision for reporting an invalid +.I base +parameter given to +.IR ultoa . +.PP +The restriction of error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The error-reporting convention lends itself to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = atoul( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/atoul.c b/src/libfreeswan/atoul.c new file mode 100644 index 000000000..e32a8cdab --- /dev/null +++ b/src/libfreeswan/atoul.c @@ -0,0 +1,90 @@ +/* + * convert from ASCII form of unsigned long to binary + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: atoul.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - atoul - convert ASCII substring to unsigned long number + */ +const char * /* NULL for success, else string literal */ +atoul(src, srclen, base, resultp) +const char *src; +size_t srclen; /* 0 means strlen(src) */ +int base; /* 0 means figure it out */ +unsigned long *resultp; +{ + const char *stop; + static char hex[] = "0123456789abcdef"; + static char uchex[] = "0123456789ABCDEF"; + int d; + char c; + char *p; + unsigned long r; + unsigned long rlimit; + int dlimit; + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + + if (base == 0 || base == 13) { + if (srclen > 2 && *src == '0' && CIEQ(*(src+1), 'x')) + return atoul(src+2, srclen-2, 16, resultp); + if (srclen > 1 && *src == '0' && base != 13) + return atoul(src+1, srclen-1, 8, resultp); + return atoul(src, srclen, 10, resultp); + } + if (base != 8 && base != 10 && base != 16) + return "unsupported number base"; + + r = 0; + stop = src + srclen; + if (base == 16) { + while (src < stop) { + c = *src++; + p = strchr(hex, c); + if (p != NULL) + d = p - hex; + else { + p = strchr(uchex, c); + if (p == NULL) + return "non-hex-digit in hex number"; + d = p - uchex; + } + r = (r << 4) | d; + } + /* defer length check to catch invalid digits first */ + if (srclen > sizeof(unsigned long) * 2) + return "hex number too long"; + } else { + rlimit = ULONG_MAX / base; + dlimit = (int)(ULONG_MAX - rlimit*base); + while (src < stop) { + c = *src++; + d = c - '0'; + if (d < 0 || d >= base) + return "non-digit in number"; + if (r > rlimit || (r == rlimit && d > dlimit)) + return "unsigned-long overflow"; + r = r*base + d; + } + } + + *resultp = r; + return NULL; +} diff --git a/src/libfreeswan/copyright.c b/src/libfreeswan/copyright.c new file mode 100644 index 000000000..0e836f6c2 --- /dev/null +++ b/src/libfreeswan/copyright.c @@ -0,0 +1,56 @@ +/* + * return IPsec copyright notice + * Copyright (C) 2001, 2002 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: copyright.c,v 1.6 2005/11/02 21:51:13 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static const char *co[] = { + "Copyright (C) 1999-2005 Henry Spencer, Richard Guy Briggs,", + " D. Hugh Redelmeier, Sandy Harris, Claudia Schmeing,", + " Michael Richardson, Angelos D. Keromytis, John Ioannidis,", + "", + " Ken Bantoft, Stephen J. Bevan, JuanJo Ciarlante, Mathieu Lafon,", + " Stephane Laroche, Kai Martius, Tuomo Soini, Herbert Xu,", + "", + " Andreas Steffen, Martin Berner, Marco Bertossa, David Buechi,", + " Ueli Galizzi, Christoph Gysin, Andreas Hess, Patric Lichtsteiner,", + " Michael Meier, Andreas Schleiss, Ariane Seiler,", + " Mario Strasser, Lukas Suter, Roger Wegmann, Simon Zwahlen,", + " Zuercher Hochschule Winterthur (Switzerland).", + "", + " Jan Hutter, Martin Willi, Andreas Steffen,", + " Hochschule fuer Technik Rapperswil (Switzerland).", + "", + "This program 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. See .", + "", + "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 General", + "Public License (file COPYING in the distribution) for more details.", + NULL +}; + +/* + - ipsec_copyright_notice - return copyright notice, as a vector of strings + */ +const char ** +ipsec_copyright_notice() +{ + return co; +} diff --git a/src/libfreeswan/datatot.c b/src/libfreeswan/datatot.c new file mode 100644 index 000000000..fbeb35fa9 --- /dev/null +++ b/src/libfreeswan/datatot.c @@ -0,0 +1,233 @@ +/* + * convert from binary data (e.g. key) to text form + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: datatot.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static void convert(const char *src, size_t nreal, int format, char *out); + +/* + - datatot - convert data bytes to text + */ +size_t /* true length (with NUL) for success */ +datatot(src, srclen, format, dst, dstlen) +const char *src; +size_t srclen; +int format; /* character indicating what format */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + size_t inblocksize; /* process this many bytes at a time */ + size_t outblocksize; /* producing this many */ + size_t breakevery; /* add a _ every this many (0 means don't) */ + size_t sincebreak; /* output bytes since last _ */ + char breakchar; /* character used to break between groups */ + char inblock[10]; /* enough for any format */ + char outblock[10]; /* enough for any format */ + char fake[1]; /* fake output area for dstlen == 0 */ + size_t needed; /* return value */ + char *stop; /* where the terminating NUL will go */ + size_t ntodo; /* remaining input */ + size_t nreal; + char *out; + char *prefix; + + breakevery = 0; + breakchar = '_'; + + switch (format) { + case 0: + case 'h': + format = 'x'; + breakevery = 8; + /* FALLTHROUGH */ + case 'x': + inblocksize = 1; + outblocksize = 2; + prefix = "0x"; + break; + case ':': + format = 'x'; + breakevery = 2; + breakchar = ':'; + /* FALLTHROUGH */ + case 16: + inblocksize = 1; + outblocksize = 2; + prefix = ""; + format = 'x'; + break; + case 's': + inblocksize = 3; + outblocksize = 4; + prefix = "0s"; + break; + case 64: /* beware, equals ' ' */ + inblocksize = 3; + outblocksize = 4; + prefix = ""; + format = 's'; + break; + default: + return 0; + break; + } + assert(inblocksize < sizeof(inblock)); + assert(outblocksize < sizeof(outblock)); + assert(breakevery % outblocksize == 0); + + if (srclen == 0) + return 0; + ntodo = srclen; + + if (dstlen == 0) { /* dispose of awkward special case */ + dst = fake; + dstlen = 1; + } + stop = dst + dstlen - 1; + + nreal = strlen(prefix); + needed = nreal; /* for starters */ + if (dstlen <= nreal) { /* prefix won't fit */ + strncpy(dst, prefix, dstlen - 1); + dst += dstlen - 1; + } else { + strcpy(dst, prefix); + dst += nreal; + } + assert(dst <= stop); + sincebreak = 0; + + while (ntodo > 0) { + if (ntodo < inblocksize) { /* incomplete input */ + memset(inblock, 0, sizeof(inblock)); + memcpy(inblock, src, ntodo); + src = inblock; + nreal = ntodo; + ntodo = inblocksize; + } else + nreal = inblocksize; + out = (outblocksize > stop - dst) ? outblock : dst; + + convert(src, nreal, format, out); + needed += outblocksize; + sincebreak += outblocksize; + if (dst < stop) { + if (out != dst) { + assert(outblocksize > stop - dst); + memcpy(dst, out, stop - dst); + dst = stop; + } else + dst += outblocksize; + } + + src += inblocksize; + ntodo -= inblocksize; + if (breakevery != 0 && sincebreak >= breakevery && ntodo > 0) { + if (dst < stop) + *dst++ = breakchar; + needed++; + sincebreak = 0; + } + } + + assert(dst <= stop); + *dst++ = '\0'; + needed++; + + return needed; +} + +/* + - convert - convert one input block to one output block + */ +static void +convert(src, nreal, format, out) +const char *src; +size_t nreal; /* how much of the input block is real */ +int format; +char *out; +{ + static char hex[] = "0123456789abcdef"; + static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + unsigned char c; + unsigned char c1, c2, c3; + + assert(nreal > 0); + switch (format) { + case 'x': + assert(nreal == 1); + c = (unsigned char)*src; + *out++ = hex[c >> 4]; + *out++ = hex[c & 0xf]; + break; + case 's': + c1 = (unsigned char)*src++; + c2 = (unsigned char)*src++; + c3 = (unsigned char)*src++; + *out++ = base64[c1 >> 2]; /* top 6 bits of c1 */ + c = (c1 & 0x3) << 4; /* bottom 2 of c1... */ + c |= c2 >> 4; /* ...top 4 of c2 */ + *out++ = base64[c]; + if (nreal == 1) + *out++ = '='; + else { + c = (c2 & 0xf) << 2; /* bottom 4 of c2... */ + c |= c3 >> 6; /* ...top 2 of c3 */ + *out++ = base64[c]; + } + if (nreal <= 2) + *out++ = '='; + else + *out++ = base64[c3 & 0x3f]; /* bottom 6 of c3 */ + break; + default: + assert(nreal == 0); /* unknown format */ + break; + } +} + +/* + - datatoa - convert data to ASCII + * backward-compatibility synonym for datatot + */ +size_t /* true length (with NUL) for success */ +datatoa(src, srclen, format, dst, dstlen) +const char *src; +size_t srclen; +int format; /* character indicating what format */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + return datatot(src, srclen, format, dst, dstlen); +} + +/* + - bytestoa - convert data bytes to ASCII + * backward-compatibility synonym for datatot + */ +size_t /* true length (with NUL) for success */ +bytestoa(src, srclen, format, dst, dstlen) +const char *src; +size_t srclen; +int format; /* character indicating what format */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + return datatot(src, srclen, format, dst, dstlen); +} diff --git a/src/libfreeswan/freeswan.h b/src/libfreeswan/freeswan.h new file mode 100644 index 000000000..b1bca870d --- /dev/null +++ b/src/libfreeswan/freeswan.h @@ -0,0 +1,470 @@ +#ifndef _FREESWAN_H +/* + * header file for FreeS/WAN library functions + * Copyright (C) 1998, 1999, 2000 Henry Spencer. + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: freeswan.h,v 1.2 2004/03/22 21:53:17 as Exp $ + */ +#define _FREESWAN_H /* seen it, no need to see it again */ + + + +/* + * We've just got to have some datatypes defined... And annoyingly, just + * where we get them depends on whether we're in userland or not. + */ +#ifdef __KERNEL__ + +# include +# include + +#else /* __KERNEL__ */ + +# include +# include + +# define uint8_t u_int8_t +# define uint16_t u_int16_t +# define uint32_t u_int32_t +# define uint64_t u_int64_t + +# define DEBUG_NO_STATIC static + +#endif /* __KERNEL__ */ + +#include + + +/* + * Grab the kernel version to see if we have NET_21, and therefore + * IPv6. Some of this is repeated from ipsec_kversions.h. Of course, + * we aren't really testing if the kernel has IPv6, but rather if the + * the include files do. + */ +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#define NET_21 +#endif + +#ifndef IPPROTO_COMP +# define IPPROTO_COMP 108 +#endif /* !IPPROTO_COMP */ + +#ifndef IPPROTO_INT +# define IPPROTO_INT 61 +#endif /* !IPPROTO_INT */ + +#ifdef CONFIG_IPSEC_DEBUG +# define DEBUG_NO_STATIC +#else /* CONFIG_IPSEC_DEBUG */ +# define DEBUG_NO_STATIC static +#endif /* CONFIG_IPSEC_DEBUG */ + +#define ESPINUDP_WITH_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ +#define ESPINUDP_WITH_NON_ESP 2 /* draft-ietf-ipsec-nat-t-ike-02 */ + +/* + * Basic data types for the address-handling functions. + * ip_address and ip_subnet are supposed to be opaque types; do not + * use their definitions directly, they are subject to change! + */ + +/* first, some quick fakes in case we're on an old system with no IPv6 */ +#ifndef s6_addr16 +struct in6_addr { + union + { + __u8 u6_addr8[16]; + __u16 u6_addr16[8]; + __u32 u6_addr32[4]; + } in6_u; +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +}; +struct sockaddr_in6 { + unsigned short int sin6_family; /* AF_INET6 */ + __u16 sin6_port; /* Transport layer port # */ + __u32 sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + __u32 sin6_scope_id; /* scope id (new in RFC2553) */ +}; +#endif /* !s6_addr16 */ + +/* then the main types */ +typedef struct { + union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } u; +} ip_address; +typedef struct { + ip_address addr; + int maskbits; +} ip_subnet; + +/* and the SA ID stuff */ +#ifdef __KERNEL__ +typedef __u32 ipsec_spi_t; +#else +typedef u_int32_t ipsec_spi_t; +#endif +typedef struct { /* to identify an SA, we need: */ + ip_address dst; /* A. destination host */ + ipsec_spi_t spi; /* B. 32-bit SPI, assigned by dest. host */ +# define SPI_PASS 256 /* magic values... */ +# define SPI_DROP 257 /* ...for use... */ +# define SPI_REJECT 258 /* ...with SA_INT */ +# define SPI_HOLD 259 +# define SPI_TRAP 260 +# define SPI_TRAPSUBNET 261 + int proto; /* C. protocol */ +# define SA_ESP 50 /* IPPROTO_ESP */ +# define SA_AH 51 /* IPPROTO_AH */ +# define SA_IPIP 4 /* IPPROTO_IPIP */ +# define SA_COMP 108 /* IPPROTO_COMP */ +# define SA_INT 61 /* IANA reserved for internal use */ +} ip_said; +struct sa_id { /* old v4-only version */ + struct in_addr dst; + ipsec_spi_t spi; + int proto; +}; + +/* misc */ +typedef const char *err_t; /* error message, or NULL for success */ +struct prng { /* pseudo-random-number-generator guts */ + unsigned char sbox[256]; + int i, j; + unsigned long count; +}; + + +/* + * definitions for user space, taken from freeswan/ipsec_sa.h + */ +typedef uint32_t IPsecSAref_t; + +#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t)) + +#define IPsecSAref2NFmark(x) ((x) << (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH)) +#define NFmark2IPsecSAref(x) ((x) >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH)) + +#define IPSEC_SAREF_NULL (~((IPsecSAref_t)0)) + +/* GCC magic for use in function definitions! */ +#ifdef GCC_LINT +# define PRINTF_LIKE(n) __attribute__ ((format(printf, n, n+1))) +# define NEVER_RETURNS __attribute__ ((noreturn)) +# define UNUSED __attribute__ ((unused)) +# define BLANK_FORMAT " " /* GCC_LINT whines about empty formats */ +#else +# define PRINTF_LIKE(n) /* ignore */ +# define NEVER_RETURNS /* ignore */ +# define UNUSED /* ignore */ +# define BLANK_FORMAT "" +#endif + + + + + +/* + * new IPv6-compatible functions + */ + +/* text conversions */ +err_t ttoul(const char *src, size_t srclen, int format, unsigned long *dst); +size_t ultot(unsigned long src, int format, char *buf, size_t buflen); +#define ULTOT_BUF (22+1) /* holds 64 bits in octal */ +err_t ttoaddr(const char *src, size_t srclen, int af, ip_address *dst); +err_t tnatoaddr(const char *src, size_t srclen, int af, ip_address *dst); +size_t addrtot(const ip_address *src, int format, char *buf, size_t buflen); +/* RFC 1886 old IPv6 reverse-lookup format is the bulkiest */ +#define ADDRTOT_BUF (32*2 + 3 + 1 + 3 + 1 + 1) +err_t ttosubnet(const char *src, size_t srclen, int af, ip_subnet *dst); +size_t subnettot(const ip_subnet *src, int format, char *buf, size_t buflen); +#define SUBNETTOT_BUF (ADDRTOT_BUF + 1 + 3) +err_t ttosa(const char *src, size_t srclen, ip_said *dst); +size_t satot(const ip_said *src, int format, char *bufptr, size_t buflen); +#define SATOT_BUF (5 + ULTOA_BUF + 1 + ADDRTOT_BUF) +err_t ttodata(const char *src, size_t srclen, int base, char *buf, + size_t buflen, size_t *needed); +err_t ttodatav(const char *src, size_t srclen, int base, + char *buf, size_t buflen, size_t *needed, + char *errp, size_t errlen, unsigned int flags); +#define TTODATAV_BUF 40 /* ttodatav's largest non-literal message */ +#define TTODATAV_IGNORESPACE (1<<1) /* ignore spaces in base64 encodings*/ +#define TTODATAV_SPACECOUNTS 0 /* do not ignore spaces in base64 */ + +size_t datatot(const char *src, size_t srclen, int format, char *buf, + size_t buflen); +size_t keyblobtoid(const unsigned char *src, size_t srclen, char *dst, + size_t dstlen); +size_t splitkeytoid(const unsigned char *e, size_t elen, const unsigned char *m, + size_t mlen, char *dst, size_t dstlen); +#define KEYID_BUF 10 /* up to 9 text digits plus NUL */ +err_t ttoprotoport(char *src, size_t src_len, u_int8_t *proto, u_int16_t *port, + int *has_port_wildcard); + +/* initializations */ +void initsaid(const ip_address *addr, ipsec_spi_t spi, int proto, ip_said *dst); +err_t loopbackaddr(int af, ip_address *dst); +err_t unspecaddr(int af, ip_address *dst); +err_t anyaddr(int af, ip_address *dst); +err_t initaddr(const unsigned char *src, size_t srclen, int af, ip_address *dst); +err_t initsubnet(const ip_address *addr, int maskbits, int clash, ip_subnet *dst); +err_t addrtosubnet(const ip_address *addr, ip_subnet *dst); + +/* misc. conversions and related */ +err_t rangetosubnet(const ip_address *from, const ip_address *to, ip_subnet *dst); +int addrtypeof(const ip_address *src); +int subnettypeof(const ip_subnet *src); +size_t addrlenof(const ip_address *src); +size_t addrbytesptr(const ip_address *src, const unsigned char **dst); +size_t addrbytesof(const ip_address *src, unsigned char *dst, size_t dstlen); +int masktocount(const ip_address *src); +void networkof(const ip_subnet *src, ip_address *dst); +void maskof(const ip_subnet *src, ip_address *dst); + +/* tests */ +int sameaddr(const ip_address *a, const ip_address *b); +int addrcmp(const ip_address *a, const ip_address *b); +int samesubnet(const ip_subnet *a, const ip_subnet *b); +int addrinsubnet(const ip_address *a, const ip_subnet *s); +int subnetinsubnet(const ip_subnet *a, const ip_subnet *b); +int subnetishost(const ip_subnet *s); +int samesaid(const ip_said *a, const ip_said *b); +int sameaddrtype(const ip_address *a, const ip_address *b); +int samesubnettype(const ip_subnet *a, const ip_subnet *b); +int isanyaddr(const ip_address *src); +int isunspecaddr(const ip_address *src); +int isloopbackaddr(const ip_address *src); + +/* low-level grot */ +int portof(const ip_address *src); +void setportof(int port, ip_address *dst); +struct sockaddr *sockaddrof(ip_address *src); +size_t sockaddrlenof(const ip_address *src); + +/* PRNG */ +void prng_init(struct prng *prng, const unsigned char *key, size_t keylen); +void prng_bytes(struct prng *prng, unsigned char *dst, size_t dstlen); +unsigned long prng_count(struct prng *prng); +void prng_final(struct prng *prng); + +/* odds and ends */ +const char *ipsec_version_code(void); +const char *ipsec_version_string(void); +const char **ipsec_copyright_notice(void); + +const char *dns_string_rr(int rr, char *buf, int bufsize); +const char *dns_string_datetime(time_t seconds, + char *buf, + int bufsize); + + +/* + * old functions, to be deleted eventually + */ + +/* unsigned long */ +const char * /* NULL for success, else string literal */ +atoul( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + int base, /* 0 means figure it out */ + unsigned long *resultp +); +size_t /* space needed for full conversion */ +ultoa( + unsigned long n, + int base, + char *dst, + size_t dstlen +); +#define ULTOA_BUF 21 /* just large enough for largest result, */ + /* assuming 64-bit unsigned long! */ + +/* Internet addresses */ +const char * /* NULL for success, else string literal */ +atoaddr( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + struct in_addr *addr +); +size_t /* space needed for full conversion */ +addrtoa( + struct in_addr addr, + int format, /* character; 0 means default */ + char *dst, + size_t dstlen +); +#define ADDRTOA_BUF 16 /* just large enough for largest result */ + +/* subnets */ +const char * /* NULL for success, else string literal */ +atosubnet( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + struct in_addr *addr, + struct in_addr *mask +); +size_t /* space needed for full conversion */ +subnettoa( + struct in_addr addr, + struct in_addr mask, + int format, /* character; 0 means default */ + char *dst, + size_t dstlen +); +#define SUBNETTOA_BUF 32 /* large enough for worst case result */ + +/* ranges */ +const char * /* NULL for success, else string literal */ +atoasr( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + char *type, /* 'a', 's', 'r' */ + struct in_addr *addrs /* two-element array */ +); +size_t /* space needed for full conversion */ +rangetoa( + struct in_addr *addrs, /* two-element array */ + int format, /* character; 0 means default */ + char *dst, + size_t dstlen +); +#define RANGETOA_BUF 34 /* large enough for worst case result */ + +/* data types for SA conversion functions */ + +/* SAs */ +const char * /* NULL for success, else string literal */ +atosa( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + struct sa_id *sa +); +size_t /* space needed for full conversion */ +satoa( + struct sa_id sa, + int format, /* character; 0 means default */ + char *dst, + size_t dstlen +); +#define SATOA_BUF (3+ULTOA_BUF+ADDRTOA_BUF) + +/* generic data, e.g. keys */ +const char * /* NULL for success, else string literal */ +atobytes( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + char *dst, + size_t dstlen, + size_t *lenp /* NULL means don't bother telling me */ +); +size_t /* 0 failure, else true size */ +bytestoa( + const char *src, + size_t srclen, + int format, /* character; 0 means default */ + char *dst, + size_t dstlen +); + +/* old versions of generic-data functions; deprecated */ +size_t /* 0 failure, else true size */ +atodata( + const char *src, + size_t srclen, /* 0 means strlen(src) */ + char *dst, + size_t dstlen +); +size_t /* 0 failure, else true size */ +datatoa( + const char *src, + size_t srclen, + int format, /* character; 0 means default */ + char *dst, + size_t dstlen +); + +/* part extraction and special addresses */ +struct in_addr +subnetof( + struct in_addr addr, + struct in_addr mask +); +struct in_addr +hostof( + struct in_addr addr, + struct in_addr mask +); +struct in_addr +broadcastof( + struct in_addr addr, + struct in_addr mask +); + +/* mask handling */ +int +goodmask( + struct in_addr mask +); +int +masktobits( + struct in_addr mask +); +struct in_addr +bitstomask( + int n +); + + + +/* + * general utilities + */ + +#ifndef __KERNEL__ +/* option pickup from files (userland only because of use of FILE) */ +const char *optionsfrom(const char *filename, int *argcp, char ***argvp, + int optind, FILE *errorreport); +#endif + +/* + * Debugging levels for pfkey_lib_debug + */ +#define PF_KEY_DEBUG_PARSE_NONE 0 +#define PF_KEY_DEBUG_PARSE_PROBLEM 1 +#define PF_KEY_DEBUG_PARSE_STRUCT 2 +#define PF_KEY_DEBUG_PARSE_FLOW 4 +#define PF_KEY_DEBUG_PARSE_MAX 7 + +extern unsigned int pfkey_lib_debug; /* bits selecting what to report */ + +/* + * pluto and lwdnsq need to know the maximum size of the commands to, + * and replies from lwdnsq. + */ + +#define LWDNSQ_CMDBUF_LEN 1024 +#define LWDNSQ_RESULT_LEN_MAX 4096 + +#endif /* _FREESWAN_H */ diff --git a/src/libfreeswan/goodmask.3 b/src/libfreeswan/goodmask.3 new file mode 100644 index 000000000..4a573e51e --- /dev/null +++ b/src/libfreeswan/goodmask.3 @@ -0,0 +1,57 @@ +.TH IPSEC_GOODMASK 3 "11 June 2001" +.\" RCSID $Id: goodmask.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec goodmask \- is this Internet subnet mask a valid one? +.br +ipsec masktobits \- convert Internet subnet mask to bit count +.br +ipsec bitstomask \- convert bit count to Internet subnet mask +.SH SYNOPSIS +.B "#include +.sp +.B "int goodmask(struct in_addr mask);" +.br +.B "int masktobits(struct in_addr mask);" +.br +.B "struct in_addr bitstomask(int n);" +.SH DESCRIPTION +These functions are obsolete; +see +.IR ipsec_masktocount (3) +for a partial replacement. +.PP +.I Goodmask +reports whether the subnet +.I mask +is a valid one, +i.e. consists of a (possibly empty) sequence of +.BR 1 s +followed by a (possibly empty) sequence of +.BR 0 s. +.I Masktobits +takes a (valid) subnet mask and returns the number of +.B 1 +bits in it. +.I Bitstomask +reverses this, +returning the subnet mask corresponding to bit count +.IR n . +.PP +All masks are in network byte order. +.SH SEE ALSO +inet(3), ipsec_atosubnet(3) +.SH DIAGNOSTICS +.I Masktobits +returns +.B \-1 +for an invalid mask. +.I Bitstomask +returns an all-zeros mask for a negative or out-of-range +.IR n . +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The error-reporting convention of +.I bitstomask +is less than ideal; +zero is sometimes a legitimate mask. diff --git a/src/libfreeswan/goodmask.c b/src/libfreeswan/goodmask.c new file mode 100644 index 000000000..fe7a42335 --- /dev/null +++ b/src/libfreeswan/goodmask.c @@ -0,0 +1,97 @@ +/* + * minor utilities for subnet-mask manipulation + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: goodmask.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - goodmask - is this a good (^1*0*$) subnet mask? + * You are not expected to understand this. See Henry S. Warren Jr, + * "Functions realizable with word-parallel logical and two's-complement + * addition instructions", CACM 20.6 (June 1977), p.439. + */ +int /* predicate */ +goodmask(mask) +struct in_addr mask; +{ + unsigned long x = ntohl(mask.s_addr); + /* clear rightmost contiguous string of 1-bits */ +# define CRCS1B(x) (((x|(x-1))+1)&x) +# define TOPBIT (1UL << 31) + + /* either zero, or has one string of 1-bits which is left-justified */ + if (x == 0 || (CRCS1B(x) == 0 && (x&TOPBIT))) + return 1; + return 0; +} + +/* + - masktobits - how many bits in this mask? + * The algorithm is essentially a binary search, but highly optimized + * for this particular task. + */ +int /* -1 means !goodmask() */ +masktobits(mask) +struct in_addr mask; +{ + unsigned long m = ntohl(mask.s_addr); + int masklen; + + if (!goodmask(mask)) + return -1; + + if (m&0x00000001UL) + return 32; + masklen = 0; + if (m&(0x0000ffffUL<<1)) { /* <<1 for 1-origin numbering */ + masklen |= 0x10; + m <<= 16; + } + if (m&(0x00ff0000UL<<1)) { + masklen |= 0x08; + m <<= 8; + } + if (m&(0x0f000000UL<<1)) { + masklen |= 0x04; + m <<= 4; + } + if (m&(0x30000000UL<<1)) { + masklen |= 0x02; + m <<= 2; + } + if (m&(0x40000000UL<<1)) + masklen |= 0x01; + + return masklen; +} + +/* + - bitstomask - return a mask with this many high bits on + */ +struct in_addr +bitstomask(n) +int n; +{ + struct in_addr result; + + if (n > 0 && n <= ABITS) + result.s_addr = htonl(~((1UL << (ABITS - n)) - 1)); + else if (n == 0) + result.s_addr = 0; + else + result.s_addr = 0; /* best error report we can do */ + return result; +} diff --git a/src/libfreeswan/initaddr.3 b/src/libfreeswan/initaddr.3 new file mode 100644 index 000000000..b963f21cc --- /dev/null +++ b/src/libfreeswan/initaddr.3 @@ -0,0 +1,129 @@ +.TH IPSEC_INITADDR 3 "11 Sept 2000" +.\" RCSID $Id: initaddr.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec initaddr \- initialize an ip_address +.br +ipsec addrtypeof \- get address type of an ip_address +.br +ipsec addrlenof \- get length of address within an ip_address +.br +ipsec addrbytesof \- get copy of address within an ip_address +.br +ipsec addrbytesptr \- get pointer to address within an ip_address +.SH SYNOPSIS +.B "#include " +.sp +.B "const char *initaddr(const char *src, size_t srclen," +.ti +1c +.B "int af, ip_address *dst);" +.br +.B "int addrtypeof(const ip_address *src);" +.br +.B "size_t addrlenof(const ip_address *src);" +.br +.B "size_t addrbytesof(const ip_address *src," +.ti +1c +.B "unsigned char *dst, size_t dstlen);" +.br +.B "size_t addrbytesptr(const ip_address *src," +.ti +1c +.B "const unsigned char **dst);" +.SH DESCRIPTION +The +.B +library uses an internal type +.I ip_address +to contain one of the (currently two) types of IP address. +These functions provide basic tools for creating and examining this type. +.PP +.I Initaddr +initializes a variable +.I *dst +of type +.I ip_address +from an address +(in network byte order, +indicated by a pointer +.I src +and a length +.IR srclen ) +and an address family +.I af +(typically +.B AF_INET +or +.BR AF_INET6 ). +The length must be consistent with the address family. +.PP +.I Addrtypeof +returns the address type of an address, +normally +.B AF_INET +or +.BR AF_INET6 . +(The +.B +header file arranges to include the necessary headers for these +names to be known.) +.PP +.I Addrlenof +returns the size (in bytes) of the address within an +.IR ip_address , +to permit storage allocation etc. +.PP +.I Addrbytesof +copies the address within the +.I ip_address +.I src +to the buffer indicated by the pointer +.I dst +and the length +.IR dstlen , +and returns the address length (in bytes). +If the address will not fit, +as many bytes as will fit are copied; +the returned length is still the full length. +It is the caller's responsibility to check the +returned value to ensure that there was enough room. +.PP +.I Addrbytesptr +sets +.I *dst +to a pointer to the internal address within the +.IR ip_address , +and returns the address length (in bytes). +If +.I dst +is +.BR NULL , +it just returns the address length. +The pointer points to +.B const +to discourage misuse. +.PP +.I Initaddr +returns +.B NULL +for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.PP +The functions which return +.I size_t +return +.B 0 +for a failure. +.SH SEE ALSO +inet(3), ipsec_ttoaddr(3) +.SH DIAGNOSTICS +An unknown address family is a fatal error for any of these functions +except +.IR addrtypeof . +An address-size mismatch is a fatal error for +.IR initaddr . +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +.I Addrtypeof +should probably have been named +.IR addrfamilyof . diff --git a/src/libfreeswan/initaddr.c b/src/libfreeswan/initaddr.c new file mode 100644 index 000000000..c215f6bdf --- /dev/null +++ b/src/libfreeswan/initaddr.c @@ -0,0 +1,51 @@ +/* + * initialize address structure + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: initaddr.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - initaddr - initialize ip_address from bytes + */ +err_t /* NULL for success, else string literal */ +initaddr(src, srclen, af, dst) +const unsigned char *src; +size_t srclen; +int af; /* address family */ +ip_address *dst; +{ + switch (af) { + case AF_INET: + if (srclen != 4) + return "IPv4 address must be exactly 4 bytes"; + dst->u.v4.sin_family = af; + dst->u.v4.sin_port = 0; /* unused */ + memcpy((char *)&dst->u.v4.sin_addr.s_addr, src, srclen); + break; + case AF_INET6: + if (srclen != 16) + return "IPv6 address must be exactly 16 bytes"; + dst->u.v6.sin6_family = af; + dst->u.v6.sin6_flowinfo = 0; /* unused */ + dst->u.v6.sin6_port = 0; /* unused */ + memcpy((char *)&dst->u.v6.sin6_addr, src, srclen); + break; + default: + return "unknown address family in initaddr"; + break; + } + return NULL; +} diff --git a/src/libfreeswan/initsaid.c b/src/libfreeswan/initsaid.c new file mode 100644 index 000000000..4790f6981 --- /dev/null +++ b/src/libfreeswan/initsaid.c @@ -0,0 +1,33 @@ +/* + * initialize SA ID structure + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: initsaid.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - initsaid - initialize SA ID from bits + */ +void +initsaid(addr, spi, proto, dst) +const ip_address *addr; +ipsec_spi_t spi; +int proto; +ip_said *dst; +{ + dst->dst = *addr; + dst->spi = spi; + dst->proto = proto; +} diff --git a/src/libfreeswan/initsubnet.3 b/src/libfreeswan/initsubnet.3 new file mode 100644 index 000000000..670f71778 --- /dev/null +++ b/src/libfreeswan/initsubnet.3 @@ -0,0 +1,137 @@ +.TH IPSEC_INITSUBNET 3 "12 March 2002" +.\" RCSID $Id: initsubnet.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec initsubnet \- initialize an ip_subnet +.br +ipsec addrtosubnet \- initialize a singleton ip_subnet +.br +ipsec subnettypeof \- get address type of an ip_subnet +.br +ipsec masktocount \- convert subnet mask to bit count +.br +ipsec networkof \- get base address of an ip_subnet +.br +ipsec maskof \- get subnet mask of an ip_subnet +.SH SYNOPSIS +.B "#include " +.sp +.B "const char *initsubnet(const ip_address *addr," +.ti +1c +.B "int maskbits, int clash, ip_subnet *dst);" +.br +.B "const char *addrtosubnet(const ip_address *addr," +.ti +1c +.B "ip_subnet *dst);" +.sp +.B "int subnettypeof(const ip_subnet *src);" +.br +.B "int masktocount(const ip_address *src);" +.br +.B "void networkof(const ip_subnet *src, ip_address *dst);" +.br +.B "void maskof(const ip_subnet *src, ip_address *dst);" +.SH DESCRIPTION +The +.B +library uses an internal type +.I ip_subnet +to contain a description of an IP subnet +(base address plus mask). +These functions provide basic tools for creating and examining this type. +.PP +.I Initsubnet +initializes a variable +.I *dst +of type +.I ip_subnet +from a base address and +a count of mask bits. +The +.I clash +parameter specifies what to do if the base address includes +.B 1 +bits outside the prefix specified by the mask +(that is, in the ``host number'' part of the address): +.RS +.IP '0' 5 +zero out host-number bits +.IP 'x' +non-zero host-number bits are an error +.RE +.PP +.I Initsubnet +returns +.B NULL +for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.PP +.I Addrtosubnet +initializes an +.I ip_subnet +variable +.I *dst +to a ``singleton subnet'' containing the single address +.IR *addr . +It returns +.B NULL +for success and +a pointer to a string-literal error message for failure. +.PP +.I Subnettypeof +returns the address type of a subnet, +normally +.B AF_INET +or +.BR AF_INET6 . +(The +.B +header file arranges to include the necessary headers for these +names to be known.) +.PP +.I Masktocount +converts a subnet mask, expressed as an address, to a bit count +suitable for use with +.IR initsubnet . +It returns +.B \-1 +for error; see DIAGNOSTICS. +.PP +.I Networkof +fills in +.I *dst +with the base address of subnet +.IR src . +.PP +.I Maskof +fills in +.I *dst +with the subnet mask of subnet +.IR src , +expressed as an address. +.SH SEE ALSO +inet(3), ipsec_ttosubnet(3), ipsec_rangetosubnet(3) +.SH DIAGNOSTICS +Fatal errors in +.I initsubnet +are: +unknown address family; +unknown +.I clash +value; +impossible mask bit count; +non-zero host-number bits and +.I clash +is +.BR 'x' . +Fatal errors in +.I addrtosubnet +are: +unknown address family. +Fatal errors in +.I masktocount +are: +unknown address family; +mask bits not contiguous. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. diff --git a/src/libfreeswan/initsubnet.c b/src/libfreeswan/initsubnet.c new file mode 100644 index 000000000..75ca72f36 --- /dev/null +++ b/src/libfreeswan/initsubnet.c @@ -0,0 +1,95 @@ +/* + * initialize subnet structure + * Copyright (C) 2000, 2002 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: initsubnet.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - initsubnet - initialize ip_subnet from address and count + * + * The only hard part is checking for host-part bits turned on. + */ +err_t /* NULL for success, else string literal */ +initsubnet(addr, count, clash, dst) +const ip_address *addr; +int count; +int clash; /* '0' zero host-part bits, 'x' die on them */ +ip_subnet *dst; +{ + unsigned char *p; + int n; + int c; + unsigned m; + int die; + + dst->addr = *addr; + n = addrbytesptr(&dst->addr, (const unsigned char **)&p); + if (n == 0) + return "unknown address family"; + + switch (clash) { + case '0': + die = 0; + break; + case 'x': + die = 1; + break; + default: + return "unknown clash-control value in initsubnet"; + break; + } + + c = count / 8; + if (c > n) + return "impossible mask count"; + p += c; + n -= c; + + m = 0xff; + c = count % 8; + if (n > 0 && c != 0) /* partial byte */ + m >>= c; + for (; n > 0; n--) { + if ((*p & m) != 0) { + if (die) + return "improper subnet, host-part bits on"; + *p &= ~m; + } + m = 0xff; + p++; + } + + dst->maskbits = count; + return NULL; +} + +/* + - addrtosubnet - initialize ip_subnet from a single address + */ +err_t /* NULL for success, else string literal */ +addrtosubnet(addr, dst) +const ip_address *addr; +ip_subnet *dst; +{ + int n; + + dst->addr = *addr; + n = addrbytesptr(&dst->addr, (const unsigned char **)NULL); + if (n == 0) + return "unknown address family"; + dst->maskbits = n*8; + return NULL; +} diff --git a/src/libfreeswan/internal.h b/src/libfreeswan/internal.h new file mode 100644 index 000000000..16ad78da0 --- /dev/null +++ b/src/libfreeswan/internal.h @@ -0,0 +1,81 @@ +/* + * internal definitions for use within the library; do not export! + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: internal.h,v 1.1 2004/03/15 20:35:26 as Exp $ + */ + +#ifndef ABITS +#define ABITS 32 /* bits in an IPv4 address */ +#endif + +/* case-independent ASCII character equality comparison */ +#define CIEQ(c1, c2) ( ((c1)&~040) == ((c2)&~040) ) + +/* syntax for passthrough SA */ +#ifndef PASSTHROUGHNAME +#define PASSTHROUGHNAME "%passthrough" +#define PASSTHROUGH4NAME "%passthrough4" +#define PASSTHROUGH6NAME "%passthrough6" +#define PASSTHROUGHIS "tun0@0.0.0.0" +#define PASSTHROUGH4IS "tun0@0.0.0.0" +#define PASSTHROUGH6IS "tun0@::" +#define PASSTHROUGHTYPE "tun" +#define PASSTHROUGHSPI 0 +#define PASSTHROUGHDST 0 +#endif + +/* + * Headers, greatly complicated by stupid and unnecessary inconsistencies + * between the user environment and the kernel environment. These are done + * here so that this mess need exist in only one place. + * + * It may seem like a -I or two could avoid most of this, but on closer + * inspection it is not quite that easy. + */ + +/* things that need to come from one place or the other, depending */ +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#define assert(foo) /* nothing */ +#else +#include +#include +#include +#include +#include +#endif + +/* things that exist only in userland */ +#ifndef __KERNEL__ + +/* You'd think this would be okay in the kernel too -- it's just a */ +/* bunch of constants -- but no, in RH5.1 it screws up other things. */ +/* (Credit: Mike Warfield tracked this problem down. Thanks Mike!) */ +/* Fortunately, we don't need it in the kernel subset of the library. */ +#include + +/* header files for things that should never be called in kernel */ +#include + +/* memory allocation, currently user-only, macro-ized just in case */ +#include +#define MALLOC(n) malloc(n) +#define FREE(p) free(p) + +#endif /* __KERNEL__ */ + diff --git a/src/libfreeswan/ipcomp.h b/src/libfreeswan/ipcomp.h new file mode 100644 index 000000000..ed8095517 --- /dev/null +++ b/src/libfreeswan/ipcomp.h @@ -0,0 +1,61 @@ +/* + * IPCOMP zlib interface code. + * Copyright (C) 2000 Svenning Soerensen + * Copyright (C) 2000, 2001 Richard Guy Briggs + * + * This program 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. See . + * + * 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 General Public License + * for more details. + + RCSID $Id: ipcomp.h,v 1.1 2004/03/15 20:35:25 as Exp $ + + */ + +/* SSS */ + +#ifndef _IPCOMP_H +#define _IPCOMP_H + +/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */ +#ifndef IPCOMP_PREFIX +#define IPCOMP_PREFIX +#endif /* IPCOMP_PREFIX */ + +#ifndef IPPROTO_COMP +#define IPPROTO_COMP 108 +#endif /* IPPROTO_COMP */ + +#ifdef CONFIG_IPSEC_DEBUG +extern int sysctl_ipsec_debug_ipcomp; +#endif /* CONFIG_IPSEC_DEBUG */ + +struct ipcomphdr { /* IPCOMP header */ + __u8 ipcomp_nh; /* Next header (protocol) */ + __u8 ipcomp_flags; /* Reserved, must be 0 */ + __u16 ipcomp_cpi; /* Compression Parameter Index */ +}; + +extern struct inet_protocol comp_protocol; +extern int sysctl_ipsec_debug_ipcomp; + +#define IPCOMP_UNCOMPRESSABLE 0x000000001 +#define IPCOMP_COMPRESSIONERROR 0x000000002 +#define IPCOMP_PARMERROR 0x000000004 +#define IPCOMP_DECOMPRESSIONERROR 0x000000008 + +#define IPCOMP_ADAPT_INITIAL_TRIES 8 +#define IPCOMP_ADAPT_INITIAL_SKIP 4 +#define IPCOMP_ADAPT_SUBSEQ_TRIES 2 +#define IPCOMP_ADAPT_SUBSEQ_SKIP 8 + +/* Function prototypes */ +struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags); +struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags); + +#endif /* _IPCOMP_H */ diff --git a/src/libfreeswan/ipsec_ah.h b/src/libfreeswan/ipsec_ah.h new file mode 100644 index 000000000..e088288d3 --- /dev/null +++ b/src/libfreeswan/ipsec_ah.h @@ -0,0 +1,235 @@ +/* + * Authentication Header declarations + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_ah.h,v 1.2 2004/03/22 21:53:18 as Exp $ + */ + +#include "ipsec_md5h.h" +#include "ipsec_sha1.h" + +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif /* IPPROTO_AH */ + +#define AH_FLENGTH 12 /* size of fixed part */ +#define AHMD5_KMAX 64 /* MD5 max 512 bits key */ +#define AHMD5_AMAX 12 /* MD5 96 bits of authenticator */ + +#define AHMD596_KLEN 16 /* MD5 128 bits key */ +#define AHSHA196_KLEN 20 /* SHA1 160 bits key */ + +#define AHMD596_ALEN 16 /* MD5 128 bits authentication length */ +#define AHSHA196_ALEN 20 /* SHA1 160 bits authentication length */ + +#define AHMD596_BLKLEN 64 /* MD5 block length */ +#define AHSHA196_BLKLEN 64 /* SHA1 block length */ +#define AHSHA2_256_BLKLEN 64 /* SHA2-256 block length */ +#define AHSHA2_384_BLKLEN 128 /* SHA2-384 block length (?) */ +#define AHSHA2_512_BLKLEN 128 /* SHA2-512 block length */ + +#define AH_BLKLEN_MAX 128 /* keep up to date! */ + +#define AH_AMAX AHSHA196_ALEN /* keep up to date! */ +#define AHHMAC_HASHLEN 12 /* authenticator length of 96bits */ +#define AHHMAC_RPLLEN 4 /* 32 bit replay counter */ + +#define DB_AH_PKTRX 0x0001 +#define DB_AH_PKTRX2 0x0002 +#define DB_AH_DMP 0x0004 +#define DB_AH_IPSA 0x0010 +#define DB_AH_XF 0x0020 +#define DB_AH_INAU 0x0040 +#define DB_AH_REPLAY 0x0100 + +#ifdef __KERNEL__ + +/* General HMAC algorithm is described in RFC 2104 */ + +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5C + +struct md5_ctx { + MD5_CTX ictx; /* context after H(K XOR ipad) */ + MD5_CTX octx; /* context after H(K XOR opad) */ +}; + +struct sha1_ctx { + SHA1_CTX ictx; /* context after H(K XOR ipad) */ + SHA1_CTX octx; /* context after H(K XOR opad) */ +}; + +struct auth_alg { + void (*init)(void *ctx); + void (*update)(void *ctx, unsigned char *bytes, __u32 len); + void (*final)(unsigned char *hash, void *ctx); + int hashlen; +}; + +extern struct inet_protocol ah_protocol; + +struct options; + +extern int +ah_rcv(struct sk_buff *skb, + struct device *dev, + struct options *opt, + __u32 daddr, + unsigned short len, + __u32 saddr, + int redo, + struct inet_protocol *protocol); + +struct ahhdr /* Generic AH header */ +{ + __u8 ah_nh; /* Next header (protocol) */ + __u8 ah_hl; /* AH length, in 32-bit words */ + __u16 ah_rv; /* reserved, must be 0 */ + __u32 ah_spi; /* Security Parameters Index */ + __u32 ah_rpl; /* Replay prevention */ + __u8 ah_data[AHHMAC_HASHLEN];/* Authentication hash */ +}; +#define AH_BASIC_LEN 8 /* basic AH header is 8 bytes, nh,hl,rv,spi + * and the ah_hl, says how many bytes after that + * to cover. */ + + +#ifdef CONFIG_IPSEC_DEBUG +extern int debug_ah; +#endif /* CONFIG_IPSEC_DEBUG */ +#endif /* __KERNEL__ */ + +/* + * $Log: ipsec_ah.h,v $ + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.4.1 2004/03/16 09:48:18 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.20 2003/02/06 02:21:34 rgb + * + * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h . + * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr". + * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code. + * + * Revision 1.19 2002/09/16 21:19:13 mcr + * fixes for west-ah-icmp-01 - length of AH header must be + * calculated properly, and next_header field properly copied. + * + * Revision 1.18 2002/05/14 02:37:02 rgb + * Change reference from _TDB to _IPSA. + * + * Revision 1.17 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_ah.h,v + * + * Revision 1.16 2002/02/20 01:27:06 rgb + * Ditched a pile of structs only used by the old Netlink interface. + * + * Revision 1.15 2001/12/11 02:35:57 rgb + * Change "struct net_device" to "struct device" for 2.2 compatibility. + * + * Revision 1.14 2001/11/26 09:23:47 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.13.2.1 2001/09/25 02:18:24 mcr + * replace "struct device" with "struct netdevice" + * + * Revision 1.13 2001/06/14 19:35:08 rgb + * Update copyright date. + * + * Revision 1.12 2000/09/12 03:21:20 rgb + * Cleared out unused htonq. + * + * Revision 1.11 2000/09/08 19:12:55 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * + * Revision 1.10 2000/01/21 06:13:10 rgb + * Tidied up spacing. + * Added macros for HMAC padding magic numbers.(kravietz) + * + * Revision 1.9 1999/12/07 18:16:23 rgb + * Fixed comments at end of #endif lines. + * + * Revision 1.8 1999/04/11 00:28:56 henry + * GPL boilerplate + * + * Revision 1.7 1999/04/06 04:54:25 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.6 1999/01/26 02:06:01 rgb + * Removed CONFIG_IPSEC_ALGO_SWITCH macro. + * + * Revision 1.5 1999/01/22 06:17:49 rgb + * Updated macro comments. + * Added context types to support algorithm switch code. + * 64-bit clean-up -- converting 'u long long' to __u64. + * + * Revision 1.4 1998/07/14 15:54:56 rgb + * Add #ifdef __KERNEL__ to protect kernel-only structures. + * + * Revision 1.3 1998/06/30 18:05:16 rgb + * Comment out references to htonq. + * + * Revision 1.2 1998/06/25 19:33:46 rgb + * Add prototype for protocol receive function. + * Rearrange for more logical layout. + * + * Revision 1.1 1998/06/18 21:27:43 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.4 1998/05/18 22:28:43 rgb + * Disable key printing facilities from /proc/net/ipsec_*. + * + * Revision 1.3 1998/04/21 21:29:07 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.2 1998/04/12 22:03:17 rgb + * Updated ESP-3DES-HMAC-MD5-96, + * ESP-DES-HMAC-MD5-96, + * AH-HMAC-MD5-96, + * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository + * from old standards (RFC182[5-9] to new (as of March 1998) drafts. + * + * Fixed eroute references in /proc/net/ipsec*. + * + * Started to patch module unloading memory leaks in ipsec_netlink and + * radij tree unloading. + * + * Revision 1.1 1998/04/09 03:05:55 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:02 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * Added definitions for new AH transforms. + * + * Revision 0.3 1996/11/20 14:35:48 ji + * Minor Cleanup. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/ipsec_alg.h b/src/libfreeswan/ipsec_alg.h new file mode 100644 index 000000000..a393784b1 --- /dev/null +++ b/src/libfreeswan/ipsec_alg.h @@ -0,0 +1,254 @@ +/* + * Modular extensions service and registration functions interface + * + * Author: JuanJo Ciarlante + * + * $Id: ipsec_alg.h,v 1.2 2004/03/22 21:53:18 as Exp $ + * + */ +/* + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ +#ifndef IPSEC_ALG_H +#define IPSEC_ALG_H + +/* + * gcc >= 3.2 has removed __FUNCTION__, replaced by C99 __func__ + * *BUT* its a compiler variable. + */ +#if (__GNUC__ >= 3) +#ifndef __FUNCTION__ +#define __FUNCTION__ __func__ +#endif +#endif + +/* Version 0.8.1-0 */ +#define IPSEC_ALG_VERSION 0x00080100 + +#include +#include +#include +/* + * The following structs are used via pointers in ipsec_alg object to + * avoid ipsec_alg.h coupling with freeswan headers, thus simplifying + * module development + */ +struct ipsec_sa; +struct esp; + +/************************************** + * + * Main registration object + * + *************************************/ +#define IPSEC_ALG_VERSION_QUAD(v) \ + (v>>24),((v>>16)&0xff),((v>>8)&0xff),(v&0xff) +/* + * Main ipsec_alg objects: "OOPrograming wannabe" + * Hierachy (carefully handled with _minimal_ cast'ing): + * + * ipsec_alg+ + * +->ipsec_alg_enc (ixt_alg_type=SADB_EXT_SUPPORTED_ENCRYPT) + * +->ipsec_alg_auth (ixt_alg_type=SADB_EXT_SUPPORTED_AUTH) + */ + +/*************************************************************** + * + * INTERFACE object: struct ipsec_alg + * + ***************************************************************/ + +/* + * common part for every struct ipsec_alg_* + * (sortof poor's man OOP) + */ +#define IPSEC_ALG_STRUCT_COMMON \ + unsigned ixt_version; /* only allow this version (or 'near')*/ \ + struct list_head ixt_list; /* dlinked list */ \ + struct module *ixt_module; /* THIS_MODULE */ \ + unsigned ixt_state; /* state flags */ \ + atomic_t ixt_refcnt; /* ref. count when pointed from ipsec_sa */ \ + char ixt_name[16]; /* descriptive short name, eg. "3des" */ \ + void *ixt_data; /* private for algo implementation */ \ + uint8_t ixt_blocksize; /* blocksize in bytes */ \ + \ + /* THIS IS A COPY of struct supported (lib/pfkey.h) \ + * please keep in sync until we migrate 'supported' stuff \ + * to ipsec_alg \ + */ \ + uint16_t ixt_alg_type; /* correspond to IPSEC_ALG_{ENCRYPT,AUTH} */ \ + uint8_t ixt_alg_id; /* enc. alg. number, eg. ESP_3DES */ \ + uint8_t ixt_ivlen; /* ivlen in bits, expected to be multiple of 8! */ \ + uint16_t ixt_keyminbits;/* min. keybits (of entropy) */ \ + uint16_t ixt_keymaxbits;/* max. keybits (of entropy) */ + +#define ixt_support ixt_alg_type + +#define IPSEC_ALG_ST_SUPP 0x01 +#define IPSEC_ALG_ST_REGISTERED 0x02 +#define IPSEC_ALG_ST_EXCL 0x04 +struct ipsec_alg { + IPSEC_ALG_STRUCT_COMMON +}; +/* + * Note the const in cbc_encrypt IV arg: + * some ciphers like to toast passed IV (eg. 3DES): make a local IV copy + */ +struct ipsec_alg_enc { + IPSEC_ALG_STRUCT_COMMON + unsigned ixt_e_keylen; /* raw key length in bytes */ + unsigned ixt_e_ctx_size; /* sa_p->key_e_size */ + int (*ixt_e_set_key)(struct ipsec_alg_enc *alg, __u8 *key_e, const __u8 *key, size_t keysize); + __u8 *(*ixt_e_new_key)(struct ipsec_alg_enc *alg, const __u8 *key, size_t keysize); + void (*ixt_e_destroy_key)(struct ipsec_alg_enc *alg, __u8 *key_e); + int (*ixt_e_cbc_encrypt)(struct ipsec_alg_enc *alg, __u8 *key_e, __u8 *in, int ilen, const __u8 *iv, int encrypt); +}; +struct ipsec_alg_auth { + IPSEC_ALG_STRUCT_COMMON + unsigned ixt_a_keylen; /* raw key length in bytes */ + unsigned ixt_a_ctx_size; /* sa_p->key_a_size */ + unsigned ixt_a_authlen; /* 'natural' auth. hash len (bytes) */ + int (*ixt_a_hmac_set_key)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *key, int keylen); + int (*ixt_a_hmac_hash)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *dat, int len, __u8 *hash, int hashlen); +}; +/* + * These are _copies_ of SADB_EXT_SUPPORTED_{AUTH,ENCRYPT}, + * to avoid header coupling for true constants + * about headers ... "cp is your friend" --Linus + */ +#define IPSEC_ALG_TYPE_AUTH 14 +#define IPSEC_ALG_TYPE_ENCRYPT 15 + +/*************************************************************** + * + * INTERFACE for module loading,testing, and unloading + * + ***************************************************************/ +/* - registration calls */ +int register_ipsec_alg(struct ipsec_alg *); +int unregister_ipsec_alg(struct ipsec_alg *); +/* - optional (simple test) for algos */ +int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int testparm); +/* inline wrappers (usefull for type validation */ +static inline int register_ipsec_alg_enc(struct ipsec_alg_enc *ixt) { + return register_ipsec_alg((struct ipsec_alg*)ixt); +} +static inline int unregister_ipsec_alg_enc(struct ipsec_alg_enc *ixt) { + return unregister_ipsec_alg((struct ipsec_alg*)ixt); +} +static inline int register_ipsec_alg_auth(struct ipsec_alg_auth *ixt) { + return register_ipsec_alg((struct ipsec_alg*)ixt); +} +static inline int unregister_ipsec_alg_auth(struct ipsec_alg_auth *ixt) { + return unregister_ipsec_alg((struct ipsec_alg*)ixt); +} + +/***************************************************************** + * + * INTERFACE for ENC services: key creation, encrypt function + * + *****************************************************************/ + +#define IPSEC_ALG_ENCRYPT 1 +#define IPSEC_ALG_DECRYPT 0 + +/* encryption key context creation function */ +int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p); +/* + * ipsec_alg_esp_encrypt(): encrypt ilen bytes in idat returns + * 0 or ERR<0 + */ +int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 *idat, int ilen, const __u8 *iv, int action); + +/*************************************************************** + * + * INTERFACE for AUTH services: key creation, hash functions + * + ***************************************************************/ +int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p); +int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) ; +#define ipsec_alg_sa_esp_update(c,k,l) ipsec_alg_sa_esp_hash(c,k,l,NULL,0) + +/* only called from ipsec_init.c */ +int ipsec_alg_init(void); + +/* algo module glue for static algos */ +void ipsec_alg_static_init(void); +typedef int (*ipsec_alg_init_func_t) (void); + +/********************************************** + * + * INTERFACE for ipsec_sa init and wipe + * + **********************************************/ + +/* returns true if ipsec_sa has ipsec_alg obj attached */ +/* + * Initializes ipsec_sa's ipsec_alg object, using already loaded + * proto, authalg, encalg.; links ipsec_alg objects (enc, auth) + */ +int ipsec_alg_sa_init(struct ipsec_sa *sa_p); +/* + * Destroys ipsec_sa's ipsec_alg object + * unlinking ipsec_alg objects + */ +int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p); + +/********************************************** + * + * 2.2 backport for some 2.4 useful module stuff + * + **********************************************/ +#ifdef MODULE +#ifndef THIS_MODULE +#define THIS_MODULE (&__this_module) +#endif +#ifndef module_init +typedef int (*__init_module_func_t)(void); +typedef void (*__cleanup_module_func_t)(void); + +#define module_init(x) \ + int init_module(void) __attribute__((alias(#x))); \ + static inline __init_module_func_t __init_module_inline(void) \ + { return x; } +#define module_exit(x) \ + void cleanup_module(void) __attribute__((alias(#x))); \ + static inline __cleanup_module_func_t __cleanup_module_inline(void) \ + { return x; } +#endif + +#define IPSEC_ALG_MODULE_INIT( func_name ) \ + static int func_name(void); \ + module_init(func_name); \ + static int __init func_name(void) +#define IPSEC_ALG_MODULE_EXIT( func_name ) \ + static void func_name(void); \ + module_exit(func_name); \ + static void __exit func_name(void) +#else /* not MODULE */ +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif +/* + * I only want module_init() magic + * when algo.c file *is THE MODULE*, in all other + * cases, initialization is called explicitely from ipsec_alg_init() + */ +#define IPSEC_ALG_MODULE_INIT( func_name ) \ + extern int func_name(void); \ + int func_name(void) +#define IPSEC_ALG_MODULE_EXIT( func_name ) \ + extern void func_name(void); \ + void func_name(void) +#endif + +#endif /* IPSEC_ALG_H */ diff --git a/src/libfreeswan/ipsec_encap.h b/src/libfreeswan/ipsec_encap.h new file mode 100644 index 000000000..17cd69269 --- /dev/null +++ b/src/libfreeswan/ipsec_encap.h @@ -0,0 +1,143 @@ +/* + * declarations relevant to encapsulation-like operations + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_encap.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + +#ifndef _IPSEC_ENCAP_H_ + +#define SENT_IP4 16 /* data is two struct in_addr + proto + ports*/ + /* (2 * sizeof(struct in_addr)) */ + /* sizeof(struct sockaddr_encap) + - offsetof(struct sockaddr_encap, Sen.Sip4.Src) */ + +struct sockaddr_encap +{ + __u8 sen_len; /* length */ + __u8 sen_family; /* AF_ENCAP */ + __u16 sen_type; /* see SENT_* */ + union + { + struct /* SENT_IP4 */ + { + struct in_addr Src; + struct in_addr Dst; + __u8 Proto; + __u16 Sport; + __u16 Dport; + } Sip4; + } Sen; +}; + +#define sen_ip_src Sen.Sip4.Src +#define sen_ip_dst Sen.Sip4.Dst +#define sen_proto Sen.Sip4.Proto +#define sen_sport Sen.Sip4.Sport +#define sen_dport Sen.Sip4.Dport + +#ifndef AF_ENCAP +#define AF_ENCAP 26 +#endif /* AF_ENCAP */ + +#define _IPSEC_ENCAP_H_ +#endif /* _IPSEC_ENCAP_H_ */ + +/* + * $Log: ipsec_encap.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.17 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_encap.h,v + * + * Revision 1.16 2001/11/26 09:23:47 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.15.2.1 2001/09/25 02:18:54 mcr + * struct eroute moved to ipsec_eroute.h + * + * Revision 1.15 2001/09/14 16:58:36 rgb + * Added support for storing the first and last packets through a HOLD. + * + * Revision 1.14 2001/09/08 21:13:31 rgb + * Added pfkey ident extension support for ISAKMPd. (NetCelo) + * + * Revision 1.13 2001/06/14 19:35:08 rgb + * Update copyright date. + * + * Revision 1.12 2001/05/27 06:12:10 rgb + * Added structures for pid, packet count and last access time to eroute. + * Added packet count to beginning of /proc/net/ipsec_eroute. + * + * Revision 1.11 2000/09/08 19:12:56 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * + * Revision 1.10 2000/03/22 16:15:36 rgb + * Fixed renaming of dev_get (MB). + * + * Revision 1.9 2000/01/21 06:13:26 rgb + * Added a macro for AF_ENCAP + * + * Revision 1.8 1999/12/31 14:56:55 rgb + * MB fix for 2.3 dev-use-count. + * + * Revision 1.7 1999/11/18 04:09:18 rgb + * Replaced all kernel version macros to shorter, readable form. + * + * Revision 1.6 1999/09/24 00:34:13 rgb + * Add Marc Boucher's support for 2.3.xx+. + * + * Revision 1.5 1999/04/11 00:28:57 henry + * GPL boilerplate + * + * Revision 1.4 1999/04/06 04:54:25 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.3 1998/10/19 14:44:28 rgb + * Added inclusion of freeswan.h. + * sa_id structure implemented and used: now includes protocol. + * + * Revision 1.2 1998/07/14 18:19:33 rgb + * Added #ifdef __KERNEL__ directives to restrict scope of header. + * + * Revision 1.1 1998/06/18 21:27:44 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.2 1998/04/21 21:29:10 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.1 1998/04/09 03:05:58 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:02 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * Minor cosmetic changes. + * + * Revision 0.3 1996/11/20 14:35:48 ji + * Minor Cleanup. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/ipsec_eroute.h b/src/libfreeswan/ipsec_eroute.h new file mode 100644 index 000000000..2ee2a10b8 --- /dev/null +++ b/src/libfreeswan/ipsec_eroute.h @@ -0,0 +1,103 @@ +/* + * @(#) declarations of eroute structures + * + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs + * Copyright (C) 2001 Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_eroute.h,v 1.1 2004/03/15 20:35:25 as Exp $ + * + * derived from ipsec_encap.h 1.15 on 2001/9/18 by mcr. + * + */ + +#ifndef _IPSEC_EROUTE_H_ + +#include "radij.h" +#include "ipsec_encap.h" +#include "ipsec_radij.h" + +/* + * The "type" is really part of the address as far as the routing + * system is concerned. By using only one bit in the type field + * for each type, we sort-of make sure that different types of + * encapsulation addresses won't be matched against the wrong type. + */ + +/* + * An entry in the radix tree + */ + +struct rjtentry +{ + struct radij_node rd_nodes[2]; /* tree glue, and other values */ +#define rd_key(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_key)) +#define rd_mask(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_mask)) + short rd_flags; + short rd_count; +}; + +struct ident +{ + __u16 type; /* identity type */ + __u64 id; /* identity id */ + __u8 len; /* identity len */ + caddr_t data; /* identity data */ +}; + +/* + * An encapsulation route consists of a pointer to a + * radix tree entry and a SAID (a destination_address/SPI/protocol triple). + */ + +struct eroute +{ + struct rjtentry er_rjt; + struct sa_id er_said; + uint32_t er_pid; + uint32_t er_count; + uint64_t er_lasttime; + struct sockaddr_encap er_eaddr; /* MCR get rid of _encap, it is silly*/ + struct sockaddr_encap er_emask; + struct ident er_ident_s; + struct ident er_ident_d; + struct sk_buff* er_first; + struct sk_buff* er_last; +}; + +#define er_dst er_said.dst +#define er_spi er_said.spi + +#define _IPSEC_EROUTE_H_ +#endif /* _IPSEC_EROUTE_H_ */ + +/* + * $Log: ipsec_eroute.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.3 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_eroute.h,v + * + * Revision 1.2 2001/11/26 09:16:13 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.1.2.1 2001/09/25 02:18:54 mcr + * struct eroute moved to ipsec_eroute.h + * + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/ipsec_errs.h b/src/libfreeswan/ipsec_errs.h new file mode 100644 index 000000000..f14b5e675 --- /dev/null +++ b/src/libfreeswan/ipsec_errs.h @@ -0,0 +1,53 @@ +/* + * @(#) definition of ipsec_errs structure + * + * Copyright (C) 2001 Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_errs.h,v 1.1 2004/03/15 20:35:25 as Exp $ + * + */ + +/* + * This file describes the errors/statistics that FreeSWAN collects. + * + */ + +struct ipsec_errs { + __u32 ips_alg_errs; /* number of algorithm errors */ + __u32 ips_auth_errs; /* # of authentication errors */ + __u32 ips_encsize_errs; /* # of encryption size errors*/ + __u32 ips_encpad_errs; /* # of encryption pad errors*/ + __u32 ips_replaywin_errs; /* # of pkt sequence errors */ +}; + +/* + * $Log: ipsec_errs.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.3 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_errs.h,v + * + * Revision 1.2 2001/11/26 09:16:13 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.1.2.1 2001/09/25 02:25:57 mcr + * lifetime structure created and common functions created. + * + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/ipsec_esp.h b/src/libfreeswan/ipsec_esp.h new file mode 100644 index 000000000..c7d5ea15d --- /dev/null +++ b/src/libfreeswan/ipsec_esp.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_esp.h,v 1.2 2004/03/22 21:53:18 as Exp $ + */ + +#include "freeswan/ipsec_md5h.h" +#include "freeswan/ipsec_sha1.h" + +#include "crypto/des.h" + +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif /* IPPROTO_ESP */ + +#define ESP_HEADER_LEN 8 /* 64 bits header (spi+rpl)*/ + +#define EMT_ESPDESCBC_ULEN 20 /* coming from user mode */ +#define EMT_ESPDES_KMAX 64 /* 512 bit secret key enough? */ +#define EMT_ESPDES_KEY_SZ 8 /* 56 bit secret key with parity = 64 bits */ +#define EMT_ESP3DES_KEY_SZ 24 /* 168 bit secret key with parity = 192 bits */ +#define EMT_ESPDES_IV_SZ 8 /* IV size */ +#define ESP_DESCBC_BLKLEN 8 /* DES-CBC block size */ + +#define ESP_IV_MAXSZ 16 /* This is _critical_ */ +#define ESP_IV_MAXSZ_INT (ESP_IV_MAXSZ/sizeof(int)) + +#define DB_ES_PKTRX 0x0001 +#define DB_ES_PKTRX2 0x0002 +#define DB_ES_IPSA 0x0010 +#define DB_ES_XF 0x0020 +#define DB_ES_IPAD 0x0040 +#define DB_ES_INAU 0x0080 +#define DB_ES_OINFO 0x0100 +#define DB_ES_OINFO2 0x0200 +#define DB_ES_OH 0x0400 +#define DB_ES_REPLAY 0x0800 + +#ifdef __KERNEL__ +struct des_eks { + des_key_schedule ks; +}; + +extern struct inet_protocol esp_protocol; + +struct options; + +extern int +esp_rcv(struct sk_buff *skb, + struct device *dev, + struct options *opt, + __u32 daddr, + unsigned short len, + __u32 saddr, + int redo, + struct inet_protocol *protocol); + +/* Only for 64 bits IVs, eg. ESP_3DES :P */ +struct esphdr +{ + __u32 esp_spi; /* Security Parameters Index */ + __u32 esp_rpl; /* Replay counter */ + __u8 esp_iv[8]; /* iv */ +}; + +#ifdef CONFIG_IPSEC_DEBUG +extern int debug_esp; +#endif /* CONFIG_IPSEC_DEBUG */ +#endif /* __KERNEL__ */ + +/* + * $Log: ipsec_esp.h,v $ + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.4.1 2004/03/16 09:48:18 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.21 2003/02/06 02:21:34 rgb + * + * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h . + * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr". + * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code. + * + * Revision 1.20 2002/05/14 02:37:02 rgb + * Change reference from _TDB to _IPSA. + * + * Revision 1.19 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.18 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_esp.h,v + * + * Revision 1.17 2002/02/20 01:27:07 rgb + * Ditched a pile of structs only used by the old Netlink interface. + * + * Revision 1.16 2001/12/11 02:35:57 rgb + * Change "struct net_device" to "struct device" for 2.2 compatibility. + * + * Revision 1.15 2001/11/26 09:23:48 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.14.2.3 2001/10/23 04:16:42 mcr + * get definition of des_key_schedule from des.h + * + * Revision 1.14.2.2 2001/10/22 20:33:13 mcr + * use "des_key_schedule" structure instead of cooking our own. + * + * Revision 1.14.2.1 2001/09/25 02:18:25 mcr + * replace "struct device" with "struct netdevice" + * + * Revision 1.14 2001/06/14 19:35:08 rgb + * Update copyright date. + * + * Revision 1.13 2000/09/08 19:12:56 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * + * Revision 1.12 2000/08/01 14:51:50 rgb + * Removed _all_ remaining traces of DES. + * + * Revision 1.11 2000/01/10 16:36:20 rgb + * Ditch last of EME option flags, including initiator. + * + * Revision 1.10 1999/12/07 18:16:22 rgb + * Fixed comments at end of #endif lines. + * + * Revision 1.9 1999/04/11 00:28:57 henry + * GPL boilerplate + * + * Revision 1.8 1999/04/06 04:54:25 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.7 1999/01/26 02:06:00 rgb + * Removed CONFIG_IPSEC_ALGO_SWITCH macro. + * + * Revision 1.6 1999/01/22 15:22:05 rgb + * Re-enable IV in the espblkrply_edata structure to avoid breaking pluto + * until pluto can be fixed properly. + * + * Revision 1.5 1999/01/22 06:18:16 rgb + * Updated macro comments. + * Added key schedule types to support algorithm switch code. + * + * Revision 1.4 1998/08/12 00:07:32 rgb + * Added data structures for new xforms: null, {,3}dessha1. + * + * Revision 1.3 1998/07/14 15:57:01 rgb + * Add #ifdef __KERNEL__ to protect kernel-only structures. + * + * Revision 1.2 1998/06/25 19:33:46 rgb + * Add prototype for protocol receive function. + * Rearrange for more logical layout. + * + * Revision 1.1 1998/06/18 21:27:45 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.6 1998/06/05 02:28:08 rgb + * Minor comment fix. + * + * Revision 1.5 1998/05/27 22:34:00 rgb + * Changed structures to accomodate key separation. + * + * Revision 1.4 1998/05/18 22:28:43 rgb + * Disable key printing facilities from /proc/net/ipsec_*. + * + * Revision 1.3 1998/04/21 21:29:07 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.2 1998/04/12 22:03:20 rgb + * Updated ESP-3DES-HMAC-MD5-96, + * ESP-DES-HMAC-MD5-96, + * AH-HMAC-MD5-96, + * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository + * from old standards (RFC182[5-9] to new (as of March 1998) drafts. + * + * Fixed eroute references in /proc/net/ipsec*. + * + * Started to patch module unloading memory leaks in ipsec_netlink and + * radij tree unloading. + * + * Revision 1.1 1998/04/09 03:06:00 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:02 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.5 1997/06/03 04:24:48 ji + * Added ESP-3DES-MD5-96 transform. + * + * Revision 0.4 1997/01/15 01:28:15 ji + * Added definitions for new ESP transforms. + * + * Revision 0.3 1996/11/20 14:35:48 ji + * Minor Cleanup. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/ipsec_ipe4.h b/src/libfreeswan/ipsec_ipe4.h new file mode 100644 index 000000000..73b6ae899 --- /dev/null +++ b/src/libfreeswan/ipsec_ipe4.h @@ -0,0 +1,68 @@ +/* + * IP-in-IP Header declarations + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_ipe4.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + +/* The packet header is an IP header! */ + +struct ipe4_xdata /* transform table data */ +{ + struct in_addr i4_src; + struct in_addr i4_dst; +}; + +#define EMT_IPE4_ULEN 8 /* coming from user mode */ + + +/* + * $Log: ipsec_ipe4.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.5 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_ipe4.h,v + * + * Revision 1.4 2001/06/14 19:35:08 rgb + * Update copyright date. + * + * Revision 1.3 1999/04/11 00:28:57 henry + * GPL boilerplate + * + * Revision 1.2 1999/04/06 04:54:25 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.1 1998/06/18 21:27:47 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.1 1998/04/09 03:06:07 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:03 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * No changes. + * + * Revision 0.3 1996/11/20 14:48:53 ji + * Release update only. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/ipsec_kversion.h b/src/libfreeswan/ipsec_kversion.h new file mode 100644 index 000000000..7bf56ac7f --- /dev/null +++ b/src/libfreeswan/ipsec_kversion.h @@ -0,0 +1,227 @@ +#ifndef _FREESWAN_KVERSIONS_H +/* + * header file for FreeS/WAN library functions + * Copyright (C) 1998, 1999, 2000 Henry Spencer. + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ipsec_kversion.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ +#define _FREESWAN_KVERSIONS_H /* seen it, no need to see it again */ + +/* + * this file contains a series of atomic defines that depend upon + * kernel version numbers. The kernel versions are arranged + * in version-order number (which is often not chronological) + * and each clause enables or disables a feature. + */ + +/* + * First, assorted kernel-version-dependent trickery. + */ +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +#define HEADER_CACHE_BIND_21 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#define SPINLOCK +#define PROC_FS_21 +#define NETLINK_SOCK +#define NET_21 +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,19) +#define net_device_stats enet_statistics +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +#define SPINLOCK_23 +#define NETDEV_23 +# ifndef CONFIG_IP_ALIAS +# define CONFIG_IP_ALIAS +# endif +#include +#include +#include +# ifdef NETLINK_XFRM +# define NETDEV_25 +# endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,25) +#define PROC_FS_2325 +#undef PROC_FS_21 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30) +#define PROC_NO_DUMMY +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,35) +#define SKB_COPY_EXPAND +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,37) +#define IP_SELECT_IDENT +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,50)) && defined(CONFIG_NETFILTER) +#define SKB_RESET_NFCT +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,2) +#define IP_SELECT_IDENT_NEW +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) +#define IPH_is_SKB_PULLED +#define SKB_COW_NEW +#define PROTO_HANDLER_SINGLE_PARM +#define IP_FRAGMENT_LINEARIZE 1 +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */ +# ifdef REDHAT_BOGOSITY +# define IP_SELECT_IDENT_NEW +# define IPH_is_SKB_PULLED +# define SKB_COW_NEW +# define PROTO_HANDLER_SINGLE_PARM +# endif /* REDHAT_BOGOSITY */ +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) +#define MALLOC_SLAB +#define LINUX_KERNEL_HAS_SNPRINTF +#endif + +#ifdef NET_21 +# include +#else + /* old kernel in.h has some IPv6 stuff, but not quite enough */ +# define s6_addr16 s6_addr +# define AF_INET6 10 +# define uint8_t __u8 +# define uint16_t __u16 +# define uint32_t __u32 +# define uint64_t __u64 +#endif + +#ifdef NET_21 +# define ipsec_kfree_skb(a) kfree_skb(a) +#else /* NET_21 */ +# define ipsec_kfree_skb(a) kfree_skb(a, FREE_WRITE) +#endif /* NET_21 */ + +#ifdef NETDEV_23 +# define device net_device +# define ipsec_dev_get dev_get_by_name +# define __ipsec_dev_get __dev_get_by_name +# define ipsec_dev_put(x) dev_put(x) +# define __ipsec_dev_put(x) __dev_put(x) +# define ipsec_dev_hold(x) dev_hold(x) +#else /* NETDEV_23 */ +# define ipsec_dev_get dev_get +# define __ipsec_dev_put(x) +# define ipsec_dev_put(x) +# define ipsec_dev_hold(x) +#endif /* NETDEV_23 */ + +#ifndef SPINLOCK +# include + /* simulate spin locks and read/write locks */ + typedef struct { + volatile char lock; + } spinlock_t; + + typedef struct { + volatile unsigned int lock; + } rwlock_t; + +# define spin_lock_init(x) { (x)->lock = 0;} +# define rw_lock_init(x) { (x)->lock = 0; } + +# define spin_lock(x) { while ((x)->lock) barrier(); (x)->lock=1;} +# define spin_lock_irq(x) { cli(); spin_lock(x);} +# define spin_lock_irqsave(x,flags) { save_flags(flags); spin_lock_irq(x);} + +# define spin_unlock(x) { (x)->lock=0;} +# define spin_unlock_irq(x) { spin_unlock(x); sti();} +# define spin_unlock_irqrestore(x,flags) { spin_unlock(x); restore_flags(flags);} + +# define read_lock(x) spin_lock(x) +# define read_lock_irq(x) spin_lock_irq(x) +# define read_lock_irqsave(x,flags) spin_lock_irqsave(x,flags) + +# define read_unlock(x) spin_unlock(x) +# define read_unlock_irq(x) spin_unlock_irq(x) +# define read_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags) + +# define write_lock(x) spin_lock(x) +# define write_lock_irq(x) spin_lock_irq(x) +# define write_lock_irqsave(x,flags) spin_lock_irqsave(x,flags) + +# define write_unlock(x) spin_unlock(x) +# define write_unlock_irq(x) spin_unlock_irq(x) +# define write_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags) +#endif /* !SPINLOCK */ + +#ifndef SPINLOCK_23 +# define spin_lock_bh(x) spin_lock_irq(x) +# define spin_unlock_bh(x) spin_unlock_irq(x) + +# define read_lock_bh(x) read_lock_irq(x) +# define read_unlock_bh(x) read_unlock_irq(x) + +# define write_lock_bh(x) write_lock_irq(x) +# define write_unlock_bh(x) write_unlock_irq(x) +#endif /* !SPINLOCK_23 */ + +#endif /* _FREESWAN_KVERSIONS_H */ + +/* + * $Log: ipsec_kversion.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.7 2003/07/31 22:48:08 mcr + * derive NET25-ness from presence of NETLINK_XFRM macro. + * + * Revision 1.6 2003/06/24 20:22:32 mcr + * added new global: ipsecdevices[] so that we can keep track of + * the ipsecX devices. They will be referenced with dev_hold(), + * so 2.2 may need this as well. + * + * Revision 1.5 2003/04/03 17:38:09 rgb + * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. + * + * Revision 1.4 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_kversion.h,v + * + * Revision 1.3 2002/04/12 03:21:17 mcr + * three parameter version of ip_select_ident appears first + * in 2.4.2 (RH7.1) not 2.4.4. + * + * Revision 1.2 2002/03/08 21:35:22 rgb + * Defined LINUX_KERNEL_HAS_SNPRINTF to shut up compiler warnings after + * 2.4.9. (Andreas Piesk). + * + * Revision 1.1 2002/01/29 02:11:42 mcr + * removal of kversions.h - sources that needed it now use ipsec_param.h. + * updating of IPv6 structures to match latest in6.h version. + * removed dead code from freeswan.h that also duplicated kversions.h + * code. + * + * + */ diff --git a/src/libfreeswan/ipsec_life.h b/src/libfreeswan/ipsec_life.h new file mode 100644 index 000000000..4cf270272 --- /dev/null +++ b/src/libfreeswan/ipsec_life.h @@ -0,0 +1,112 @@ +/* + * Definitions relevant to IPSEC lifetimes + * Copyright (C) 2001 Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_life.h,v 1.1 2004/03/15 20:35:25 as Exp $ + * + * This file derived from ipsec_xform.h on 2001/9/18 by mcr. + * + */ + +/* + * This file describes the book keeping fields for the + * IPsec Security Association Structure. ("ipsec_sa") + * + * This structure is never allocated directly by kernel code, + * (it is always a static/auto or is part of a structure) + * so it does not have a reference count. + * + */ + +#ifndef _IPSEC_LIFE_H_ + +/* + * _count is total count. + * _hard is hard limit (kill SA after this number) + * _soft is soft limit (try to renew SA after this number) + * _last is used in some special cases. + * + */ + +struct ipsec_lifetime64 +{ + __u64 ipl_count; + __u64 ipl_soft; + __u64 ipl_hard; + __u64 ipl_last; +}; + +struct ipsec_lifetimes +{ + /* number of bytes processed */ + struct ipsec_lifetime64 ipl_bytes; + + /* number of packets processed */ + struct ipsec_lifetime64 ipl_packets; + + /* time since SA was added */ + struct ipsec_lifetime64 ipl_addtime; + + /* time since SA was first used */ + struct ipsec_lifetime64 ipl_usetime; + + /* from rfc2367: + * For CURRENT, the number of different connections, + * endpoints, or flows that the association has been + * allocated towards. For HARD and SOFT, the number of + * these the association may be allocated towards + * before it expires. The concept of a connection, + * flow, or endpoint is system specific. + * + * mcr(2001-9-18) it is unclear what purpose these serve for FreeSWAN. + * They are maintained for PF_KEY compatibility. + */ + struct ipsec_lifetime64 ipl_allocations; +}; + +enum ipsec_life_alive { + ipsec_life_harddied = -1, + ipsec_life_softdied = 0, + ipsec_life_okay = 1 +}; + +enum ipsec_life_type { + ipsec_life_timebased = 1, + ipsec_life_countbased= 0 +}; + +#define _IPSEC_LIFE_H_ +#endif /* _IPSEC_LIFE_H_ */ + + +/* + * $Log: ipsec_life.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.3 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_life.h,v + * + * Revision 1.2 2001/11/26 09:16:14 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.1.2.1 2001/09/25 02:25:58 mcr + * lifetime structure created and common functions created. + * + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/ipsec_md5h.h b/src/libfreeswan/ipsec_md5h.h new file mode 100644 index 000000000..3fc54bc82 --- /dev/null +++ b/src/libfreeswan/ipsec_md5h.h @@ -0,0 +1,140 @@ +/* + * RCSID $Id: ipsec_md5h.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + +/* + * The rest of this file is Copyright RSA DSI. See the following comments + * for the full Copyright notice. + */ + +#ifndef _IPSEC_MD5H_H_ +#define _IPSEC_MD5H_H_ + +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 1 +#endif /* !PROTOTYPES */ + +/* POINTER defines a generic pointer type */ +typedef __u8 *POINTER; + +/* UINT2 defines a two byte word */ +typedef __u16 UINT2; + +/* UINT4 defines a four byte word */ +typedef __u32 UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ + +#if PROTOTYPES +#define PROTO_LIST(list) list +#else /* PROTOTYPES */ +#define PROTO_LIST(list) () +#endif /* PROTOTYPES */ + + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((void *)); +void MD5Update PROTO_LIST + ((void *, unsigned char *, __u32)); +void MD5Final PROTO_LIST ((unsigned char [16], void *)); + +#endif /* _IPSEC_MD5H_H_ */ + +/* + * $Log: ipsec_md5h.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.8 2002/09/10 01:45:09 mcr + * changed type of MD5_CTX and SHA1_CTX to void * so that + * the function prototypes would match, and could be placed + * into a pointer to a function. + * + * Revision 1.7 2002/04/24 07:36:46 mcr + * Moved from ./klips/net/ipsec/ipsec_md5h.h,v + * + * Revision 1.6 1999/12/13 13:59:13 rgb + * Quick fix to argument size to Update bugs. + * + * Revision 1.5 1999/12/07 18:16:23 rgb + * Fixed comments at end of #endif lines. + * + * Revision 1.4 1999/04/06 04:54:26 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.3 1999/01/22 06:19:58 rgb + * 64-bit clean-up. + * + * Revision 1.2 1998/11/30 13:22:54 rgb + * Rationalised all the klips kernel file headers. They are much shorter + * now and won't conflict under RH5.2. + * + * Revision 1.1 1998/06/18 21:27:48 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.2 1998/04/23 20:54:03 rgb + * Fixed md5 and sha1 include file nesting issues, to be cleaned up when + * verified. + * + * Revision 1.1 1998/04/09 03:04:21 henry + * sources moved up from linux/net/ipsec + * these two include files modified not to include others except in kernel + * + * Revision 1.1.1.1 1998/04/08 05:35:03 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * No changes. + * + * Revision 0.3 1996/11/20 14:48:53 ji + * Release update only. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/ipsec_param.h b/src/libfreeswan/ipsec_param.h new file mode 100644 index 000000000..02b36e6a3 --- /dev/null +++ b/src/libfreeswan/ipsec_param.h @@ -0,0 +1,226 @@ +/* + * @(#) FreeSWAN tunable paramaters + * + * Copyright (C) 2001 Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_param.h,v 1.2 2004/04/28 08:07:11 as Exp $ + * + */ + +/* + * This file provides a set of #define's which may be tuned by various + * people/configurations. It keeps all compile-time tunables in one place. + * + * This file should be included before all other IPsec kernel-only files. + * + */ + +#ifndef _IPSEC_PARAM_H_ + +#ifdef __KERNEL__ +#include "ipsec_kversion.h" + +/* Set number of ipsecX virtual devices here. */ +/* This must be < exp(field width of IPSEC_DEV_FORMAT) */ +/* It must also be reasonable so as not to overload the memory and CPU */ +/* constraints of the host. */ +#define IPSEC_NUM_IF 4 +/* The field width must be < IF_NAM_SIZ - strlen("ipsec") - 1. */ +/* With "ipsec" being 5 characters, that means 10 is the max field width */ +/* but machine memory and CPU constraints are not likely to tollerate */ +/* more than 3 digits. The default is one digit. */ +/* Update: userland scripts get upset if they can't find "ipsec0", so */ +/* for now, no "0"-padding should be used (which would have been helpful */ +/* to make text-searches work */ +#define IPSEC_DEV_FORMAT "ipsec%d" +/* For, say, 500 virtual ipsec devices, I would recommend: */ +/* #define IPSEC_NUM_IF 500 */ +/* #define IPSEC_DEV_FORMAT "ipsec%03d" */ +/* Note that the "interfaces=" line in /etc/ipsec.conf would be, um, challenging. */ + +/* use dynamic ipsecX device allocation */ +#ifndef CONFIG_IPSEC_DYNDEV +#define CONFIG_IPSEC_DYNDEV 1 +#endif /* CONFIG_IPSEC_DYNDEV */ + + +#ifdef CONFIG_IPSEC_BIGGATE +# define SADB_HASHMOD 8069 +#else /* CONFIG_IPSEC_BIGGATE */ +# define SADB_HASHMOD 257 +#endif /* CONFIG_IPSEC_BIGGATE */ +#endif /* __KERNEL__ */ + +/* + * This is for the SA reference table. This number is related to the + * maximum number of SAs that KLIPS can concurrently deal with, plus enough + * space for keeping expired SAs around. + * + * TABLE_MAX_WIDTH is the number of bits that we will use. + * MAIN_TABLE_WIDTH is the number of bits used for the primary index table. + * + */ +#ifndef IPSEC_SA_REF_TABLE_IDX_WIDTH +# define IPSEC_SA_REF_TABLE_IDX_WIDTH 16 +#endif + +#ifndef IPSEC_SA_REF_MAINTABLE_IDX_WIDTH +# define IPSEC_SA_REF_MAINTABLE_IDX_WIDTH 4 +#endif + +#ifndef IPSEC_SA_REF_FREELIST_NUM_ENTRIES +# define IPSEC_SA_REF_FREELIST_NUM_ENTRIES 256 +#endif + +#ifndef IPSEC_SA_REF_CODE +# define IPSEC_SA_REF_CODE 1 +#endif + +#ifdef __KERNEL__ +/* This is defined for 2.4, but not 2.2.... */ +#ifndef ARPHRD_VOID +# define ARPHRD_VOID 0xFFFF +#endif + +/* + * Worry about PROC_FS stuff + */ +#if defined(PROC_FS_2325) +/* kernel 2.4 */ +# define IPSEC_PROC_LAST_ARG ,int *eof,void *data +# define IPSEC_PROCFS_DEBUG_NO_STATIC +# define IPSEC_PROC_SUBDIRS +#else +/* kernel <2.4 */ +# define IPSEC_PROCFS_DEBUG_NO_STATIC DEBUG_NO_STATIC + +# ifndef PROC_NO_DUMMY +# define IPSEC_PROC_LAST_ARG , int dummy +# else +# define IPSEC_PROC_LAST_ARG +# endif /* !PROC_NO_DUMMY */ +#endif /* PROC_FS_2325 */ + +#if !defined(LINUX_KERNEL_HAS_SNPRINTF) +/* GNU CPP specific! */ +# define snprintf(buf, len, fmt...) sprintf(buf, ##fmt) +#endif /* !LINUX_KERNEL_HAS_SNPRINTF */ + +#ifdef SPINLOCK +# ifdef SPINLOCK_23 +# include /* *lock* */ +# else /* SPINLOCK_23 */ +# include /* *lock* */ +# endif /* SPINLOCK_23 */ +#endif /* SPINLOCK */ + +#ifndef KLIPS_FIXES_DES_PARITY +# define KLIPS_FIXES_DES_PARITY 1 +#endif /* !KLIPS_FIXES_DES_PARITY */ + +/* we don't really want to print these unless there are really big problems */ +#ifndef KLIPS_DIVULGE_CYPHER_KEY +# define KLIPS_DIVULGE_CYPHER_KEY 0 +#endif /* !KLIPS_DIVULGE_CYPHER_KEY */ + +#ifndef KLIPS_DIVULGE_HMAC_KEY +# define KLIPS_DIVULGE_HMAC_KEY 0 +#endif /* !KLIPS_DIVULGE_HMAC_KEY */ + +#ifndef IPSEC_DISALLOW_IPOPTIONS +# define IPSEC_DISALLOW_IPOPTIONS 1 +#endif /* !KLIPS_DIVULGE_HMAC_KEY */ + +/* extra toggles for regression testing */ +#ifdef CONFIG_IPSEC_REGRESS + +/* + * should pfkey_acquire() become 100% lossy? + * + */ +extern int sysctl_ipsec_regress_pfkey_lossage; +#ifndef KLIPS_PFKEY_ACQUIRE_LOSSAGE +# ifdef CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE +# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 100 +# else /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */ +/* not by default! */ +# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 0 +# endif /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */ +#endif /* KLIPS_PFKEY_ACQUIRE_LOSSAGE */ + +#endif /* CONFIG_IPSEC_REGRESS */ + +/* + * debugging routines. + */ +#ifdef CONFIG_IPSEC_DEBUG +extern void ipsec_print_ip(struct iphdr *ip); + + #define KLIPS_PRINT(flag, format, args...) \ + ((flag) ? printk(KERN_INFO format , ## args) : 0) + #define KLIPS_PRINTMORE(flag, format, args...) \ + ((flag) ? printk(format , ## args) : 0) + #define KLIPS_IP_PRINT(flag, ip) \ + ((flag) ? ipsec_print_ip(ip) : 0) +#else /* CONFIG_IPSEC_DEBUG */ + #define KLIPS_PRINT(flag, format, args...) do ; while(0) + #define KLIPS_PRINTMORE(flag, format, args...) do ; while(0) + #define KLIPS_IP_PRINT(flag, ip) do ; while(0) +#endif /* CONFIG_IPSEC_DEBUG */ + + +/* + * Stupid kernel API differences in APIs. Not only do some + * kernels not have ip_select_ident, but some have differing APIs, + * and SuSE has one with one parameter, but no way of checking to + * see what is really what. + */ + +#ifdef SUSE_LINUX_2_4_19_IS_STUPID +#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph) +#else + +/* simplest case, nothing */ +#if !defined(IP_SELECT_IDENT) +#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0) +#endif + +/* kernels > 2.3.37-ish */ +#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW) +#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst) +#endif + +/* kernels > 2.4.2 */ +#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW) +#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL) +#endif + +#endif /* SUSE_LINUX_2_4_19_IS_STUPID */ + +/* + * make klips fail test:east-espiv-01. + * exploit is at testing/attacks/espiv + * + */ +#define KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK 0 + + +/* IP_FRAGMENT_LINEARIZE is set in freeswan.h if Kernel > 2.4.4 */ +#ifndef IP_FRAGMENT_LINEARIZE +# define IP_FRAGMENT_LINEARIZE 0 +#endif /* IP_FRAGMENT_LINEARIZE */ +#endif /* __KERNEL__ */ + +#define _IPSEC_PARAM_H_ +#endif /* _IPSEC_PARAM_H_ */ diff --git a/src/libfreeswan/ipsec_policy.h b/src/libfreeswan/ipsec_policy.h new file mode 100644 index 000000000..671919e4b --- /dev/null +++ b/src/libfreeswan/ipsec_policy.h @@ -0,0 +1,225 @@ +#ifndef _IPSEC_POLICY_H +/* + * policy interface file between pluto and applications + * Copyright (C) 2003 Michael Richardson + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ipsec_policy.h,v 1.4 2004/10/04 22:43:56 as Exp $ + */ +#define _IPSEC_POLICY_H /* seen it, no need to see it again */ + + +/* + * this file defines an interface between an application (or rather an + * application library) and a key/policy daemon. It provides for inquiries + * as to the current state of a connected socket, as well as for general + * questions. + * + * In general, the interface is defined as a series of functional interfaces, + * and the policy messages should be internal. However, because this is in + * fact an ABI between pieces of the system that may get compiled and revised + * seperately, this ABI must be public and revision controlled. + * + * It is expected that the daemon will always support previous versions. + */ + +#define IPSEC_POLICY_MSG_REVISION (unsigned)200305061 + +enum ipsec_policy_command { + IPSEC_CMD_QUERY_FD = 1, + IPSEC_CMD_QUERY_HOSTPAIR = 2, + IPSEC_CMD_QUERY_DSTONLY = 3, +}; + +struct ipsec_policy_msg_head { + u_int32_t ipm_version; + u_int32_t ipm_msg_len; + u_int32_t ipm_msg_type; + u_int32_t ipm_msg_seq; +}; + +enum ipsec_privacy_quality { + IPSEC_PRIVACY_NONE = 0, + IPSEC_PRIVACY_INTEGRAL = 4, /* not private at all. AH-like */ + IPSEC_PRIVACY_UNKNOWN = 8, /* something is claimed, but details unavail */ + IPSEC_PRIVACY_ROT13 = 12, /* trivially breakable, i.e. 1DES */ + IPSEC_PRIVACY_GAK = 16, /* known eavesdroppers */ + IPSEC_PRIVACY_PRIVATE = 32, /* secure for at least a decade */ + IPSEC_PRIVACY_STRONG = 64, /* ridiculously secure */ + IPSEC_PRIVACY_TORTOISE = 192, /* even stronger, but very slow */ + IPSEC_PRIVACY_OTP = 224, /* some kind of *true* one time pad */ +}; + +enum ipsec_bandwidth_quality { + IPSEC_QOS_UNKNOWN = 0, /* unknown bandwidth */ + IPSEC_QOS_INTERACTIVE = 16, /* reasonably moderate jitter, moderate fast. + Good enough for telnet/ssh. */ + IPSEC_QOS_VOIP = 32, /* faster crypto, predicable jitter */ + IPSEC_QOS_FTP = 64, /* higher throughput crypto, perhaps hardware + offloaded, but latency/jitter may be bad */ + IPSEC_QOS_WIRESPEED = 128, /* expect to be able to fill your pipe */ +}; + +/* moved from programs/pluto/constants.h */ +/* IPsec AH transform values + * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.3 + * and in http://www.iana.org/assignments/isakmp-registry + */ +enum ipsec_authentication_algo { + AH_NONE = 0, + AH_MD5 = 2, + AH_SHA = 3, + AH_DES = 4, + AH_SHA2_256 = 5, + AH_SHA2_384 = 6, + AH_SHA2_512 = 7, + AH_RIPEMD = 8 +}; + +/* IPsec ESP transform values + * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.4 + * and from http://www.iana.org/assignments/isakmp-registry + */ + +enum ipsec_cipher_algo { + ESP_NONE = 0, + ESP_DES_IV64 = 1, + ESP_DES = 2, + ESP_3DES = 3, + ESP_RC5 = 4, + ESP_IDEA = 5, + ESP_CAST = 6, + ESP_BLOWFISH = 7, + ESP_3IDEA = 8, + ESP_DES_IV32 = 9, + ESP_RC4 = 10, + ESP_NULL = 11, + ESP_AES = 12, + ESP_AES_CTR = 13, + ESP_AES_CCM_8 = 14, + ESP_AES_CCM_12 = 15, + ESP_AES_CCM_16 = 16, + ESP_SERPENT = 252, + ESP_TWOFISH = 253 +}; + +/* IPCOMP transform values + * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.5 + */ + +enum ipsec_comp_algo { + IPSCOMP_NONE = 0, + IPCOMP_OUI = 1, + IPCOMP_DEFLATE = 2, + IPCOMP_LZS = 3, + IPCOMP_LZJH = 4 +}; + +/* Identification type values + * RFC 2407 The Internet IP security Domain of Interpretation for ISAKMP 4.6.2.1 + */ + +enum ipsec_id_type { + ID_IMPOSSIBLE= (-2), /* private to Pluto */ + ID_MYID= (-1), /* private to Pluto */ + ID_NONE= 0, /* private to Pluto */ + ID_IPV4_ADDR= 1, + ID_FQDN= 2, + ID_USER_FQDN= 3, + ID_IPV4_ADDR_SUBNET= 4, + ID_IPV6_ADDR= 5, + ID_IPV6_ADDR_SUBNET= 6, + ID_IPV4_ADDR_RANGE= 7, + ID_IPV6_ADDR_RANGE= 8, + ID_DER_ASN1_DN= 9, + ID_DER_ASN1_GN= 10, + ID_KEY_ID= 11 +}; + +/* Certificate type values + * RFC 2408 ISAKMP, chapter 3.9 + */ +enum ipsec_cert_type { + CERT_NONE= 0, + CERT_PKCS7_WRAPPED_X509= 1, + CERT_PGP= 2, + CERT_DNS_SIGNED_KEY= 3, + CERT_X509_SIGNATURE= 4, + CERT_X509_KEY_EXCHANGE= 5, + CERT_KERBEROS_TOKENS= 6, + CERT_CRL= 7, + CERT_ARL= 8, + CERT_SPKI= 9, + CERT_X509_ATTRIBUTE= 10, + CERT_RAW_RSA_KEY= 11 +}; + +/* a SIG record in ASCII */ +struct ipsec_dns_sig { + char fqdn[256]; + char dns_sig[768]; /* empty string if not signed */ +}; + +struct ipsec_raw_key { + char id_name[256]; + char fs_keyid[8]; +}; + +struct ipsec_identity { + enum ipsec_id_type ii_type; + enum ipsec_cert_type ii_format; + union { + struct ipsec_dns_sig ipsec_dns_signed; + /* some thing for PGP */ + /* some thing for PKIX */ + struct ipsec_raw_key ipsec_raw_key; + } ii_credential; +}; + +#define IPSEC_MAX_CREDENTIALS 32 + +struct ipsec_policy_cmd_query { + struct ipsec_policy_msg_head head; + + /* Query section */ + ip_address query_local; /* us */ + ip_address query_remote; /* them */ + u_short src_port, dst_port; + + /* Answer section */ + enum ipsec_privacy_quality strength; + enum ipsec_bandwidth_quality bandwidth; + enum ipsec_authentication_algo auth_detail; + enum ipsec_cipher_algo esp_detail; + enum ipsec_comp_algo comp_detail; + + int credential_count; + + struct ipsec_identity credentials[IPSEC_MAX_CREDENTIALS]; +}; + +#define IPSEC_POLICY_SOCKET "/var/run/pluto.info" + +/* prototypes */ +extern err_t ipsec_policy_lookup(int fd, struct ipsec_policy_cmd_query *result); +extern err_t ipsec_policy_init(void); +extern err_t ipsec_policy_final(void); +extern err_t ipsec_policy_readmsg(int policysock, + unsigned char *buf, size_t buflen); +extern err_t ipsec_policy_sendrecv(unsigned char *buf, size_t buflen); +extern err_t ipsec_policy_cgilookup(struct ipsec_policy_cmd_query *result); + + +extern const char *ipsec_policy_version_code(void); +extern const char *ipsec_policy_version_string(void); + +#endif /* _IPSEC_POLICY_H */ diff --git a/src/libfreeswan/ipsec_proto.h b/src/libfreeswan/ipsec_proto.h new file mode 100644 index 000000000..55f947512 --- /dev/null +++ b/src/libfreeswan/ipsec_proto.h @@ -0,0 +1,111 @@ +/* + * @(#) prototypes for FreeSWAN functions + * + * Copyright (C) 2001 Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_proto.h,v 1.3 2004/06/13 19:55:14 as Exp $ + * + */ + +#ifndef _IPSEC_PROTO_H_ + +#include "ipsec_param.h" + +/* + * This file is a kernel only file that declares prototypes for + * all intra-module function calls and global data structures. + * + * Include this file last. + * + */ + +/* ipsec_init.c */ +extern struct prng ipsec_prng; + +/* ipsec_sa.c */ +extern struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD]; +extern spinlock_t tdb_lock; +extern int ipsec_sadb_init(void); + +extern struct ipsec_sa *ipsec_sa_getbyid(struct sa_id*); +extern int ipsec_sa_put(struct ipsec_sa *); +extern /* void */ int ipsec_sa_del(struct ipsec_sa *); +extern /* void */ int ipsec_sa_delchain(struct ipsec_sa *); +extern /* void */ int ipsec_sa_add(struct ipsec_sa *); + +extern int ipsec_sadb_cleanup(__u8); +extern int ipsec_sa_wipe(struct ipsec_sa *); + +/* debug declarations */ + +/* ipsec_proc.c */ +extern int ipsec_proc_init(void); +extern void ipsec_proc_cleanup(void); + +/* ipsec_radij.c */ +extern int ipsec_makeroute(struct sockaddr_encap *ea, + struct sockaddr_encap *em, + struct sa_id said, + uint32_t pid, + struct sk_buff *skb, + struct ident *ident_s, + struct ident *ident_d); + +extern int ipsec_breakroute(struct sockaddr_encap *ea, + struct sockaddr_encap *em, + struct sk_buff **first, + struct sk_buff **last); + +int ipsec_radijinit(void); +int ipsec_cleareroutes(void); +int ipsec_radijcleanup(void); + +/* ipsec_life.c */ +extern enum ipsec_life_alive ipsec_lifetime_check(struct ipsec_lifetime64 *il64, + const char *lifename, + const char *saname, + enum ipsec_life_type ilt, + enum ipsec_direction idir, + struct ipsec_sa *ips); + + +extern int ipsec_lifetime_format(char *buffer, + int buflen, + char *lifename, + enum ipsec_life_type timebaselife, + struct ipsec_lifetime64 *lifetime); + +extern void ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime, + __u64 newvalue); + +extern void ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime, + __u64 newvalue); + + + + +#ifdef CONFIG_IPSEC_DEBUG + +extern int debug_xform; +extern int debug_eroute; +extern int debug_spi; +extern int debug_netlink; + +#endif /* CONFIG_IPSEC_DEBUG */ + + + + +#define _IPSEC_PROTO_H +#endif /* _IPSEC_PROTO_H_ */ diff --git a/src/libfreeswan/ipsec_radij.h b/src/libfreeswan/ipsec_radij.h new file mode 100644 index 000000000..7776dd8e4 --- /dev/null +++ b/src/libfreeswan/ipsec_radij.h @@ -0,0 +1,63 @@ +/* + * @(#) Definitions relevant to the IPSEC <> radij tree interfacing + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_radij.h,v 1.3 2004/04/28 05:44:29 as Exp $ + */ + +#ifndef _IPSEC_RADIJ_H + +#include + +int ipsec_walk(char *); + +int ipsec_rj_walker_procprint(struct radij_node *, void *); +int ipsec_rj_walker_delete(struct radij_node *, void *); + +/* This structure is used to pass information between + * ipsec_eroute_get_info and ipsec_rj_walker_procprint + * (through rj_walktree) and between calls of ipsec_rj_walker_procprint. + */ +struct wsbuf +{ + /* from caller of ipsec_eroute_get_info: */ + char *const buffer; /* start of buffer provided */ + const int length; /* length of buffer provided */ + const off_t offset; /* file position of first character of interest */ + /* accumulated by ipsec_rj_walker_procprint: */ + int len; /* number of character filled into buffer */ + off_t begin; /* file position contained in buffer[0] (<=offset) */ +}; + + +extern struct radij_node_head *rnh; +extern spinlock_t eroute_lock; + +struct eroute * ipsec_findroute(struct sockaddr_encap *); + +#define O1(x) (int)(((x)>>24)&0xff) +#define O2(x) (int)(((x)>>16)&0xff) +#define O3(x) (int)(((x)>>8)&0xff) +#define O4(x) (int)(((x))&0xff) + +#ifdef CONFIG_IPSEC_DEBUG +extern int debug_radij; +void rj_dumptrees(void); + +#define DB_RJ_DUMPTREES 0x0001 +#define DB_RJ_FINDROUTE 0x0002 +#endif /* CONFIG_IPSEC_DEBUG */ + +#define _IPSEC_RADIJ_H +#endif diff --git a/src/libfreeswan/ipsec_rcv.h b/src/libfreeswan/ipsec_rcv.h new file mode 100644 index 000000000..3ae239bf9 --- /dev/null +++ b/src/libfreeswan/ipsec_rcv.h @@ -0,0 +1,196 @@ +/* + * + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_rcv.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + +#define DB_RX_PKTRX 0x0001 +#define DB_RX_PKTRX2 0x0002 +#define DB_RX_DMP 0x0004 +#define DB_RX_IPSA 0x0010 +#define DB_RX_XF 0x0020 +#define DB_RX_IPAD 0x0040 +#define DB_RX_INAU 0x0080 +#define DB_RX_OINFO 0x0100 +#define DB_RX_OINFO2 0x0200 +#define DB_RX_OH 0x0400 +#define DB_RX_REPLAY 0x0800 + +#ifdef __KERNEL__ +/* struct options; */ + +#define __NO_VERSION__ +#include +#include /* for CONFIG_IP_FORWARD */ +#include +#include + +#define IPSEC_BIRTH_TEMPLATE_MAXLEN 256 + +struct ipsec_birth_reply { + int packet_template_len; + unsigned char packet_template[IPSEC_BIRTH_TEMPLATE_MAXLEN]; +}; + +extern struct ipsec_birth_reply ipsec_ipv4_birth_packet; +extern struct ipsec_birth_reply ipsec_ipv6_birth_packet; + +extern int +#ifdef PROTO_HANDLER_SINGLE_PARM +ipsec_rcv(struct sk_buff *skb); +#else /* PROTO_HANDLER_SINGLE_PARM */ +ipsec_rcv(struct sk_buff *skb, +#ifdef NET_21 + unsigned short xlen); +#else /* NET_21 */ + struct device *dev, + struct options *opt, + __u32 daddr, + unsigned short len, + __u32 saddr, + int redo, + struct inet_protocol *protocol); +#endif /* NET_21 */ +#endif /* PROTO_HANDLER_SINGLE_PARM */ + +#ifdef CONFIG_IPSEC_DEBUG +extern int debug_rcv; +#endif /* CONFIG_IPSEC_DEBUG */ +extern int sysctl_ipsec_inbound_policy_check; +#endif /* __KERNEL__ */ + +/* + * $Log: ipsec_rcv.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.17 2002/09/03 16:32:32 mcr + * definitions of ipsec_birth_reply. + * + * Revision 1.16 2002/05/14 02:36:00 rgb + * Change references to _TDB to _IPSA. + * + * Revision 1.15 2002/04/24 07:36:47 mcr + * Moved from ./klips/net/ipsec/ipsec_rcv.h,v + * + * Revision 1.14 2001/09/07 22:15:48 rgb + * Fix for removal of transport layer protocol handler arg in 2.4.4. + * + * Revision 1.13 2001/06/14 19:35:09 rgb + * Update copyright date. + * + * Revision 1.12 2001/03/16 07:36:44 rgb + * Fixed #endif comment to sate compiler. + * + * Revision 1.11 2000/09/21 04:34:21 rgb + * Moved declaration of sysctl_ipsec_inbound_policy_check outside + * CONFIG_IPSEC_DEBUG. (MB) + * + * Revision 1.10 2000/09/18 02:36:10 rgb + * Exported sysctl_ipsec_inbound_policy_check for skb_decompress(). + * + * Revision 1.9 2000/09/08 19:12:56 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * + * Revision 1.8 1999/11/18 04:09:19 rgb + * Replaced all kernel version macros to shorter, readable form. + * + * Revision 1.7 1999/05/25 01:45:37 rgb + * Fix version macros for 2.0.x as a module. + * + * Revision 1.6 1999/05/08 21:24:27 rgb + * Add includes for 2.2.x include into net/ipv4/protocol.c + * + * Revision 1.5 1999/05/05 22:02:32 rgb + * Add a quick and dirty port to 2.2 kernels by Marc Boucher . + * + * Revision 1.4 1999/04/11 00:28:59 henry + * GPL boilerplate + * + * Revision 1.3 1999/04/06 04:54:27 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.2 1999/01/22 20:06:59 rgb + * Fixed cut-and-paste error from ipsec_esp.h. + * + * Revision 1.1 1999/01/21 20:29:12 rgb + * Converted from transform switching to algorithm switching. + * + * Log: ipsec_esp.h,v + * Revision 1.4 1998/08/12 00:07:32 rgb + * Added data structures for new xforms: null, {,3}dessha1. + * + * Revision 1.3 1998/07/14 15:57:01 rgb + * Add #ifdef __KERNEL__ to protect kernel-only structures. + * + * Revision 1.2 1998/06/25 19:33:46 rgb + * Add prototype for protocol receive function. + * Rearrange for more logical layout. + * + * Revision 1.1 1998/06/18 21:27:45 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.6 1998/06/05 02:28:08 rgb + * Minor comment fix. + * + * Revision 1.5 1998/05/27 22:34:00 rgb + * Changed structures to accomodate key separation. + * + * Revision 1.4 1998/05/18 22:28:43 rgb + * Disable key printing facilities from /proc/net/ipsec_*. + * + * Revision 1.3 1998/04/21 21:29:07 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.2 1998/04/12 22:03:20 rgb + * Updated ESP-3DES-HMAC-MD5-96, + * ESP-DES-HMAC-MD5-96, + * AH-HMAC-MD5-96, + * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository + * from old standards (RFC182[5-9] to new (as of March 1998) drafts. + * + * Fixed eroute references in /proc/net/ipsec*. + * + * Started to patch module unloading memory leaks in ipsec_netlink and + * radij tree unloading. + * + * Revision 1.1 1998/04/09 03:06:00 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:02 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.5 1997/06/03 04:24:48 ji + * Added ESP-3DES-MD5-96 transform. + * + * Revision 0.4 1997/01/15 01:28:15 ji + * Added definitions for new ESP transforms. + * + * Revision 0.3 1996/11/20 14:35:48 ji + * Minor Cleanup. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ + + diff --git a/src/libfreeswan/ipsec_sa.h b/src/libfreeswan/ipsec_sa.h new file mode 100644 index 000000000..555df42d3 --- /dev/null +++ b/src/libfreeswan/ipsec_sa.h @@ -0,0 +1,338 @@ +/* + * @(#) Definitions of IPsec Security Association (ipsec_sa) + * + * Copyright (C) 2001, 2002, 2003 + * Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_sa.h,v 1.3 2004/04/28 08:07:11 as Exp $ + * + * This file derived from ipsec_xform.h on 2001/9/18 by mcr. + * + */ + +/* + * This file describes the IPsec Security Association Structure. + * + * This structure keeps track of a single transform that may be done + * to a set of packets. It can describe applying the transform or + * apply the reverse. (e.g. compression vs expansion). However, it + * only describes one at a time. To describe both, two structures would + * be used, but since the sides of the transform are performed + * on different machines typically it is usual to have only one side + * of each association. + * + */ + +#ifndef _IPSEC_SA_H_ + +#ifdef __KERNEL__ +#include "ipsec_stats.h" +#include "ipsec_life.h" +#include "ipsec_eroute.h" +#endif /* __KERNEL__ */ +#include "ipsec_param.h" + + +/* SAs are held in a table. + * Entries in this table are referenced by IPsecSAref_t values. + * IPsecSAref_t values are conceptually subscripts. Because + * we want to allocate the table piece-meal, the subscripting + * is implemented with two levels, a bit like paged virtual memory. + * This representation mechanism is known as an Iliffe Vector. + * + * The Main table (AKA the refTable) consists of 2^IPSEC_SA_REF_MAINTABLE_IDX_WIDTH + * pointers to subtables. + * Each subtable has 2^IPSEC_SA_REF_SUBTABLE_IDX_WIDTH entries, each of which + * is a pointer to an SA. + * + * An IPsecSAref_t contains either an exceptional value (signified by the + * high-order bit being on) or a reference to a table entry. A table entry + * reference has the subtable subscript in the low-order + * IPSEC_SA_REF_SUBTABLE_IDX_WIDTH bits and the Main table subscript + * in the next lowest IPSEC_SA_REF_MAINTABLE_IDX_WIDTH bits. + * + * The Maintable entry for an IPsecSAref_t x, a pointer to its subtable, is + * IPsecSAref2table(x). It is of type struct IPsecSArefSubTable *. + * + * The pointer to the SA for x is IPsecSAref2SA(x). It is of type + * struct ipsec_sa*. The macro definition clearly shows the two-level + * access needed to find the SA pointer. + * + * The Maintable is allocated when IPsec is initialized. + * Each subtable is allocated when needed, but the first is allocated + * when IPsec is initialized. + * + * IPsecSAref_t is designed to be smaller than an NFmark so that + * they can be stored in NFmarks and still leave a few bits for other + * purposes. The spare bits are in the low order of the NFmark + * but in the high order of the IPsecSAref_t, so conversion is required. + * We pick the upper bits of NFmark on the theory that they are less likely to + * interfere with more pedestrian uses of nfmark. + */ + + +typedef unsigned short int IPsecRefTableUnusedCount; + +#define IPSEC_SA_REF_TABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH) + +#ifdef __KERNEL__ +#if ((IPSEC_SA_REF_TABLE_IDX_WIDTH - (1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) < 0) +#error "IPSEC_SA_REF_TABLE_IDX_WIDTH("IPSEC_SA_REF_TABLE_IDX_WIDTH") MUST be < 1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH("IPSEC_SA_REF_MAINTABLE_IDX_WIDTH")" +#endif + +#define IPSEC_SA_REF_SUBTABLE_IDX_WIDTH (IPSEC_SA_REF_TABLE_IDX_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH) + +#define IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_MAINTABLE_IDX_WIDTH) +#define IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) + +#ifdef CONFIG_NETFILTER +#define IPSEC_SA_REF_HOST_FIELD(x) ((struct sk_buff*)(x))->nfmark +#define IPSEC_SA_REF_HOST_FIELD_TYPE typeof(IPSEC_SA_REF_HOST_FIELD(NULL)) +#else /* CONFIG_NETFILTER */ +/* just make it work for now, it doesn't matter, since there is no nfmark */ +#define IPSEC_SA_REF_HOST_FIELD_TYPE unsigned long +#endif /* CONFIG_NETFILTER */ +#define IPSEC_SA_REF_HOST_FIELD_WIDTH (8 * sizeof(IPSEC_SA_REF_HOST_FIELD_TYPE)) +#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t)) + +#define IPSEC_SA_REF_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH)) +#define IPSEC_SA_REF_TABLE_MASK ((IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) +#define IPSEC_SA_REF_ENTRY_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)) + +#define IPsecSAref2table(x) (((x) & IPSEC_SA_REF_TABLE_MASK) >> IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) +#define IPsecSAref2entry(x) ((x) & IPSEC_SA_REF_ENTRY_MASK) +#define IPsecSArefBuild(x,y) (((x) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) + (y)) + +#define IPsecSAref2SA(x) (ipsec_sadb.refTable[IPsecSAref2table(x)]->entry[IPsecSAref2entry(x)]) +#define IPsecSA2SAref(x) ((x)->ips_ref) + +#define EMT_INBOUND 0x01 /* SA direction, 1=inbound */ + +/* 'struct ipsec_sa' should be 64bit aligned when allocated. */ +struct ipsec_sa +{ + IPsecSAref_t ips_ref; /* reference table entry number */ + atomic_t ips_refcount; /* reference count for this struct */ + struct ipsec_sa *ips_hnext; /* next in hash chain */ + struct ipsec_sa *ips_inext; /* pointer to next xform */ + struct ipsec_sa *ips_onext; /* pointer to prev xform */ + + struct ifnet *ips_rcvif; /* related rcv encap interface */ + + struct sa_id ips_said; /* SA ID */ + + __u32 ips_seq; /* seq num of msg that initiated this SA */ + __u32 ips_pid; /* PID of process that initiated this SA */ + __u8 ips_authalg; /* auth algorithm for this SA */ + __u8 ips_encalg; /* enc algorithm for this SA */ + + struct ipsec_stats ips_errs; + + __u8 ips_replaywin; /* replay window size */ + __u8 ips_state; /* state of SA */ + __u32 ips_replaywin_lastseq; /* last pkt sequence num */ + __u64 ips_replaywin_bitmap; /* bitmap of received pkts */ + __u32 ips_replaywin_maxdiff; /* max pkt sequence difference */ + + __u32 ips_flags; /* generic xform flags */ + + + struct ipsec_lifetimes ips_life; /* lifetime records */ + + /* selector information */ + struct sockaddr*ips_addr_s; /* src sockaddr */ + struct sockaddr*ips_addr_d; /* dst sockaddr */ + struct sockaddr*ips_addr_p; /* proxy sockaddr */ + __u16 ips_addr_s_size; + __u16 ips_addr_d_size; + __u16 ips_addr_p_size; + ip_address ips_flow_s; + ip_address ips_flow_d; + ip_address ips_mask_s; + ip_address ips_mask_d; + + __u16 ips_key_bits_a; /* size of authkey in bits */ + __u16 ips_auth_bits; /* size of authenticator in bits */ + __u16 ips_key_bits_e; /* size of enckey in bits */ + __u16 ips_iv_bits; /* size of IV in bits */ + __u8 ips_iv_size; + __u16 ips_key_a_size; + __u16 ips_key_e_size; + + caddr_t ips_key_a; /* authentication key */ + caddr_t ips_key_e; /* encryption key */ + caddr_t ips_iv; /* Initialisation Vector */ + + struct ident ips_ident_s; /* identity src */ + struct ident ips_ident_d; /* identity dst */ + +#ifdef CONFIG_IPSEC_IPCOMP + __u16 ips_comp_adapt_tries; /* ipcomp self-adaption tries */ + __u16 ips_comp_adapt_skip; /* ipcomp self-adaption to-skip */ + __u64 ips_comp_ratio_cbytes; /* compressed bytes */ + __u64 ips_comp_ratio_dbytes; /* decompressed (or uncompressed) bytes */ +#endif /* CONFIG_IPSEC_IPCOMP */ + +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + __u8 ips_natt_type; + __u8 ips_natt_reserved[3]; + __u16 ips_natt_sport; + __u16 ips_natt_dport; + + struct sockaddr *ips_natt_oa; + __u16 ips_natt_oa_size; + __u16 ips_natt_reserved2; +#endif + +#if 0 + __u32 ips_sens_dpd; + __u8 ips_sens_sens_level; + __u8 ips_sens_sens_len; + __u64* ips_sens_sens_bitmap; + __u8 ips_sens_integ_level; + __u8 ips_sens_integ_len; + __u64* ips_sens_integ_bitmap; +#endif + struct ipsec_alg_enc *ips_alg_enc; + struct ipsec_alg_auth *ips_alg_auth; + IPsecSAref_t ips_ref_rel; +}; + +struct IPsecSArefSubTable +{ + struct ipsec_sa* entry[IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES]; +}; + +struct ipsec_sadb { + struct IPsecSArefSubTable* refTable[IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES]; + IPsecSAref_t refFreeList[IPSEC_SA_REF_FREELIST_NUM_ENTRIES]; + int refFreeListHead; + int refFreeListTail; + IPsecSAref_t refFreeListCont; + IPsecSAref_t said_hash[SADB_HASHMOD]; + spinlock_t sadb_lock; +}; + +extern struct ipsec_sadb ipsec_sadb; + +extern int ipsec_SAref_recycle(void); +extern int ipsec_SArefSubTable_alloc(unsigned table); +extern int ipsec_saref_freelist_init(void); +extern int ipsec_sadb_init(void); +extern struct ipsec_sa *ipsec_sa_alloc(int*error); /* pass in error var by pointer */ +extern IPsecSAref_t ipsec_SAref_alloc(int*erorr); /* pass in error var by pointer */ +extern int ipsec_sa_free(struct ipsec_sa* ips); +extern struct ipsec_sa *ipsec_sa_getbyid(struct sa_id *said); +extern int ipsec_sa_put(struct ipsec_sa *ips); +extern int ipsec_sa_add(struct ipsec_sa *ips); +extern int ipsec_sa_del(struct ipsec_sa *ips); +extern int ipsec_sa_delchain(struct ipsec_sa *ips); +extern int ipsec_sadb_cleanup(__u8 proto); +extern int ipsec_sadb_free(void); +extern int ipsec_sa_wipe(struct ipsec_sa *ips); +#endif /* __KERNEL__ */ + +enum ipsec_direction { + ipsec_incoming = 1, + ipsec_outgoing = 2 +}; + +#define _IPSEC_SA_H_ +#endif /* _IPSEC_SA_H_ */ + +/* + * $Log: ipsec_sa.h,v $ + * Revision 1.3 2004/04/28 08:07:11 as + * added dhr's freeswan-2.06 changes + * + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.2.1.2.1 2004/03/16 09:48:18 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1.2.1 2004/03/15 22:30:06 as + * nat-0.6c patch merged + * + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.15 2003/05/11 00:53:09 mcr + * IPsecSAref_t and macros were moved to freeswan.h. + * + * Revision 1.14 2003/02/12 19:31:55 rgb + * Fixed bug in "file seen" machinery. + * Updated copyright year. + * + * Revision 1.13 2003/01/30 02:31:52 rgb + * + * Re-wrote comments describing SAref system for accuracy. + * Rename SAref table macro names for clarity. + * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. + * Transmit error code through to caller from callee for better diagnosis of problems. + * Enclose all macro arguments in parens to avoid any possible obscrure bugs. + * + * Revision 1.12 2002/10/07 18:31:19 rgb + * Change comment to reflect the flexible nature of the main and sub-table widths. + * Added a counter for the number of unused entries in each subtable. + * Further break up host field type macro to host field. + * Move field width sanity checks to ipsec_sa.c + * Define a mask for an entire saref. + * + * Revision 1.11 2002/09/20 15:40:33 rgb + * Re-write most of the SAref macros and types to eliminate any pointer references to Entrys. + * Fixed SAref/nfmark macros. + * Rework saref freeslist. + * Place all ipsec sadb globals into one struct. + * Restrict some bits to kernel context for use to klips utils. + * + * Revision 1.10 2002/09/20 05:00:34 rgb + * Update copyright date. + * + * Revision 1.9 2002/09/17 17:19:29 mcr + * make it compile even if there is no netfilter - we lost + * functionality, but it works, especially on 2.2. + * + * Revision 1.8 2002/07/28 22:59:53 mcr + * clarified/expanded one comment. + * + * Revision 1.7 2002/07/26 08:48:31 rgb + * Added SA ref table code. + * + * Revision 1.6 2002/05/31 17:27:48 rgb + * Comment fix. + * + * Revision 1.5 2002/05/27 18:55:03 rgb + * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT. + * + * Revision 1.4 2002/05/23 07:13:36 rgb + * Convert "usecount" to "refcount" to remove ambiguity. + * + * Revision 1.3 2002/04/24 07:36:47 mcr + * Moved from ./klips/net/ipsec/ipsec_sa.h,v + * + * Revision 1.2 2001/11/26 09:16:15 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.1.2.1 2001/09/25 02:24:58 mcr + * struct tdb -> struct ipsec_sa. + * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c + * ipsec_xform.c removed. header file still contains useful things. + * + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/ipsec_sha1.h b/src/libfreeswan/ipsec_sha1.h new file mode 100644 index 000000000..116170e6b --- /dev/null +++ b/src/libfreeswan/ipsec_sha1.h @@ -0,0 +1,79 @@ +/* + * RCSID $Id: ipsec_sha1.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + +/* + * Here is the original comment from the distribution: + +SHA-1 in C +By Steve Reid +100% Public Domain + + * Adapted for use by the IPSEC code by John Ioannidis + */ + + +#ifndef _IPSEC_SHA1_H_ +#define _IPSEC_SHA1_H_ + +typedef struct +{ + __u32 state[5]; + __u32 count[2]; + __u8 buffer[64]; +} SHA1_CTX; + +void SHA1Transform(__u32 state[5], __u8 buffer[64]); +void SHA1Init(void *context); +void SHA1Update(void *context, unsigned char *data, __u32 len); +void SHA1Final(unsigned char digest[20], void *context); + + +#endif /* _IPSEC_SHA1_H_ */ + +/* + * $Log: ipsec_sha1.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.7 2002/09/10 01:45:09 mcr + * changed type of MD5_CTX and SHA1_CTX to void * so that + * the function prototypes would match, and could be placed + * into a pointer to a function. + * + * Revision 1.6 2002/04/24 07:36:47 mcr + * Moved from ./klips/net/ipsec/ipsec_sha1.h,v + * + * Revision 1.5 1999/12/13 13:59:13 rgb + * Quick fix to argument size to Update bugs. + * + * Revision 1.4 1999/12/07 18:16:23 rgb + * Fixed comments at end of #endif lines. + * + * Revision 1.3 1999/04/06 04:54:27 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.2 1998/11/30 13:22:54 rgb + * Rationalised all the klips kernel file headers. They are much shorter + * now and won't conflict under RH5.2. + * + * Revision 1.1 1998/06/18 21:27:50 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.2 1998/04/23 20:54:05 rgb + * Fixed md5 and sha1 include file nesting issues, to be cleaned up when + * verified. + * + * Revision 1.1 1998/04/09 03:04:21 henry + * sources moved up from linux/net/ipsec + * these two include files modified not to include others except in kernel + * + * Revision 1.1.1.1 1998/04/08 05:35:04 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * New transform + * + */ diff --git a/src/libfreeswan/ipsec_stats.h b/src/libfreeswan/ipsec_stats.h new file mode 100644 index 000000000..e4be11d29 --- /dev/null +++ b/src/libfreeswan/ipsec_stats.h @@ -0,0 +1,38 @@ +/* + * @(#) definition of ipsec_stats structure + * + * Copyright (C) 2001 Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_stats.h,v 1.2 2004/03/30 19:33:52 as Exp $ + * + */ + +/* + * This file describes the errors/statistics that FreeSWAN collects. + */ + +#ifndef _IPSEC_STATS_H_ + +struct ipsec_stats { + __u32 ips_alg_errs; /* number of algorithm errors */ + __u32 ips_auth_errs; /* # of authentication errors */ + __u32 ips_encsize_errs; /* # of encryption size errors*/ + __u32 ips_encpad_errs; /* # of encryption pad errors*/ + __u32 ips_replaywin_errs; /* # of pkt sequence errors */ +}; + +extern int ipsec_snprintf(char * buf, ssize_t size, const char *fmt, ...); + +#define _IPSEC_STATS_H_ +#endif /* _IPSEC_STATS_H_ */ diff --git a/src/libfreeswan/ipsec_tunnel.h b/src/libfreeswan/ipsec_tunnel.h new file mode 100644 index 000000000..3b25e95e1 --- /dev/null +++ b/src/libfreeswan/ipsec_tunnel.h @@ -0,0 +1,265 @@ +/* + * IPSEC tunneling code + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_tunnel.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + + +#ifdef NET_21 +# define DEV_QUEUE_XMIT(skb, device, pri) {\ + skb->dev = device; \ + neigh_compat_output(skb); \ + /* skb->dst->output(skb); */ \ + } +# define ICMP_SEND(skb_in, type, code, info, dev) \ + icmp_send(skb_in, type, code, htonl(info)) +# define IP_SEND(skb, dev) \ + ip_send(skb); +#else /* NET_21 */ +# define DEV_QUEUE_XMIT(skb, device, pri) {\ + dev_queue_xmit(skb, device, pri); \ + } +# define ICMP_SEND(skb_in, type, code, info, dev) \ + icmp_send(skb_in, type, code, info, dev) +# define IP_SEND(skb, dev) \ + if(ntohs(iph->tot_len) > physmtu) { \ + ip_fragment(NULL, skb, dev, 0); \ + ipsec_kfree_skb(skb); \ + } else { \ + dev_queue_xmit(skb, dev, SOPRI_NORMAL); \ + } +#endif /* NET_21 */ + + +/* + * Heavily based on drivers/net/new_tunnel.c. Lots + * of ideas also taken from the 2.1.x version of drivers/net/shaper.c + */ + +struct ipsectunnelconf +{ + __u32 cf_cmd; + union + { + char cfu_name[12]; + } cf_u; +#define cf_name cf_u.cfu_name +}; + +#define IPSEC_SET_DEV (SIOCDEVPRIVATE) +#define IPSEC_DEL_DEV (SIOCDEVPRIVATE + 1) +#define IPSEC_CLR_DEV (SIOCDEVPRIVATE + 2) + +#ifdef __KERNEL__ +#include +#ifndef KERNEL_VERSION +# define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) +#endif +struct ipsecpriv +{ + struct sk_buff_head sendq; + struct device *dev; + struct wait_queue *wait_queue; + char locked; + int (*hard_start_xmit) (struct sk_buff *skb, + struct device *dev); + int (*hard_header) (struct sk_buff *skb, + struct device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len); +#ifdef NET_21 + int (*rebuild_header)(struct sk_buff *skb); +#else /* NET_21 */ + int (*rebuild_header)(void *buff, struct device *dev, + unsigned long raddr, struct sk_buff *skb); +#endif /* NET_21 */ + int (*set_mac_address)(struct device *dev, void *addr); +#ifndef NET_21 + void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, + unsigned short htype, __u32 daddr); +#endif /* !NET_21 */ + void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr); + struct net_device_stats *(*get_stats)(struct device *dev); + struct net_device_stats mystats; + int mtu; /* What is the desired MTU? */ +}; + +extern char ipsec_tunnel_c_version[]; + +extern struct device *ipsecdevices[IPSEC_NUM_IF]; + +int ipsec_tunnel_init_devices(void); + +/* void */ int ipsec_tunnel_cleanup_devices(void); + +extern /* void */ int ipsec_init(void); + +extern int ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev); + +#ifdef CONFIG_IPSEC_DEBUG +extern int debug_tunnel; +extern int sysctl_ipsec_debug_verbose; +#endif /* CONFIG_IPSEC_DEBUG */ +#endif /* __KERNEL__ */ + +#ifdef CONFIG_IPSEC_DEBUG +#define DB_TN_INIT 0x0001 +#define DB_TN_PROCFS 0x0002 +#define DB_TN_XMIT 0x0010 +#define DB_TN_OHDR 0x0020 +#define DB_TN_CROUT 0x0040 +#define DB_TN_OXFS 0x0080 +#define DB_TN_REVEC 0x0100 +#endif /* CONFIG_IPSEC_DEBUG */ + +/* + * $Log: ipsec_tunnel.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.28 2003/06/24 20:22:32 mcr + * added new global: ipsecdevices[] so that we can keep track of + * the ipsecX devices. They will be referenced with dev_hold(), + * so 2.2 may need this as well. + * + * Revision 1.27 2003/04/03 17:38:09 rgb + * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}. + * + * Revision 1.26 2003/02/12 19:32:20 rgb + * Updated copyright year. + * + * Revision 1.25 2002/05/27 18:56:07 rgb + * Convert to dynamic ipsec device allocation. + * + * Revision 1.24 2002/04/24 07:36:48 mcr + * Moved from ./klips/net/ipsec/ipsec_tunnel.h,v + * + * Revision 1.23 2001/11/06 19:50:44 rgb + * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for + * use also by pfkey_v2_parser.c + * + * Revision 1.22 2001/09/15 16:24:05 rgb + * Re-inject first and last HOLD packet when an eroute REPLACE is done. + * + * Revision 1.21 2001/06/14 19:35:10 rgb + * Update copyright date. + * + * Revision 1.20 2000/09/15 11:37:02 rgb + * Merge in heavily modified Svenning Soerensen's + * IPCOMP zlib deflate code. + * + * Revision 1.19 2000/09/08 19:12:56 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * + * Revision 1.18 2000/07/28 13:50:54 rgb + * Changed enet_statistics to net_device_stats and added back compatibility + * for pre-2.1.19. + * + * Revision 1.17 1999/11/19 01:12:15 rgb + * Purge unneeded proc_info prototypes, now that static linking uses + * dynamic proc_info registration. + * + * Revision 1.16 1999/11/18 18:51:00 rgb + * Changed all device registrations for static linking to + * dynamic to reduce the number and size of patches. + * + * Revision 1.15 1999/11/18 04:14:21 rgb + * Replaced all kernel version macros to shorter, readable form. + * Added CONFIG_PROC_FS compiler directives in case it is shut off. + * Added Marc Boucher's 2.3.25 proc patches. + * + * Revision 1.14 1999/05/25 02:50:10 rgb + * Fix kernel version macros for 2.0.x static linking. + * + * Revision 1.13 1999/05/25 02:41:06 rgb + * Add ipsec_klipsdebug support for static linking. + * + * Revision 1.12 1999/05/05 22:02:32 rgb + * Add a quick and dirty port to 2.2 kernels by Marc Boucher . + * + * Revision 1.11 1999/04/29 15:19:50 rgb + * Add return values to init and cleanup functions. + * + * Revision 1.10 1999/04/16 16:02:39 rgb + * Bump up macro to 4 ipsec I/Fs. + * + * Revision 1.9 1999/04/15 15:37:25 rgb + * Forward check changes from POST1_00 branch. + * + * Revision 1.5.2.1 1999/04/02 04:26:14 rgb + * Backcheck from HEAD, pre1.0. + * + * Revision 1.8 1999/04/11 00:29:01 henry + * GPL boilerplate + * + * Revision 1.7 1999/04/06 04:54:28 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.6 1999/03/31 05:44:48 rgb + * Keep PMTU reduction private. + * + * Revision 1.5 1999/02/10 22:31:20 rgb + * Change rebuild_header member to reflect generality of link layer. + * + * Revision 1.4 1998/12/01 13:22:04 rgb + * Added support for debug printing of version info. + * + * Revision 1.3 1998/07/29 20:42:46 rgb + * Add a macro for clearing all tunnel devices. + * Rearrange structures and declarations for sharing with userspace. + * + * Revision 1.2 1998/06/25 20:01:45 rgb + * Make prototypes available for ipsec_init and ipsec proc_dir_entries + * for static linking. + * + * Revision 1.1 1998/06/18 21:27:50 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.3 1998/05/18 21:51:50 rgb + * Added macros for num of I/F's and a procfs debug switch. + * + * Revision 1.2 1998/04/21 21:29:09 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.1 1998/04/09 03:06:13 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:05 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.5 1997/06/03 04:24:48 ji + * Added transport mode. + * Changed the way routing is done. + * Lots of bug fixes. + * + * Revision 0.4 1997/01/15 01:28:15 ji + * No changes. + * + * Revision 0.3 1996/11/20 14:39:04 ji + * Minor cleanups. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/ipsec_xform.h b/src/libfreeswan/ipsec_xform.h new file mode 100644 index 000000000..1dc6b6083 --- /dev/null +++ b/src/libfreeswan/ipsec_xform.h @@ -0,0 +1,274 @@ +/* + * Definitions relevant to IPSEC transformations + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_xform.h,v 1.3 2004/09/29 22:26:13 as Exp $ + */ + +#ifndef _IPSEC_XFORM_H_ + +#include +#include "ipsec_policy.h" + +#define XF_NONE 0 /* No transform set */ +#define XF_IP4 1 /* IPv4 inside IPv4 */ +#define XF_AHMD5 2 /* AH MD5 */ +#define XF_AHSHA 3 /* AH SHA */ +#define XF_ESP3DES 5 /* ESP DES3-CBC */ +#define XF_AHHMACMD5 6 /* AH-HMAC-MD5 with opt replay prot */ +#define XF_AHHMACSHA1 7 /* AH-HMAC-SHA1 with opt replay prot */ +#define XF_ESP3DESMD5 9 /* triple DES, HMAC-MD-5, 128-bits of authentication */ +#define XF_ESP3DESMD596 10 /* triple DES, HMAC-MD-5, 96-bits of authentication */ +#define XF_ESPNULLMD596 12 /* NULL, HMAC-MD-5 with 96-bits of authentication */ +#define XF_ESPNULLSHA196 13 /* NULL, HMAC-SHA-1 with 96-bits of authentication */ +#define XF_ESP3DESSHA196 14 /* triple DES, HMAC-SHA-1, 96-bits of authentication */ +#define XF_IP6 15 /* IPv6 inside IPv6 */ +#define XF_COMPDEFLATE 16 /* IPCOMP deflate */ + +#define XF_CLR 126 /* Clear SA table */ +#define XF_DEL 127 /* Delete SA */ + +#define XFT_AUTH 0x0001 +#define XFT_CONF 0x0100 + +/* available if CONFIG_IPSEC_DEBUG is defined */ +#define DB_XF_INIT 0x0001 + +#define PROTO2TXT(x) \ + (x) == IPPROTO_AH ? "AH" : \ + (x) == IPPROTO_ESP ? "ESP" : \ + (x) == IPPROTO_IPIP ? "IPIP" : \ + (x) == IPPROTO_COMP ? "COMP" : \ + "UNKNOWN_proto" +static inline const char *enc_name_id (unsigned id) { + static char buf[16]; + snprintf(buf, sizeof(buf), "_ID%d", id); + return buf; +} +static inline const char *auth_name_id (unsigned id) { + static char buf[16]; + snprintf(buf, sizeof(buf), "_ID%d", id); + return buf; +} +#define IPS_XFORM_NAME(x) \ + PROTO2TXT((x)->ips_said.proto), \ + (x)->ips_said.proto == IPPROTO_COMP ? \ + ((x)->ips_encalg == SADB_X_CALG_DEFLATE ? \ + "_DEFLATE" : "_UNKNOWN_comp") : \ + (x)->ips_encalg == ESP_NONE ? "" : \ + (x)->ips_encalg == ESP_3DES ? "_3DES" : \ + (x)->ips_encalg == ESP_AES ? "_AES" : \ + (x)->ips_encalg == ESP_SERPENT ? "_SERPENT" : \ + (x)->ips_encalg == ESP_TWOFISH ? "_TWOFISH" : \ + enc_name_id(x->ips_encalg)/* "_UNKNOWN_encr" */, \ + (x)->ips_authalg == AH_NONE ? "" : \ + (x)->ips_authalg == AH_MD5 ? "_HMAC_MD5" : \ + (x)->ips_authalg == AH_SHA ? "_HMAC_SHA1" : \ + (x)->ips_authalg == AH_SHA2_256 ? "_HMAC_SHA2_256" : \ + (x)->ips_authalg == AH_SHA2_384 ? "_HMAC_SHA2_384" : \ + (x)->ips_authalg == AH_SHA2_512 ? "_HMAC_SHA2_512" : \ + auth_name_id(x->ips_authalg) /* "_UNKNOWN_auth" */ \ + +#define _IPSEC_XFORM_H_ +#endif /* _IPSEC_XFORM_H_ */ + +/* + * $Log: ipsec_xform.h,v $ + * Revision 1.3 2004/09/29 22:26:13 as + * included ipsec_policy.h + * + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.4.1 2004/03/16 09:48:18 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.36 2002/04/24 07:36:48 mcr + * Moved from ./klips/net/ipsec/ipsec_xform.h,v + * + * Revision 1.35 2001/11/26 09:23:51 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.33.2.1 2001/09/25 02:24:58 mcr + * struct tdb -> struct ipsec_sa. + * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c + * ipsec_xform.c removed. header file still contains useful things. + * + * Revision 1.34 2001/11/06 19:47:17 rgb + * Changed lifetime_packets to uint32 from uint64. + * + * Revision 1.33 2001/09/08 21:13:34 rgb + * Added pfkey ident extension support for ISAKMPd. (NetCelo) + * + * Revision 1.32 2001/07/06 07:40:01 rgb + * Reformatted for readability. + * Added inbound policy checking fields for use with IPIP SAs. + * + * Revision 1.31 2001/06/14 19:35:11 rgb + * Update copyright date. + * + * Revision 1.30 2001/05/30 08:14:03 rgb + * Removed vestiges of esp-null transforms. + * + * Revision 1.29 2001/01/30 23:42:47 rgb + * Allow pfkey msgs from pid other than user context required for ACQUIRE + * and subsequent ADD or UDATE. + * + * Revision 1.28 2000/11/06 04:30:40 rgb + * Add Svenning's adaptive content compression. + * + * Revision 1.27 2000/09/19 00:38:25 rgb + * Fixed algorithm name bugs introduced for ipcomp. + * + * Revision 1.26 2000/09/17 21:36:48 rgb + * Added proto2txt macro. + * + * Revision 1.25 2000/09/17 18:56:47 rgb + * Added IPCOMP support. + * + * Revision 1.24 2000/09/12 19:34:12 rgb + * Defined XF_IP6 from Gerhard for ipv6 tunnel support. + * + * Revision 1.23 2000/09/12 03:23:14 rgb + * Cleaned out now unused tdb_xform and tdb_xdata members of struct tdb. + * + * Revision 1.22 2000/09/08 19:12:56 rgb + * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG. + * + * Revision 1.21 2000/09/01 18:32:43 rgb + * Added (disabled) sensitivity members to tdb struct. + * + * Revision 1.20 2000/08/30 05:31:01 rgb + * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst. + * Kill remainder of tdb_xform, tdb_xdata, xformsw. + * + * Revision 1.19 2000/08/01 14:51:52 rgb + * Removed _all_ remaining traces of DES. + * + * Revision 1.18 2000/01/21 06:17:45 rgb + * Tidied up spacing. + * + * Revision 1.17 1999/11/17 15:53:40 rgb + * Changed all occurrences of #include "../../../lib/freeswan.h" + * to #include which works due to -Ilibfreeswan in the + * klips/net/ipsec/Makefile. + * + * Revision 1.16 1999/10/16 04:23:07 rgb + * Add stats for replaywin_errs, replaywin_max_sequence_difference, + * authentication errors, encryption size errors, encryption padding + * errors, and time since last packet. + * + * Revision 1.15 1999/10/16 00:29:11 rgb + * Added SA lifetime packet counting variables. + * + * Revision 1.14 1999/10/01 00:04:14 rgb + * Added tdb structure locking. + * Add function to initialize tdb hash table. + * + * Revision 1.13 1999/04/29 15:20:57 rgb + * dd return values to init and cleanup functions. + * Eliminate unnessessary usage of tdb_xform member to further switch + * away from the transform switch to the algorithm switch. + * Change gettdb parameter to a pointer to reduce stack loading and + * facilitate parameter sanity checking. + * Add a parameter to tdbcleanup to be able to delete a class of SAs. + * + * Revision 1.12 1999/04/15 15:37:25 rgb + * Forward check changes from POST1_00 branch. + * + * Revision 1.9.2.2 1999/04/13 20:35:57 rgb + * Fix spelling mistake in comment. + * + * Revision 1.9.2.1 1999/03/30 17:13:52 rgb + * Extend struct tdb to support pfkey. + * + * Revision 1.11 1999/04/11 00:29:01 henry + * GPL boilerplate + * + * Revision 1.10 1999/04/06 04:54:28 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.9 1999/01/26 02:09:31 rgb + * Removed CONFIG_IPSEC_ALGO_SWITCH macro. + * Removed dead code. + * + * Revision 1.8 1999/01/22 06:29:35 rgb + * Added algorithm switch code. + * Cruft clean-out. + * + * Revision 1.7 1998/11/10 05:37:35 rgb + * Add support for SA direction flag. + * + * Revision 1.6 1998/10/19 14:44:29 rgb + * Added inclusion of freeswan.h. + * sa_id structure implemented and used: now includes protocol. + * + * Revision 1.5 1998/08/12 00:12:30 rgb + * Added macros for new xforms. Added prototypes for new xforms. + * + * Revision 1.4 1998/07/28 00:04:20 rgb + * Add macro for clearing the SA table. + * + * Revision 1.3 1998/07/14 18:06:46 rgb + * Added #ifdef __KERNEL__ directives to restrict scope of header. + * + * Revision 1.2 1998/06/23 03:02:19 rgb + * Created a prototype for ipsec_tdbcleanup when it was moved from + * ipsec_init.c. + * + * Revision 1.1 1998/06/18 21:27:51 henry + * move sources from klips/src to klips/net/ipsec, to keep stupid + * kernel-build scripts happier in the presence of symlinks + * + * Revision 1.4 1998/06/11 05:55:31 rgb + * Added transform version string pointer to xformsw structure definition. + * Added extern declarations for transform version strings. + * + * Revision 1.3 1998/05/18 22:02:54 rgb + * Modify the *_zeroize function prototypes to include one parameter. + * + * Revision 1.2 1998/04/21 21:29:08 rgb + * Rearrange debug switches to change on the fly debug output from user + * space. Only kernel changes checked in at this time. radij.c was also + * changed to temporarily remove buggy debugging code in rj_delete causing + * an OOPS and hence, netlink device open errors. + * + * Revision 1.1 1998/04/09 03:06:14 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:06 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.5 1997/06/03 04:24:48 ji + * Added ESP-3DES-MD5-96 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * Added new transforms. + * + * Revision 0.3 1996/11/20 14:39:04 ji + * Minor cleanups. + * Rationalized debugging code. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/ipsec_xmit.h b/src/libfreeswan/ipsec_xmit.h new file mode 100644 index 000000000..033984886 --- /dev/null +++ b/src/libfreeswan/ipsec_xmit.h @@ -0,0 +1,140 @@ +/* + * IPSEC tunneling code + * Copyright (C) 1996, 1997 John Ioannidis. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_xmit.h,v 1.3 2004/06/13 19:37:07 as Exp $ + */ + +#include "freeswan/ipsec_sa.h" + +enum ipsec_xmit_value +{ + IPSEC_XMIT_STOLEN=2, + IPSEC_XMIT_PASS=1, + IPSEC_XMIT_OK=0, + IPSEC_XMIT_ERRMEMALLOC=-1, + IPSEC_XMIT_ESP_BADALG=-2, + IPSEC_XMIT_BADPROTO=-3, + IPSEC_XMIT_ESP_PUSHPULLERR=-4, + IPSEC_XMIT_BADLEN=-5, + IPSEC_XMIT_AH_BADALG=-6, + IPSEC_XMIT_SAIDNOTFOUND=-7, + IPSEC_XMIT_SAIDNOTLIVE=-8, + IPSEC_XMIT_REPLAYROLLED=-9, + IPSEC_XMIT_LIFETIMEFAILED=-10, + IPSEC_XMIT_CANNOTFRAG=-11, + IPSEC_XMIT_MSSERR=-12, + IPSEC_XMIT_ERRSKBALLOC=-13, + IPSEC_XMIT_ENCAPFAIL=-14, + IPSEC_XMIT_NODEV=-15, + IPSEC_XMIT_NOPRIVDEV=-16, + IPSEC_XMIT_NOPHYSDEV=-17, + IPSEC_XMIT_NOSKB=-18, + IPSEC_XMIT_NOIPV6=-19, + IPSEC_XMIT_NOIPOPTIONS=-20, + IPSEC_XMIT_TTLEXPIRED=-21, + IPSEC_XMIT_BADHHLEN=-22, + IPSEC_XMIT_PUSHPULLERR=-23, + IPSEC_XMIT_ROUTEERR=-24, + IPSEC_XMIT_RECURSDETECT=-25, + IPSEC_XMIT_IPSENDFAILURE=-26, +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + IPSEC_XMIT_ESPUDP=-27, +#endif +}; + +struct ipsec_xmit_state +{ + struct sk_buff *skb; /* working skb pointer */ + struct device *dev; /* working dev pointer */ + struct ipsecpriv *prv; /* Our device' private space */ + struct sk_buff *oskb; /* Original skb pointer */ + struct net_device_stats *stats; /* This device's statistics */ + struct iphdr *iph; /* Our new IP header */ + __u32 newdst; /* The other SG's IP address */ + __u32 orgdst; /* Original IP destination address */ + __u32 orgedst; /* 1st SG's IP address */ + __u32 newsrc; /* The new source SG's IP address */ + __u32 orgsrc; /* Original IP source address */ + __u32 innersrc; /* Innermost IP source address */ + int iphlen; /* IP header length */ + int pyldsz; /* upper protocol payload size */ + int headroom; + int tailroom; + int max_headroom; /* The extra header space needed */ + int max_tailroom; /* The extra stuffing needed */ + int ll_headroom; /* The extra link layer hard_header space needed */ + int tot_headroom; /* The total header space needed */ + int tot_tailroom; /* The totalstuffing needed */ + __u8 *saved_header; /* saved copy of the hard header */ + unsigned short sport, dport; + + struct sockaddr_encap matcher; /* eroute search key */ + struct eroute *eroute; + struct ipsec_sa *ipsp, *ipsq; /* ipsec_sa pointers */ + char sa_txt[SATOA_BUF]; + size_t sa_len; + int hard_header_stripped; /* has the hard header been removed yet? */ + int hard_header_len; + struct device *physdev; +/* struct device *virtdev; */ + short physmtu; + short mtudiff; +#ifdef NET_21 + struct rtable *route; +#endif /* NET_21 */ + struct sa_id outgoing_said; +#ifdef NET_21 + int pass; +#endif /* NET_21 */ + int error; + uint32_t eroute_pid; + struct ipsec_sa ips; +#ifdef CONFIG_IPSEC_NAT_TRAVERSAL + uint8_t natt_type; + uint8_t natt_head; + uint16_t natt_sport; + uint16_t natt_dport; +#endif +}; + +#if 0 /* save for alg refactorisation */ +struct xform_functions +{ + enum ipsec_xmit_value (*checks)(struct ipsec_xmit_state *ixs, + struct sk_buff *skb); + enum ipsec_xmit_value (*encrypt)(struct ipsec_xmit_state *ixs); + + enum ipsec_xmit_value (*setup_auth)(struct ipsec_xmit_state *ixs, + struct sk_buff *skb, + __u32 *replay, + unsigned char **authenticator); + enum ipsec_xmit_value (*calc_auth)(struct ipsec_xmit_state *ixs, + struct sk_buff *skb); +}; +#endif + +enum ipsec_xmit_value +ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs); + +enum ipsec_xmit_value +ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs); + +enum ipsec_xmit_value +ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs); + +extern int ipsec_xmit_trap_count; +extern int ipsec_xmit_trap_sendcount; + +extern void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er); diff --git a/src/libfreeswan/keyblobtoid.3 b/src/libfreeswan/keyblobtoid.3 new file mode 100644 index 000000000..be381531a --- /dev/null +++ b/src/libfreeswan/keyblobtoid.3 @@ -0,0 +1,103 @@ +.TH IPSEC_KEYBLOBTOID 3 "25 March 2002" +.\" RCSID $Id: keyblobtoid.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec keyblobtoid, splitkeytoid \- generate key IDs from RSA keys +.SH SYNOPSIS +.B "#include +.sp +.B "size_t keyblobtoid(const unsigned char *blob," +.ti +1c +.B "size_t bloblen, char *dst, size_t dstlen);" +.br +.B "size_t splitkeytoid(const unsigned char *e, size_t elen," +.ti +1c +.B "const unsigned char *m, size_t mlen, char *dst, +.ti +1c +.B "size_t dstlen);" +.SH DESCRIPTION +.I Keyblobtoid +and +.I splitkeytoid +generate +key IDs +from RSA keys, +for use in messages and reporting, +writing the result to +.IR dst . +A +.I key ID +is a short ASCII string identifying a key; +currently it is just the first nine characters of the base64 +encoding of the RFC 2537/3110 ``byte blob'' representation of the key. +(Beware that no finite key ID can be collision-proof: +there is always some small chance of two random keys having the +same ID.) +.PP +.I Keyblobtoid +generates a key ID from a key which is already in the form of an +RFC 2537/3110 binary key +.I blob +(encoded exponent length, exponent, modulus). +.PP +.I Splitkeytoid +generates a key ID from a key given in the form of a separate +(binary) exponent +.I e +and modulus +.IR m . +.PP +The +.I dstlen +parameter of either +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.I freeswan.h +header file defines a constant +.B KEYID_BUF +which is the size of a buffer large enough for worst-case results. +.PP +Both functions return +.B 0 +for a failure, and otherwise +always return the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.P +With keys generated by +.IR ipsec_rsasigkey (3), +the first two base64 digits are always the same, +and the third carries only about one bit of information. +It's worse with keys using longer fixed exponents, +e.g. the 24-bit exponent that's common in X.509 certificates. +However, being able to relate key IDs to the full +base64 text form of keys by eye is sufficiently useful that this +waste of space seems justifiable. +The choice of nine digits is a compromise between bulk and +probability of collision. +.SH SEE ALSO +RFC 3110, +\fIRSA/SHA-1 SIGs and RSA KEYs in the Domain Name System (DNS)\fR, +Eastlake, 2001 +(superseding the older but better-known RFC 2537). +.SH DIAGNOSTICS +Fatal errors are: +key too short to supply enough bits to construct a complete key ID +(almost certainly indicating a garbage key); +exponent too long for its length to be representable. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. diff --git a/src/libfreeswan/keyblobtoid.c b/src/libfreeswan/keyblobtoid.c new file mode 100644 index 000000000..7798601cf --- /dev/null +++ b/src/libfreeswan/keyblobtoid.c @@ -0,0 +1,148 @@ +/* + * generate printable key IDs + * Copyright (C) 2002 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: keyblobtoid.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - keyblobtoid - generate a printable key ID from an RFC 2537/3110 key blob + * Current algorithm is just to use first nine base64 digits. + */ +size_t +keyblobtoid(src, srclen, dst, dstlen) +const unsigned char *src; +size_t srclen; +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + char buf[KEYID_BUF]; + size_t ret; +# define NDIG 9 + + if (srclen < (NDIG*6 + 7)/8) { + strcpy(buf, "?len= ?"); + buf[5] = '0' + srclen; + ret = 0; + } else { + (void) datatot(src, srclen, 64, buf, NDIG+1); + ret = NDIG+1; + } + + if (dstlen > 0) { + if (strlen(buf)+1 > dstlen) + *(buf + dstlen - 1) = '\0'; + strcpy(dst, buf); + } + return ret; +} + +/* + - splitkeytoid - generate a printable key ID from exponent/modulus pair + * Just constructs the beginnings of a key blob and calls keyblobtoid(). + */ +size_t +splitkeytoid(e, elen, m, mlen, dst, dstlen) +const unsigned char *e; +size_t elen; +const unsigned char *m; +size_t mlen; +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + unsigned char buf[KEYID_BUF]; /* ample room */ + unsigned char *bufend = buf + sizeof(buf); + unsigned char *p; + size_t n; + + p = buf; + if (elen <= 255) + *p++ = elen; + else if ((elen &~ 0xffff) == 0) { + *p++ = 0; + *p++ = (elen>>8) & 0xff; + *p++ = elen & 0xff; + } else + return 0; /* unrepresentable exponent length */ + + n = bufend - p; + if (elen < n) + n = elen; + memcpy(p, e, n); + p += n; + + n = bufend - p; + if (n > 0) { + if (mlen < n) + n = mlen; + memcpy(p, m, n); + p += n; + } + + return keyblobtoid(buf, p - buf, dst, dstlen); +} + + + +#ifdef KEYBLOBTOID_MAIN + +#include + +void regress(); + +int +main(argc, argv) +int argc; +char *argv[]; +{ + typedef unsigned char uc; + uc hexblob[] = "\x01\x03\x85\xf2\xd6\x76\x9b\x03\x59\xb6\x21\x52"; + uc hexe[] = "\x03"; + uc hexm[] = "\x85\xf2\xd6\x76\x9b\x03\x59\xb6\x21\x52\xef\x85"; + char b64nine[] = "AQOF8tZ2m"; + char b64six[] = "AQOF8t"; + char buf[100]; + size_t n; + char *b = b64nine; + size_t bl = strlen(b) + 1; + int st = 0; + + n = keyblobtoid(hexblob, strlen(hexblob), buf, sizeof(buf)); + if (n != bl) { + fprintf(stderr, "%s: keyblobtoid returned %d not %d\n", + argv[0], n, bl); + st = 1; + } + if (strcmp(buf, b) != 0) { + fprintf(stderr, "%s: keyblobtoid generated `%s' not `%s'\n", + argv[0], buf, b); + st = 1; + } + n = splitkeytoid(hexe, strlen(hexe), hexm, strlen(hexm), buf, + sizeof(buf)); + if (n != bl) { + fprintf(stderr, "%s: splitkeytoid returned %d not %d\n", + argv[0], n, bl); + st = 1; + } + if (strcmp(buf, b) != 0) { + fprintf(stderr, "%s: splitkeytoid generated `%s' not `%s'\n", + argv[0], buf, b); + st = 1; + } + exit(st); +} + +#endif /* KEYBLOBTOID_MAIN */ diff --git a/src/libfreeswan/optionsfrom.3 b/src/libfreeswan/optionsfrom.3 new file mode 100644 index 000000000..e270475bd --- /dev/null +++ b/src/libfreeswan/optionsfrom.3 @@ -0,0 +1,182 @@ +.TH IPSEC_OPTIONSFROM 3 "16 Oct 1998" +.\" RCSID $Id: optionsfrom.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec optionsfrom \- read additional ``command-line'' options from file +.SH SYNOPSIS +.B "#include +.sp +.B "const char *optionsfrom(char *filename, int *argcp," +.ti +1c +.B "char ***argvp, int optind, FILE *errsto);" +.SH DESCRIPTION +.I Optionsfrom +is called from within a +.IR getopt_long (3) +scan, +as the result of the appearance of an option (preferably +.BR \-\-optionsfrom ) +to insert additional ``command-line'' arguments +into the scan immediately after +the option. +Typically this would be done to pick up options which are +security-sensitive and should not be visible to +.IR ps (1) +and similar commands, +and hence cannot be supplied as part +of the actual command line or the environment. +.PP +.I Optionsfrom +reads the additional arguments from the specified +.IR filename , +allocates a new argument vector to hold pointers to the existing +arguments plus the new ones, +and amends +.I argc +and +.I argv +(via the pointers +.I argcp +and +.IR argvp , +which must point to the +.I argc +and +.I argv +being supplied to +.IR getopt_long (3)) +accordingly. +.I Optind +must be the index, in the original argument vector, +of the next argument. +.PP +If +.I errsto +is NULL, +.I optionsfrom +returns NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +If +.I errsto +is non-NULL and an error occurs, +.I optionsfrom +prints a suitable complaint onto the +.I errsto +descriptor and invokes +.I exit +with an exit status of 2; +this is a convenience for cases where more sophisticated +responses are not required. +.PP +The text of existing arguments is not disturbed by +.IR optionsfrom , +so pointers to them and into them remain valid. +.PP +The file of additional arguments is an ASCII text file. +Lines consisting solely of white space, +and lines beginning with +.BR # , +are comments and are ignored. +Otherwise, a line which does not begin with +.BR \- +is taken to be a single argument; +if it both begins and ends with double-quote ("), +those quotes are stripped off (note, no other processing is done within +the line!). +A line beginning with +.B \- +is considered to contain multiple arguments separated by white space. +.PP +Because +.I optionsfrom +reads its entire file before the +.IR getopt_long (3) +scan is resumed, an +.I optionsfrom +file can contain another +.B \-\-optionsfrom +option. +Obviously, infinite loops are possible here. +If +.I errsto +is non-NULL, +.I optionsfrom +considers it an error to be called more than 100 times. +If +.I errsto +is NULL, +loop detection is up to the caller +(and the internal loop counter is zeroed out). +.SH EXAMPLE +A reasonable way to invoke +.I optionsfrom +would be like so: +.PP +.nf +.ft B +#include + +struct option opts[] = { + /* ... */ + "optionsfrom", 1, NULL, '+', + /* ... */ +}; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int opt; + extern char *optarg; + extern int optind; + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF) + switch (opt) { + /* ... */ + case '+': /* optionsfrom */ + optionsfrom(optarg, &argc, &argv, optind, stderr); + /* does not return on error */ + break; + /* ... */ + } + /* ... */ +.ft +.fi +.SH SEE ALSO +getopt_long(3) +.SH DIAGNOSTICS +Errors in +.I optionsfrom +are: +unable to open file; +attempt to allocate temporary storage for argument or +argument vector failed; +read error in file; +line too long. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The double-quote convention is rather simplistic. +.PP +Line length is currently limited to 1023 bytes, +and there is no continuation convention. +.PP +The restriction of error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +.PP +There is a certain element of unwarranted chumminess with +the insides of +.IR getopt_long (3) +here. +No non-public interfaces are actually used, but +.IR optionsfrom +does rely on +.IR getopt_long (3) +being well-behaved in certain ways that are not actually +promised by the specs. diff --git a/src/libfreeswan/optionsfrom.c b/src/libfreeswan/optionsfrom.c new file mode 100644 index 000000000..d96a3124d --- /dev/null +++ b/src/libfreeswan/optionsfrom.c @@ -0,0 +1,301 @@ +/* + * pick up more options from a file, in the middle of an option scan + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: optionsfrom.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +#include + +#define MAX 100 /* loop-detection limit */ + +/* internal work area */ +struct work { +# define LOTS 1024 + char buf[LOTS]; + char *line; + char *pending; +}; + +static const char *dowork(const char *, int *, char ***, int); +static const char *getanarg(FILE *, struct work *, char **); +static char *getline(FILE *, char *, size_t); + +/* + - optionsfrom - add some options, taken from a file, to argc/argv + * If errsto is non-NULL, does not return in event of error. + */ +const char * /* NULL for success, else string literal */ +optionsfrom(filename, argcp, argvp, optind, errsto) +const char *filename; +int *argcp; /* pointer to argc */ +char ***argvp; /* pointer to argv */ +int optind; /* current optind, number of next argument */ +FILE *errsto; /* where to report errors (NULL means return) */ +{ + const char *e; + static int nuses = 0; + + if (errsto != NULL) { + nuses++; + if (nuses >= MAX) { + fprintf(errsto, + "%s: optionsfrom called %d times, looping?\n", + (*argvp)[0], nuses); + exit(2); + } + } else + nuses = 0; + + e = dowork(filename, argcp, argvp, optind); + if (e != NULL && errsto != NULL) { + fprintf(errsto, "%s: optionsfrom failed: %s\n", (*argvp)[0], e); + exit(2); + } + return e; +} + +/* + - dowork - do all the real work of optionsfrom + * Does not alter the existing arguments, but does relocate and alter + * the argv pointer vector. + */ +static const char * /* NULL for success, else string literal */ +dowork(filename, argcp, argvp, optind) +const char *filename; +int *argcp; /* pointer to argc */ +char ***argvp; /* pointer to argv */ +int optind; /* current optind, number of next argument */ +{ + char **newargv; + char **tmp; + int newargc; + int next; /* place for next argument */ + int room; /* how many more new arguments we can hold */ +# define SOME 10 /* first guess at how many we'll need */ + FILE *f; + int i; + const char *p; + struct work wa; /* for getanarg() */ + + f = fopen(filename, "r"); + if (f == NULL) + return "unable to open file"; + + newargc = *argcp + SOME; + newargv = malloc((newargc+1) * sizeof(char *)); + if (newargv == NULL) + return "unable to allocate memory"; + memcpy(newargv, *argvp, optind * sizeof(char *)); + room = SOME; + next = optind; + + newargv[next] = NULL; + wa.pending = NULL; + while ((p = getanarg(f, &wa, &newargv[next])) == NULL) { + if (room == 0) { + newargc += SOME; + tmp = realloc(newargv, (newargc+1) * sizeof(char *)); + if (tmp == NULL) { + p = "out of space for new argv"; + break; /* NOTE BREAK OUT */ + } + newargv = tmp; + room += SOME; + } + next++; + room--; + } + if (p != NULL && !feof(f)) { /* error of some kind */ + for (i = optind+1; i <= next; i++) + if (newargv[i] != NULL) + free(newargv[i]); + free(newargv); + fclose(f); + return p; + } + + fclose(f); + memcpy(newargv + next, *argvp + optind, + (*argcp+1-optind) * sizeof(char *)); + *argcp += next - optind; + *argvp = newargv; + return NULL; +} + +/* + - getanarg - get a malloced argument from the file + */ +static const char * /* NULL for success, else string literal */ +getanarg(f, w, linep) +FILE *f; +struct work *w; +char **linep; /* where to store pointer if successful */ +{ + size_t len; + char *p; + char *endp; + + while (w->pending == NULL) { /* no pending line */ + if ((w->line = getline(f, w->buf, sizeof(w->buf))) == NULL) + return "error in line read"; /* caller checks EOF */ + if (w->line[0] != '#' && + *(w->line + strspn(w->line, " \t")) != '\0') + w->pending = w->line; + } + + if (w->pending == w->line && w->line[0] != '-') { + /* fresh plain line */ + w->pending = NULL; + p = w->line; + endp = p + strlen(p); + if (*p == '"' && endp > p+1 && *(endp-1) == '"') { + p++; + endp--; + *endp = '\0'; + } + if (w->line == w->buf) { + *linep = malloc(endp - p + 1); + if (*linep == NULL) + return "out of memory for new line"; + strcpy(*linep, p); + } else /* getline already malloced it */ + *linep = p; + return NULL; + } + + /* chip off a piece of a pending line */ + p = w->pending; + p += strspn(p, " \t"); + endp = p + strcspn(p, " \t"); + len = endp - p; + if (*endp != '\0') { + *endp++ = '\0'; + endp += strspn(endp, " \t"); + } + /* endp now points to next real character, or to line-end NUL */ + *linep = malloc(len + 1); + if (*linep == NULL) { + if (w->line != w->buf) + free(w->line); + return "out of memory for new argument"; + } + strcpy(*linep, p); + if (*endp == '\0') { + w->pending = NULL; + if (w->line != w->buf) + free(w->line); + } else + w->pending = endp; + return NULL; +} + +/* + - getline - read a line from the file, trim newline off + */ +static char * /* pointer to line, NULL for eof/error */ +getline(f, buf, bufsize) +FILE *f; +char *buf; /* buffer to use, if convenient */ +size_t bufsize; /* size of buf */ +{ + size_t len; + + if (fgets(buf, bufsize, f) == NULL) + return NULL; + len = strlen(buf); + + if (len < bufsize-1 || buf[bufsize-1] == '\n') { + /* it fit */ + buf[len-1] = '\0'; + return buf; + } + + /* oh crud, buffer overflow */ + /* for now, to hell with it */ + return NULL; +} + + + +#ifdef TEST + +#include + +char usage[] = "Usage: tester [--foo] [--bar] [--optionsfrom file] arg ..."; +struct option opts[] = { + "foo", 0, NULL, 'f', + "bar", 0, NULL, 'b', + "builtin", 0, NULL, 'B', + "optionsfrom", 1, NULL, '+', + "help", 0, NULL, 'h', + "version", 0, NULL, 'v', + 0, 0, NULL, 0, +}; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int opt; + extern char *optarg; + extern int optind; + int errflg = 0; + const char *p; + int i; + FILE *errs = NULL; + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF) + switch (opt) { + case 'f': + case 'b': + break; + case 'B': + errs = stderr; + break; + case '+': /* optionsfrom */ + p = optionsfrom(optarg, &argc, &argv, optind, errs); + if (p != NULL) { + fprintf(stderr, "%s: optionsfrom error: %s\n", + argv[0], p); + exit(1); + } + break; + case 'h': /* help */ + printf("%s\n", usage); + exit(0); + break; + case 'v': /* version */ + printf("1\n"); + exit(0); + break; + case '?': + default: + errflg = 1; + break; + } + if (errflg) { + fprintf(stderr, "%s\n", usage); + exit(2); + } + + for (i = 1; i < argc; i++) + printf("%d: `%s'\n", i, argv[i]); + exit(0); +} + + +#endif /* TEST */ diff --git a/src/libfreeswan/pfkey.h b/src/libfreeswan/pfkey.h new file mode 100644 index 000000000..afa5ce032 --- /dev/null +++ b/src/libfreeswan/pfkey.h @@ -0,0 +1,498 @@ +/* + * FreeS/WAN specific PF_KEY headers + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pfkey.h,v 1.2 2004/03/22 21:53:18 as Exp $ + */ + +#ifndef __NET_IPSEC_PF_KEY_H +#define __NET_IPSEC_PF_KEY_H +#ifdef __KERNEL__ +extern struct proto_ops pfkey_proto_ops; +typedef struct sock pfkey_sock; +extern int debug_pfkey; + +extern /* void */ int pfkey_init(void); +extern /* void */ int pfkey_cleanup(void); + +extern struct sock *pfkey_sock_list; +struct socket_list +{ + struct socket *socketp; + struct socket_list *next; +}; +extern int pfkey_list_insert_socket(struct socket*, struct socket_list**); +extern int pfkey_list_remove_socket(struct socket*, struct socket_list**); +extern struct socket_list *pfkey_open_sockets; +extern struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1]; + +/* + * There is a field-by-field copy in klips/net/ipsec/ipsec_alg.h + * please keep in sync until we migrate all support stuff + * to ipsec_alg objects + */ +struct supported +{ + uint16_t supported_alg_exttype; + uint8_t supported_alg_id; + uint8_t supported_alg_ivlen; + uint16_t supported_alg_minbits; + uint16_t supported_alg_maxbits; +}; +extern struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1]; +struct supported_list +{ + struct supported *supportedp; + struct supported_list *next; +}; +extern int pfkey_list_insert_supported(struct supported*, struct supported_list**); +extern int pfkey_list_remove_supported(struct supported*, struct supported_list**); + +struct sockaddr_key +{ + uint16_t key_family; /* PF_KEY */ + uint16_t key_pad; /* not used */ + uint32_t key_pid; /* process ID */ +}; + +struct pfkey_extracted_data +{ + struct ipsec_sa* ips; + struct ipsec_sa* ips2; + struct eroute *eroute; +}; + +extern int +pfkey_alloc_eroute(struct eroute** eroute); + +extern int +pfkey_sa_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_lifetime_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_address_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_key_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_ident_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_sens_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_prop_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_supported_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_spirange_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_x_satype_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int +pfkey_x_debug_process(struct sadb_ext *pfkey_ext, + struct pfkey_extracted_data* extr); + +extern int pfkey_register_reply(int satype, struct sadb_msg *); +extern int pfkey_upmsg(struct socket *, struct sadb_msg *); +extern int pfkey_expire(struct ipsec_sa *, int); +extern int pfkey_acquire(struct ipsec_sa *); +#else /* ! __KERNEL__ */ + +extern void (*pfkey_debug_func)(const char *message, ...); + +#endif /* __KERNEL__ */ + +extern uint8_t satype2proto(uint8_t satype); +extern uint8_t proto2satype(uint8_t proto); +extern char* satype2name(uint8_t satype); +extern char* proto2name(uint8_t proto); + +struct key_opt +{ + uint32_t key_pid; /* process ID */ + struct sock *sk; +}; + +#define key_pid(sk) ((struct key_opt*)&((sk)->protinfo))->key_pid + +#define IPSEC_PFKEYv2_ALIGN (sizeof(uint64_t)/sizeof(uint8_t)) +#define BITS_PER_OCTET 8 +#define OCTETBITS 8 +#define PFKEYBITS 64 +#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */ +#define ALIGN_N(x,y) (DIVUP(x,y) * y) /* align on y boundary */ + +#define PFKEYv2_MAX_MSGSIZE 4096 + +/* + * PF_KEYv2 permitted and required extensions in and out bitmaps + */ +struct pf_key_ext_parsers_def { + int (*parser)(struct sadb_ext*); + char *parser_name; +}; + + +extern unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/]; +#define EXT_BITS_IN 0 +#define EXT_BITS_OUT 1 +#define EXT_BITS_PERM 0 +#define EXT_BITS_REQ 1 + +extern void pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]); +extern void pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]); +extern void pfkey_msg_free(struct sadb_msg **pfkey_msg); + +extern int pfkey_msg_parse(struct sadb_msg *pfkey_msg, + struct pf_key_ext_parsers_def *ext_parsers[], + struct sadb_ext **extensions, + int dir); + +/* + * PF_KEYv2 build function prototypes + */ + +int +pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext, + uint8_t msg_type, + uint8_t satype, + uint8_t msg_errno, + uint32_t seq, + uint32_t pid); + +int +pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext, + uint16_t exttype, + uint32_t spi, /* in network order */ + uint8_t replay_window, + uint8_t sa_state, + uint8_t auth, + uint8_t encrypt, + uint32_t flags, + uint32_t/*IPsecSAref_t*/ ref); + +int +pfkey_sa_build(struct sadb_ext ** pfkey_ext, + uint16_t exttype, + uint32_t spi, /* in network order */ + uint8_t replay_window, + uint8_t sa_state, + uint8_t auth, + uint8_t encrypt, + uint32_t flags); + +int +pfkey_lifetime_build(struct sadb_ext ** pfkey_ext, + uint16_t exttype, + uint32_t allocations, + uint64_t bytes, + uint64_t addtime, + uint64_t usetime, + uint32_t packets); + +int +pfkey_address_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint8_t proto, + uint8_t prefixlen, + struct sockaddr* address); + +int +pfkey_key_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint16_t key_bits, + char* key); + +int +pfkey_ident_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint16_t ident_type, + uint64_t ident_id, + uint8_t ident_len, + char* ident_string); + +#ifdef __KERNEL__ +extern int pfkey_nat_t_new_mapping(struct ipsec_sa *, struct sockaddr *, __u16); +extern int pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr); +extern int pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr); +#endif /* __KERNEL__ */ + +int +pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext, + uint8_t type); +int +pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint16_t port); + +int +pfkey_sens_build(struct sadb_ext** pfkey_ext, + uint32_t dpd, + uint8_t sens_level, + uint8_t sens_len, + uint64_t* sens_bitmap, + uint8_t integ_level, + uint8_t integ_len, + uint64_t* integ_bitmap); + +int +pfkey_x_protocol_build(struct sadb_ext **, uint8_t); + + +int +pfkey_prop_build(struct sadb_ext** pfkey_ext, + uint8_t replay, + unsigned int comb_num, + struct sadb_comb* comb); + +int +pfkey_supported_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + unsigned int alg_num, + struct sadb_alg* alg); + +int +pfkey_spirange_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint32_t min, + uint32_t max); + +int +pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext); + +int +pfkey_x_satype_build(struct sadb_ext** pfkey_ext, + uint8_t satype); + +int +pfkey_x_debug_build(struct sadb_ext** pfkey_ext, + uint32_t tunnel, + uint32_t netlink, + uint32_t xform, + uint32_t eroute, + uint32_t spi, + uint32_t radij, + uint32_t esp, + uint32_t ah, + uint32_t rcv, + uint32_t pfkey, + uint32_t ipcomp, + uint32_t verbose); + +int +pfkey_msg_build(struct sadb_msg** pfkey_msg, + struct sadb_ext* extensions[], + int dir); + +/* in pfkey_v2_debug.c - routines to decode numbers -> strings */ +const char * +pfkey_v2_sadb_ext_string(int extnum); + +const char * +pfkey_v2_sadb_type_string(int sadb_type); + + +#endif /* __NET_IPSEC_PF_KEY_H */ + +/* + * $Log: pfkey.h,v $ + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.2.1.2.1 2004/03/16 09:48:18 as + * alg-0.8.1rc12 patch merged + * + * Revision 1.1.2.1 2004/03/15 22:30:06 as + * nat-0.6c patch merged + * + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.42 2003/08/25 22:08:19 mcr + * removed pfkey_proto_init() from pfkey.h for 2.6 support. + * + * Revision 1.41 2003/05/07 17:28:57 mcr + * new function pfkey_debug_func added for us in debugging from + * pfkey library. + * + * Revision 1.40 2003/01/30 02:31:34 rgb + * + * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. + * + * Revision 1.39 2002/09/20 15:40:21 rgb + * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc(). + * Added ref parameter to pfkey_sa_build(). + * Cleaned out unused cruft. + * + * Revision 1.38 2002/05/14 02:37:24 rgb + * Change all references to tdb, TDB or Tunnel Descriptor Block to ips, + * ipsec_sa or ipsec_sa. + * Added function prototypes for the functions moved to + * pfkey_v2_ext_process.c. + * + * Revision 1.37 2002/04/24 07:36:49 mcr + * Moved from ./lib/pfkey.h,v + * + * Revision 1.36 2002/01/20 20:34:49 mcr + * added pfkey_v2_sadb_type_string to decode sadb_type to string. + * + * Revision 1.35 2001/11/27 05:27:47 mcr + * pfkey parses are now maintained by a structure + * that includes their name for debug purposes. + * + * Revision 1.34 2001/11/26 09:23:53 rgb + * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes. + * + * Revision 1.33 2001/11/06 19:47:47 rgb + * Added packet parameter to lifetime and comb structures. + * + * Revision 1.32 2001/09/08 21:13:34 rgb + * Added pfkey ident extension support for ISAKMPd. (NetCelo) + * + * Revision 1.31 2001/06/14 19:35:16 rgb + * Update copyright date. + * + * Revision 1.30 2001/02/27 07:04:52 rgb + * Added satype2name prototype. + * + * Revision 1.29 2001/02/26 19:59:33 rgb + * Ditch unused sadb_satype2proto[], replaced by satype2proto(). + * + * Revision 1.28 2000/10/10 20:10:19 rgb + * Added support for debug_ipcomp and debug_verbose to klipsdebug. + * + * Revision 1.27 2000/09/21 04:20:45 rgb + * Fixed array size off-by-one error. (Thanks Svenning!) + * + * Revision 1.26 2000/09/12 03:26:05 rgb + * Added pfkey_acquire prototype. + * + * Revision 1.25 2000/09/08 19:21:28 rgb + * Fix pfkey_prop_build() parameter to be only single indirection. + * + * Revision 1.24 2000/09/01 18:46:42 rgb + * Added a supported algorithms array lists, one per satype and registered + * existing algorithms. + * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to + * list. + * + * Revision 1.23 2000/08/27 01:55:26 rgb + * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code. + * + * Revision 1.22 2000/08/20 21:39:23 rgb + * Added kernel prototypes for kernel funcitions pfkey_upmsg() and + * pfkey_expire(). + * + * Revision 1.21 2000/08/15 17:29:23 rgb + * Fixes from SZI to untested pfkey_prop_build(). + * + * Revision 1.20 2000/05/10 20:14:19 rgb + * Fleshed out sensitivity, proposal and supported extensions. + * + * Revision 1.19 2000/03/16 14:07:23 rgb + * Renamed ALIGN macro to avoid fighting with others in kernel. + * + * Revision 1.18 2000/01/22 23:24:06 rgb + * Added prototypes for proto2satype(), satype2proto() and proto2name(). + * + * Revision 1.17 2000/01/21 06:26:59 rgb + * Converted from double tdb arguments to one structure (extr) + * containing pointers to all temporary information structures. + * Added klipsdebug switching capability. + * Dropped unused argument to pfkey_x_satype_build(). + * + * Revision 1.16 1999/12/29 21:17:41 rgb + * Changed pfkey_msg_build() I/F to include a struct sadb_msg** + * parameter for cleaner manipulation of extensions[] and to guard + * against potential memory leaks. + * Changed the I/F to pfkey_msg_free() for the same reason. + * + * Revision 1.15 1999/12/09 23:12:54 rgb + * Added macro for BITS_PER_OCTET. + * Added argument to pfkey_sa_build() to do eroutes. + * + * Revision 1.14 1999/12/08 20:33:25 rgb + * Changed sa_family_t to uint16_t for 2.0.xx compatibility. + * + * Revision 1.13 1999/12/07 19:53:40 rgb + * Removed unused first argument from extension parsers. + * Changed __u* types to uint* to avoid use of asm/types.h and + * sys/types.h in userspace code. + * Added function prototypes for pfkey message and extensions + * initialisation and cleanup. + * + * Revision 1.12 1999/12/01 22:19:38 rgb + * Change pfkey_sa_build to accept an SPI in network byte order. + * + * Revision 1.11 1999/11/27 11:55:26 rgb + * Added extern sadb_satype2proto to enable moving protocol lookup table + * to lib/pfkey_v2_parse.c. + * Delete unused, moved typedefs. + * Add argument to pfkey_msg_parse() for direction. + * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. + * + * Revision 1.10 1999/11/23 22:29:21 rgb + * This file has been moved in the distribution from klips/net/ipsec to + * lib. + * Add macros for dealing with alignment and rounding up more opaquely. + * The uint_t type defines have been moved to freeswan.h to avoid + * chicken-and-egg problems. + * Add macros for dealing with alignment and rounding up more opaque. + * Added prototypes for using extention header bitmaps. + * Added prototypes of all the build functions. + * + * Revision 1.9 1999/11/20 21:59:48 rgb + * Moved socketlist type declarations and prototypes for shared use. + * Slightly modified scope of sockaddr_key declaration. + * + * Revision 1.8 1999/11/17 14:34:25 rgb + * Protect sa_family_t from being used in userspace with GLIBC<2. + * + * Revision 1.7 1999/10/27 19:40:35 rgb + * Add a maximum PFKEY packet size macro. + * + * Revision 1.6 1999/10/26 16:58:58 rgb + * Created a sockaddr_key and key_opt socket extension structures. + * + * Revision 1.5 1999/06/10 05:24:41 rgb + * Renamed variables to reduce confusion. + * + * Revision 1.4 1999/04/29 15:21:11 rgb + * Add pfkey support to debugging. + * Add return values to init and cleanup functions. + * + * Revision 1.3 1999/04/15 17:58:07 rgb + * Add RCSID labels. + * + */ diff --git a/src/libfreeswan/pfkey_v2_build.c b/src/libfreeswan/pfkey_v2_build.c new file mode 100644 index 000000000..340c12cfe --- /dev/null +++ b/src/libfreeswan/pfkey_v2_build.c @@ -0,0 +1,1435 @@ +/* + * RFC2367 PF_KEYv2 Key management API message parser + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pfkey_v2_build.c,v 1.4 2005/04/07 19:43:52 as Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_parser.c. + */ + +char pfkey_v2_build_c_version[] = "$Id: pfkey_v2_build.c,v 1.4 2005/04/07 19:43:52 as Exp $"; + +/* + * Some ugly stuff to allow consistent debugging code for use in the + * kernel and in user space +*/ + +#ifdef __KERNEL__ + +# include /* for printk */ + +# include "freeswan/ipsec_kversion.h" /* for malloc switch */ +# ifdef MALLOC_SLAB +# include /* kmalloc() */ +# else /* MALLOC_SLAB */ +# include /* kmalloc() */ +# endif /* MALLOC_SLAB */ +# include /* error codes */ +# include /* size_t */ +# include /* mark_bh */ + +# include /* struct device, and other headers */ +# include /* eth_type_trans */ +# include /* struct iphdr */ +# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# include /* struct ipv6hdr */ +# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + +# define MALLOC(size) kmalloc(size, GFP_ATOMIC) +# define FREE(obj) kfree(obj) +# include +#else /* __KERNEL__ */ + +# include +# include +# include +# include +# include /* memset */ + +# include +unsigned int pfkey_lib_debug = 0; + +void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1); + +/* #define PLUTO */ + +#define DEBUGGING(args...) if(pfkey_lib_debug) { \ + if(pfkey_debug_func != NULL) { \ + (*pfkey_debug_func)("pfkey_lib_debug:" args); \ + } else { \ + printf("pfkey_lib_debug:" args); \ + } } +# define MALLOC(size) malloc(size) +# define FREE(obj) free(obj) +#endif /* __KERNEL__ */ + +#include +#include + +#ifdef __KERNEL__ + +#include "freeswan/radij.h" /* rd_nodes */ +#include "freeswan/ipsec_encap.h" /* sockaddr_encap */ + +# define DEBUGGING(args...) \ + KLIPS_PRINT(debug_pfkey, "klips_debug:" args) +#endif /* __KERNEL__ */ + +#include "ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */ + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +void +pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + int i; + + for (i = 0; i != SADB_EXT_MAX + 1; i++) { + extensions[i] = NULL; + } +} + +void +pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + int i; + + if (!extensions) { + return; + } + + if (extensions[0]) { + memset(extensions[0], 0, sizeof(struct sadb_msg)); + FREE(extensions[0]); + extensions[0] = NULL; + } + + for (i = 1; i != SADB_EXT_MAX + 1; i++) { + if(extensions[i]) { + memset(extensions[i], 0, extensions[i]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); + FREE(extensions[i]); + extensions[i] = NULL; + } + } +} + +void +pfkey_msg_free(struct sadb_msg **pfkey_msg) +{ + if (*pfkey_msg) { + memset(*pfkey_msg, 0, (*pfkey_msg)->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); + FREE(*pfkey_msg); + *pfkey_msg = NULL; + } +} + +/* Default extension builders taken from the KLIPS code */ + +int +pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext, + uint8_t msg_type, + uint8_t satype, + uint8_t msg_errno, + uint32_t seq, + uint32_t pid) +{ + int error = 0; + struct sadb_msg *pfkey_msg = (struct sadb_msg *)*pfkey_ext; + + DEBUGGING( + "pfkey_msg_hdr_build:\n"); + DEBUGGING( + "pfkey_msg_hdr_build: " + "on_entry &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n", + &pfkey_ext, + pfkey_ext, + *pfkey_ext); + /* sanity checks... */ + if (pfkey_msg) { + DEBUGGING( + "pfkey_msg_hdr_build: " + "why is pfkey_msg already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (!msg_type) { + DEBUGGING( + "pfkey_msg_hdr_build: " + "msg type not set, must be non-zero..\n"); + SENDERR(EINVAL); + } + + if (msg_type > SADB_MAX) { + DEBUGGING( + "pfkey_msg_hdr_build: " + "msg type too large:%d.\n", + msg_type); + SENDERR(EINVAL); + } + + if (satype > SADB_SATYPE_MAX) { + DEBUGGING( + "pfkey_msg_hdr_build: " + "satype %d > max %d\n", + satype, SADB_SATYPE_MAX); + SENDERR(EINVAL); + } + + pfkey_msg = (struct sadb_msg*)MALLOC(sizeof(struct sadb_msg)); + *pfkey_ext = (struct sadb_ext*)pfkey_msg; + + if (pfkey_msg == NULL) { + DEBUGGING( + "pfkey_msg_hdr_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_msg, 0, sizeof(struct sadb_msg)); + + pfkey_msg->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; + + pfkey_msg->sadb_msg_type = msg_type; + pfkey_msg->sadb_msg_satype = satype; + + pfkey_msg->sadb_msg_version = PF_KEY_V2; + pfkey_msg->sadb_msg_errno = msg_errno; + pfkey_msg->sadb_msg_reserved = 0; + pfkey_msg->sadb_msg_seq = seq; + pfkey_msg->sadb_msg_pid = pid; + DEBUGGING( + "pfkey_msg_hdr_build: " + "on_exit &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n", + &pfkey_ext, + pfkey_ext, + *pfkey_ext); +errlab: + return error; +} + +int +pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext, + uint16_t exttype, + uint32_t spi, + uint8_t replay_window, + uint8_t sa_state, + uint8_t auth, + uint8_t encrypt, + uint32_t flags, + uint32_t/*IPsecSAref_t*/ ref) +{ + int error = 0; + struct sadb_sa *pfkey_sa = (struct sadb_sa *)*pfkey_ext; + + DEBUGGING( + "pfkey_sa_build: " + "spi=%08x replay=%d sa_state=%d auth=%d encrypt=%d flags=%d\n", + ntohl(spi), /* in network order */ + replay_window, + sa_state, + auth, + encrypt, + flags); + /* sanity checks... */ + if (pfkey_sa) { + DEBUGGING( + "pfkey_sa_build: " + "why is pfkey_sa already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (exttype != SADB_EXT_SA + && exttype != SADB_X_EXT_SA2) { + DEBUGGING( + "pfkey_sa_build: " + "invalid exttype=%d.\n", + exttype); + SENDERR(EINVAL); + } + + if (replay_window > 64) { + DEBUGGING( + "pfkey_sa_build: " + "replay window size: %d -- must be 0 <= size <= 64\n", + replay_window); + SENDERR(EINVAL); + } + + if (auth > SADB_AALG_MAX) { + DEBUGGING( + "pfkey_sa_build: " + "auth=%d > SADB_AALG_MAX=%d.\n", + auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if (encrypt > SADB_EALG_MAX) { + DEBUGGING( + "pfkey_sa_build: " + "encrypt=%d > SADB_EALG_MAX=%d.\n", + encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if (sa_state > SADB_SASTATE_MAX) { + DEBUGGING( + "pfkey_sa_build: " + "sa_state=%d exceeds MAX=%d.\n", + sa_state, + SADB_SASTATE_MAX); + SENDERR(EINVAL); + } + + if (sa_state == SADB_SASTATE_DEAD) { + DEBUGGING( + "pfkey_sa_build: " + "sa_state=%d is DEAD=%d is not allowed.\n", + sa_state, + SADB_SASTATE_DEAD); + SENDERR(EINVAL); + } + + if ((IPSEC_SAREF_NULL != ref) && (ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) { + DEBUGGING( + "pfkey_sa_build: " + "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n", + ref, + IPSEC_SAREF_NULL, + IPSEC_SA_REF_TABLE_NUM_ENTRIES); + SENDERR(EINVAL); + } + + pfkey_sa = (struct sadb_sa*)MALLOC(sizeof(struct sadb_sa)); + *pfkey_ext = (struct sadb_ext*)pfkey_sa; + + if (pfkey_sa == NULL) { + DEBUGGING( + "pfkey_sa_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_sa, 0, sizeof(struct sadb_sa)); + + pfkey_sa->sadb_sa_len = sizeof(*pfkey_sa) / IPSEC_PFKEYv2_ALIGN; + pfkey_sa->sadb_sa_exttype = exttype; + pfkey_sa->sadb_sa_spi = spi; + pfkey_sa->sadb_sa_replay = replay_window; + pfkey_sa->sadb_sa_state = sa_state; + pfkey_sa->sadb_sa_auth = auth; + pfkey_sa->sadb_sa_encrypt = encrypt; + pfkey_sa->sadb_sa_flags = flags; + pfkey_sa->sadb_x_sa_ref = ref; + +errlab: + return error; +} + +int +pfkey_sa_build(struct sadb_ext ** pfkey_ext, + uint16_t exttype, + uint32_t spi, + uint8_t replay_window, + uint8_t sa_state, + uint8_t auth, + uint8_t encrypt, + uint32_t flags) +{ + return pfkey_sa_ref_build(pfkey_ext, + exttype, + spi, + replay_window, + sa_state, + auth, + encrypt, + flags, + IPSEC_SAREF_NULL); +} + +int +pfkey_lifetime_build(struct sadb_ext ** pfkey_ext, + uint16_t exttype, + uint32_t allocations, + uint64_t bytes, + uint64_t addtime, + uint64_t usetime, + uint32_t packets) +{ + int error = 0; + struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)*pfkey_ext; + + DEBUGGING( + "pfkey_lifetime_build:\n"); + /* sanity checks... */ + if (pfkey_lifetime) { + DEBUGGING( + "pfkey_lifetime_build: " + "why is pfkey_lifetime already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (exttype != SADB_EXT_LIFETIME_CURRENT + && exttype != SADB_EXT_LIFETIME_HARD + && exttype != SADB_EXT_LIFETIME_SOFT) { + DEBUGGING( + "pfkey_lifetime_build: " + "invalid exttype=%d.\n", + exttype); + SENDERR(EINVAL); + } + + pfkey_lifetime = (struct sadb_lifetime*)MALLOC(sizeof(struct sadb_lifetime)); + *pfkey_ext = (struct sadb_ext*)pfkey_lifetime; + + if (pfkey_lifetime == NULL) { + DEBUGGING( + "pfkey_lifetime_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_lifetime, 0, sizeof(struct sadb_lifetime)); + + pfkey_lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN; + pfkey_lifetime->sadb_lifetime_exttype = exttype; + pfkey_lifetime->sadb_lifetime_allocations = allocations; + pfkey_lifetime->sadb_lifetime_bytes = bytes; + pfkey_lifetime->sadb_lifetime_addtime = addtime; + pfkey_lifetime->sadb_lifetime_usetime = usetime; + pfkey_lifetime->sadb_x_lifetime_packets = packets; + +errlab: + return error; +} + +int +pfkey_address_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint8_t proto, + uint8_t prefixlen, + struct sockaddr* address) +{ + int error = 0; + int saddr_len = 0; + char ipaddr_txt[ADDRTOT_BUF + 6/*extra for port number*/]; + struct sadb_address *pfkey_address = (struct sadb_address *)*pfkey_ext; + + DEBUGGING( + "pfkey_address_build: " + "exttype=%d proto=%d prefixlen=%d\n", + exttype, + proto, + prefixlen); + /* sanity checks... */ + if (pfkey_address) { + DEBUGGING( + "pfkey_address_build: " + "why is pfkey_address already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (!address) { + DEBUGGING("pfkey_address_build: " + "address is NULL\n"); + SENDERR(EINVAL); + } + + switch(exttype) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_X_EXT_ADDRESS_DST2: + case SADB_X_EXT_ADDRESS_SRC_FLOW: + case SADB_X_EXT_ADDRESS_DST_FLOW: + case SADB_X_EXT_ADDRESS_SRC_MASK: + case SADB_X_EXT_ADDRESS_DST_MASK: + case SADB_X_EXT_NAT_T_OA: + break; + default: + DEBUGGING( + "pfkey_address_build: " + "unrecognised ext_type=%d.\n", + exttype); + SENDERR(EINVAL); + } + + switch (address->sa_family) { + case AF_INET: + DEBUGGING( + "pfkey_address_build: " + "found address family AF_INET.\n"); + saddr_len = sizeof(struct sockaddr_in); + sprintf(ipaddr_txt, "%d.%d.%d.%d:%d" + , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 0) & 0xFF + , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 8) & 0xFF + , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 16) & 0xFF + , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 24) & 0xFF + , ntohs(((struct sockaddr_in*)address)->sin_port)); + break; + case AF_INET6: + DEBUGGING( + "pfkey_address_build: " + "found address family AF_INET6.\n"); + saddr_len = sizeof(struct sockaddr_in6); + sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x-%x" + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[0]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[1]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[2]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[3]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[4]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[5]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[6]) + , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[7]) + , ntohs(((struct sockaddr_in6*)address)->sin6_port)); + break; + default: + DEBUGGING( + "pfkey_address_build: " + "address->sa_family=%d not supported.\n", + address->sa_family); + SENDERR(EPFNOSUPPORT); + } + + DEBUGGING( + "pfkey_address_build: " + "found address=%s.\n", + ipaddr_txt); + if (prefixlen != 0) { + DEBUGGING( + "pfkey_address_build: " + "address prefixes not supported yet.\n"); + SENDERR(EAFNOSUPPORT); /* not supported yet */ + } + + pfkey_address = (struct sadb_address*) + MALLOC(ALIGN_N(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)); + *pfkey_ext = (struct sadb_ext*)pfkey_address; + + if (pfkey_address == NULL) { + DEBUGGING( + "pfkey_lifetime_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_address, + 0, + ALIGN_N(sizeof(struct sadb_address) + saddr_len, + IPSEC_PFKEYv2_ALIGN)); + + pfkey_address->sadb_address_len = DIVUP(sizeof(struct sadb_address) + saddr_len, + IPSEC_PFKEYv2_ALIGN); + + pfkey_address->sadb_address_exttype = exttype; + pfkey_address->sadb_address_proto = proto; + pfkey_address->sadb_address_prefixlen = prefixlen; + pfkey_address->sadb_address_reserved = 0; + + memcpy((char*)pfkey_address + sizeof(struct sadb_address), + address, + saddr_len); + +#if 0 + for (i = 0; i < sizeof(struct sockaddr_in) - offsetof(struct sockaddr_in, sin_zero); i++) { + pfkey_address_s_ska.sin_zero[i] = 0; + } +#endif + DEBUGGING( + "pfkey_address_build: " + "successful.\n"); + + errlab: + return error; +} + +int +pfkey_key_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint16_t key_bits, + char* key) +{ + int error = 0; + struct sadb_key *pfkey_key = (struct sadb_key *)*pfkey_ext; + + DEBUGGING( + "pfkey_key_build:\n"); + /* sanity checks... */ + if (pfkey_key) { + DEBUGGING( + "pfkey_key_build: " + "why is pfkey_key already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (!key_bits) { + DEBUGGING( + "pfkey_key_build: " + "key_bits is zero, it must be non-zero.\n"); + SENDERR(EINVAL); + } + + if ( !((exttype == SADB_EXT_KEY_AUTH) || (exttype == SADB_EXT_KEY_ENCRYPT))) { + DEBUGGING( + "pfkey_key_build: " + "unsupported extension type=%d.\n", + exttype); + SENDERR(EINVAL); + } + + pfkey_key = (struct sadb_key*) + MALLOC(sizeof(struct sadb_key) + + DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN); + *pfkey_ext = (struct sadb_ext*)pfkey_key; + + if (pfkey_key == NULL) { + DEBUGGING( + "pfkey_key_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_key, + 0, + sizeof(struct sadb_key) + + DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN); + + pfkey_key->sadb_key_len = DIVUP(sizeof(struct sadb_key) * IPSEC_PFKEYv2_ALIGN + key_bits, + 64); + pfkey_key->sadb_key_exttype = exttype; + pfkey_key->sadb_key_bits = key_bits; + pfkey_key->sadb_key_reserved = 0; + memcpy((char*)pfkey_key + sizeof(struct sadb_key), + key, + DIVUP(key_bits, 8)); + +errlab: + return error; +} + +int +pfkey_ident_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint16_t ident_type, + uint64_t ident_id, + uint8_t ident_len, + char* ident_string) +{ + int error = 0; + struct sadb_ident *pfkey_ident = (struct sadb_ident *)*pfkey_ext; + int data_len = ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident); + + DEBUGGING( + "pfkey_ident_build:\n"); + /* sanity checks... */ + if (pfkey_ident) { + DEBUGGING( + "pfkey_ident_build: " + "why is pfkey_ident already pointing to something?\n"); + SENDERR(EINVAL); + } + + if ( !((exttype == SADB_EXT_IDENTITY_SRC) || + (exttype == SADB_EXT_IDENTITY_DST))) { + DEBUGGING( + "pfkey_ident_build: " + "unsupported extension type=%d.\n", + exttype); + SENDERR(EINVAL); + } + + if (ident_type == SADB_IDENTTYPE_RESERVED) { + DEBUGGING( + "pfkey_ident_build: " + "ident_type must be non-zero.\n"); + SENDERR(EINVAL); + } + + if (ident_type > SADB_IDENTTYPE_MAX) { + DEBUGGING( + "pfkey_ident_build: " + "identtype=%d out of range.\n", + ident_type); + SENDERR(EINVAL); + } + + if ((ident_type == SADB_IDENTTYPE_PREFIX || + ident_type == SADB_IDENTTYPE_FQDN) && + !ident_string) { + DEBUGGING( + "pfkey_ident_build: " + "string required to allocate size of extension.\n"); + SENDERR(EINVAL); + } + +#if 0 + if (ident_type == SADB_IDENTTYPE_USERFQDN) { + } +#endif + + pfkey_ident = (struct sadb_ident*) + MALLOC(ident_len * IPSEC_PFKEYv2_ALIGN); + *pfkey_ext = (struct sadb_ext*)pfkey_ident; + + if (pfkey_ident == NULL) { + DEBUGGING( + "pfkey_ident_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_ident, 0, ident_len * IPSEC_PFKEYv2_ALIGN); + + pfkey_ident->sadb_ident_len = ident_len; + pfkey_ident->sadb_ident_exttype = exttype; + pfkey_ident->sadb_ident_type = ident_type; + pfkey_ident->sadb_ident_reserved = 0; + pfkey_ident->sadb_ident_id = ident_id; + memcpy((char*)pfkey_ident + sizeof(struct sadb_ident), + ident_string, + data_len); + +errlab: + return error; +} + +int +pfkey_sens_build(struct sadb_ext** pfkey_ext, + uint32_t dpd, + uint8_t sens_level, + uint8_t sens_len, + uint64_t* sens_bitmap, + uint8_t integ_level, + uint8_t integ_len, + uint64_t* integ_bitmap) +{ + int error = 0; + struct sadb_sens *pfkey_sens = (struct sadb_sens *)*pfkey_ext; + int i; + uint64_t* bitmap; + + DEBUGGING( + "pfkey_sens_build:\n"); + /* sanity checks... */ + if (pfkey_sens) { + DEBUGGING( + "pfkey_sens_build: " + "why is pfkey_sens already pointing to something?\n"); + SENDERR(EINVAL); + } + + DEBUGGING( + "pfkey_sens_build: " + "Sorry, I can't build exttype=%d yet.\n", + (*pfkey_ext)->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + + pfkey_sens = (struct sadb_sens*) + MALLOC(sizeof(struct sadb_sens) + + (sens_len + integ_len) * sizeof(uint64_t)); + *pfkey_ext = (struct sadb_ext*)pfkey_sens; + + if (pfkey_sens == NULL) { + DEBUGGING( + "pfkey_sens_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_sens, + 0, + sizeof(struct sadb_sens) + + (sens_len + integ_len) * sizeof(uint64_t)); + + pfkey_sens->sadb_sens_len = (sizeof(struct sadb_sens) + + (sens_len + integ_len) * sizeof(uint64_t)) / IPSEC_PFKEYv2_ALIGN; + pfkey_sens->sadb_sens_exttype = SADB_EXT_SENSITIVITY; + pfkey_sens->sadb_sens_dpd = dpd; + pfkey_sens->sadb_sens_sens_level = sens_level; + pfkey_sens->sadb_sens_sens_len = sens_len; + pfkey_sens->sadb_sens_integ_level = integ_level; + pfkey_sens->sadb_sens_integ_len = integ_len; + pfkey_sens->sadb_sens_reserved = 0; + + bitmap = (uint64_t*)((char*)pfkey_ext + sizeof(struct sadb_sens)); + for (i = 0; i < sens_len; i++) { + *bitmap = sens_bitmap[i]; + bitmap++; + } + for (i = 0; i < integ_len; i++) { + *bitmap = integ_bitmap[i]; + bitmap++; + } + +errlab: + return error; +} + +int +pfkey_prop_build(struct sadb_ext** pfkey_ext, + uint8_t replay, + unsigned int comb_num, + struct sadb_comb* comb) +{ + int error = 0; + int i; + struct sadb_prop *pfkey_prop = (struct sadb_prop *)*pfkey_ext; + struct sadb_comb *combp; + + DEBUGGING( + "pfkey_prop_build:\n"); + /* sanity checks... */ + if (pfkey_prop) { + DEBUGGING( + "pfkey_prop_build: " + "why is pfkey_prop already pointing to something?\n"); + SENDERR(EINVAL); + } + + pfkey_prop = (struct sadb_prop*) + MALLOC(sizeof(struct sadb_prop) + + comb_num * sizeof(struct sadb_comb)); + + *pfkey_ext = (struct sadb_ext*)pfkey_prop; + + if (pfkey_prop == NULL) { + DEBUGGING( + "pfkey_prop_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_prop, + 0, + sizeof(struct sadb_prop) + + comb_num * sizeof(struct sadb_comb)); + + pfkey_prop->sadb_prop_len = (sizeof(struct sadb_prop) + + comb_num * sizeof(struct sadb_comb)) / IPSEC_PFKEYv2_ALIGN; + + pfkey_prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; + pfkey_prop->sadb_prop_replay = replay; + + for (i=0; i<3; i++) { + pfkey_prop->sadb_prop_reserved[i] = 0; + } + + combp = (struct sadb_comb*)((char*)*pfkey_ext + sizeof(struct sadb_prop)); + for (i = 0; i < comb_num; i++) { + memcpy (combp, &(comb[i]), sizeof(struct sadb_comb)); + combp++; + } + +#if 0 + uint8_t sadb_comb_auth; + uint8_t sadb_comb_encrypt; + uint16_t sadb_comb_flags; + uint16_t sadb_comb_auth_minbits; + uint16_t sadb_comb_auth_maxbits; + uint16_t sadb_comb_encrypt_minbits; + uint16_t sadb_comb_encrypt_maxbits; + uint32_t sadb_comb_reserved; + uint32_t sadb_comb_soft_allocations; + uint32_t sadb_comb_hard_allocations; + uint64_t sadb_comb_soft_bytes; + uint64_t sadb_comb_hard_bytes; + uint64_t sadb_comb_soft_addtime; + uint64_t sadb_comb_hard_addtime; + uint64_t sadb_comb_soft_usetime; + uint64_t sadb_comb_hard_usetime; + uint32_t sadb_comb_soft_packets; + uint32_t sadb_comb_hard_packets; +#endif +errlab: + return error; +} + +int +pfkey_supported_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + unsigned int alg_num, + struct sadb_alg* alg) +{ + int error = 0; + unsigned int i; + struct sadb_supported *pfkey_supported = (struct sadb_supported *)*pfkey_ext; + struct sadb_alg *pfkey_alg; + + /* sanity checks... */ + if (pfkey_supported) { + DEBUGGING( + "pfkey_supported_build: " + "why is pfkey_supported already pointing to something?\n"); + SENDERR(EINVAL); + } + + if ( !((exttype == SADB_EXT_SUPPORTED_AUTH) || (exttype == SADB_EXT_SUPPORTED_ENCRYPT))) { + DEBUGGING( + "pfkey_supported_build: " + "unsupported extension type=%d.\n", + exttype); + SENDERR(EINVAL); + } + + pfkey_supported = (struct sadb_supported*) + MALLOC(sizeof(struct sadb_supported) + + alg_num * sizeof(struct sadb_alg)); + + *pfkey_ext = (struct sadb_ext*)pfkey_supported; + + if (pfkey_supported == NULL) { + DEBUGGING( + "pfkey_supported_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_supported, + 0, + sizeof(struct sadb_supported) + + alg_num * + sizeof(struct sadb_alg)); + + pfkey_supported->sadb_supported_len = (sizeof(struct sadb_supported) + + alg_num * + sizeof(struct sadb_alg)) / + IPSEC_PFKEYv2_ALIGN; + pfkey_supported->sadb_supported_exttype = exttype; + pfkey_supported->sadb_supported_reserved = 0; + + pfkey_alg = (struct sadb_alg*)((char*)pfkey_supported + sizeof(struct sadb_supported)); + for(i = 0; i < alg_num; i++) { + memcpy (pfkey_alg, &(alg[i]), sizeof(struct sadb_alg)); + pfkey_alg->sadb_alg_reserved = 0; + pfkey_alg++; + } + +#if 0 + DEBUGGING( + "pfkey_supported_build: " + "Sorry, I can't build exttype=%d yet.\n", + (*pfkey_ext)->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + + uint8_t sadb_alg_id; + uint8_t sadb_alg_ivlen; + uint16_t sadb_alg_minbits; + uint16_t sadb_alg_maxbits; + uint16_t sadb_alg_reserved; +#endif +errlab: + return error; +} + +int +pfkey_spirange_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint32_t min, /* in network order */ + uint32_t max) /* in network order */ +{ + int error = 0; + struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)*pfkey_ext; + + /* sanity checks... */ + if (pfkey_spirange) { + DEBUGGING( + "pfkey_spirange_build: " + "why is pfkey_spirange already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (ntohl(max) < ntohl(min)) { + DEBUGGING( + "pfkey_spirange_build: " + "minspi=%08x must be < maxspi=%08x.\n", + ntohl(min), + ntohl(max)); + SENDERR(EINVAL); + } + + if (ntohl(min) <= 255) { + DEBUGGING( + "pfkey_spirange_build: " + "minspi=%08x must be > 255.\n", + ntohl(min)); + SENDERR(EEXIST); + } + + pfkey_spirange = (struct sadb_spirange*) + MALLOC(sizeof(struct sadb_spirange)); + *pfkey_ext = (struct sadb_ext*)pfkey_spirange; + + if (pfkey_spirange == NULL) { + DEBUGGING( + "pfkey_spirange_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_spirange, + 0, + sizeof(struct sadb_spirange)); + + pfkey_spirange->sadb_spirange_len = sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN; + + pfkey_spirange->sadb_spirange_exttype = SADB_EXT_SPIRANGE; + pfkey_spirange->sadb_spirange_min = min; + pfkey_spirange->sadb_spirange_max = max; + pfkey_spirange->sadb_spirange_reserved = 0; + errlab: + return error; +} + +int +pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext) +{ + int error = 0; + struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)*pfkey_ext; + + /* sanity checks... */ + if (pfkey_x_kmprivate) { + DEBUGGING( + "pfkey_x_kmprivate_build: " + "why is pfkey_x_kmprivate already pointing to something?\n"); + SENDERR(EINVAL); + } + + pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0; + + DEBUGGING( + "pfkey_x_kmprivate_build: " + "Sorry, I can't build exttype=%d yet.\n", + (*pfkey_ext)->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + + pfkey_x_kmprivate = (struct sadb_x_kmprivate*) + MALLOC(sizeof(struct sadb_x_kmprivate)); + *pfkey_ext = (struct sadb_ext*)pfkey_x_kmprivate; + + if (pfkey_x_kmprivate == NULL) { + DEBUGGING( + "pfkey_x_kmprivate_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_x_kmprivate, + 0, + sizeof(struct sadb_x_kmprivate)); + + pfkey_x_kmprivate->sadb_x_kmprivate_len = + sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN; + + pfkey_x_kmprivate->sadb_x_kmprivate_exttype = SADB_X_EXT_KMPRIVATE; + pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0; +errlab: + return error; +} + +int +pfkey_x_satype_build(struct sadb_ext** pfkey_ext, + uint8_t satype) +{ + int error = 0; + int i; + struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)*pfkey_ext; + + DEBUGGING( + "pfkey_x_satype_build:\n"); + /* sanity checks... */ + if (pfkey_x_satype) { + DEBUGGING( + "pfkey_x_satype_build: " + "why is pfkey_x_satype already pointing to something?\n"); + SENDERR(EINVAL); + } + + if (!satype) { + DEBUGGING( + "pfkey_x_satype_build: " + "SA type not set, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if (satype > SADB_SATYPE_MAX) { + DEBUGGING( + "pfkey_x_satype_build: " + "satype %d > max %d\n", + satype, SADB_SATYPE_MAX); + SENDERR(EINVAL); + } + + pfkey_x_satype = (struct sadb_x_satype*) + MALLOC(sizeof(struct sadb_x_satype)); + + *pfkey_ext = (struct sadb_ext*)pfkey_x_satype; + + if (pfkey_x_satype == NULL) { + DEBUGGING( + "pfkey_x_satype_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + memset(pfkey_x_satype, + 0, + sizeof(struct sadb_x_satype)); + + pfkey_x_satype->sadb_x_satype_len = sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN; + + pfkey_x_satype->sadb_x_satype_exttype = SADB_X_EXT_SATYPE2; + pfkey_x_satype->sadb_x_satype_satype = satype; + for (i=0; i<3; i++) { + pfkey_x_satype->sadb_x_satype_reserved[i] = 0; + } + +errlab: + return error; +} + +int +pfkey_x_debug_build(struct sadb_ext** pfkey_ext, + uint32_t tunnel, + uint32_t netlink, + uint32_t xform, + uint32_t eroute, + uint32_t spi, + uint32_t radij, + uint32_t esp, + uint32_t ah, + uint32_t rcv, + uint32_t pfkey, + uint32_t ipcomp, + uint32_t verbose) +{ + int error = 0; + int i; + struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)*pfkey_ext; + + DEBUGGING( + "pfkey_x_debug_build:\n"); + /* sanity checks... */ + if (pfkey_x_debug) { + DEBUGGING( + "pfkey_x_debug_build: " + "why is pfkey_x_debug already pointing to something?\n"); + SENDERR(EINVAL); + } + + DEBUGGING( + "pfkey_x_debug_build: " + "tunnel=%x netlink=%x xform=%x eroute=%x spi=%x radij=%x esp=%x ah=%x rcv=%x pfkey=%x ipcomp=%x verbose=%x?\n", + tunnel, netlink, xform, eroute, spi, radij, esp, ah, rcv, pfkey, ipcomp, verbose); + + pfkey_x_debug = (struct sadb_x_debug*) + MALLOC(sizeof(struct sadb_x_debug)); + *pfkey_ext = (struct sadb_ext*)pfkey_x_debug; + + if (pfkey_x_debug == NULL) { + DEBUGGING( + "pfkey_x_debug_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } +#if 0 + memset(pfkey_x_debug, + 0, + sizeof(struct sadb_x_debug)); +#endif + + pfkey_x_debug->sadb_x_debug_len = sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN; + pfkey_x_debug->sadb_x_debug_exttype = SADB_X_EXT_DEBUG; + + pfkey_x_debug->sadb_x_debug_tunnel = tunnel; + pfkey_x_debug->sadb_x_debug_netlink = netlink; + pfkey_x_debug->sadb_x_debug_xform = xform; + pfkey_x_debug->sadb_x_debug_eroute = eroute; + pfkey_x_debug->sadb_x_debug_spi = spi; + pfkey_x_debug->sadb_x_debug_radij = radij; + pfkey_x_debug->sadb_x_debug_esp = esp; + pfkey_x_debug->sadb_x_debug_ah = ah; + pfkey_x_debug->sadb_x_debug_rcv = rcv; + pfkey_x_debug->sadb_x_debug_pfkey = pfkey; + pfkey_x_debug->sadb_x_debug_ipcomp = ipcomp; + pfkey_x_debug->sadb_x_debug_verbose = verbose; + + for (i=0; i<4; i++) { + pfkey_x_debug->sadb_x_debug_reserved[i] = 0; + } + +errlab: + return error; +} + +int +pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext, + uint8_t type) +{ + int error = 0; + int i; + struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)*pfkey_ext; + + DEBUGGING( + "pfkey_x_nat_t_type_build:\n"); + /* sanity checks... */ + if (pfkey_x_nat_t_type) { + DEBUGGING( + "pfkey_x_nat_t_type_build: " + "why is pfkey_x_nat_t_type already pointing to something?\n"); + SENDERR(EINVAL); + } + + DEBUGGING( + "pfkey_x_nat_t_type_build: " + "type=%d\n", type); + + pfkey_x_nat_t_type = (struct sadb_x_nat_t_type*) + MALLOC(sizeof(struct sadb_x_nat_t_type)); + + *pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_type; + if (pfkey_x_nat_t_type == NULL) { + DEBUGGING( + "pfkey_x_nat_t_type_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + + pfkey_x_nat_t_type->sadb_x_nat_t_type_len = sizeof(struct sadb_x_nat_t_type) / IPSEC_PFKEYv2_ALIGN; + pfkey_x_nat_t_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; + pfkey_x_nat_t_type->sadb_x_nat_t_type_type = type; + for (i=0; i<3; i++) { + pfkey_x_nat_t_type->sadb_x_nat_t_type_reserved[i] = 0; + } + +errlab: + return error; +} + +int +pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext, + uint16_t exttype, + uint16_t port) +{ + int error = 0; + struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)*pfkey_ext; + + DEBUGGING( + "pfkey_x_nat_t_port_build:\n"); + /* sanity checks... */ + if (pfkey_x_nat_t_port) { + DEBUGGING( + "pfkey_x_nat_t_port_build: " + "why is pfkey_x_nat_t_port already pointing to something?\n"); + SENDERR(EINVAL); + } + + switch (exttype) { + case SADB_X_EXT_NAT_T_SPORT: + case SADB_X_EXT_NAT_T_DPORT: + break; + default: + DEBUGGING( + "pfkey_nat_t_port_build: " + "unrecognised ext_type=%d.\n", + exttype); + SENDERR(EINVAL); + } + + DEBUGGING( + "pfkey_x_nat_t_port_build: " + "ext=%d, port=%d\n", exttype, port); + + pfkey_x_nat_t_port = (struct sadb_x_nat_t_port*) + MALLOC(sizeof(struct sadb_x_nat_t_port)); + *pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_port; + + if (pfkey_x_nat_t_port == NULL) { + DEBUGGING( + "pfkey_x_nat_t_port_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + + pfkey_x_nat_t_port->sadb_x_nat_t_port_len = sizeof(struct sadb_x_nat_t_port) / IPSEC_PFKEYv2_ALIGN; + pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype = exttype; + pfkey_x_nat_t_port->sadb_x_nat_t_port_port = port; + pfkey_x_nat_t_port->sadb_x_nat_t_port_reserved = 0; + +errlab: + return error; +} + +int pfkey_x_protocol_build(struct sadb_ext **pfkey_ext, + uint8_t protocol) +{ + int error = 0; + struct sadb_protocol * p = (struct sadb_protocol *)*pfkey_ext; + DEBUGGING("pfkey_x_protocol_build: protocol=%u\n", protocol); + /* sanity checks... */ + if (p != 0) { + DEBUGGING("pfkey_x_protocol_build: bogus protocol pointer\n"); + SENDERR(EINVAL); + } + if ((p = (struct sadb_protocol*)MALLOC(sizeof(*p))) == 0) { + DEBUGGING("pfkey_build: memory allocation failed\n"); + SENDERR(ENOMEM); + } + *pfkey_ext = (struct sadb_ext *)p; + p->sadb_protocol_len = sizeof(*p) / sizeof(uint64_t); + p->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL; + p->sadb_protocol_proto = protocol; + p->sadb_protocol_flags = 0; + p->sadb_protocol_reserved2 = 0; + errlab: + return error; +} + + +#if I_DONT_THINK_THIS_WILL_BE_USEFUL +int (*ext_default_builders[SADB_EXT_MAX +1])(struct sadb_msg*, struct sadb_ext*) + = +{ + NULL, /* pfkey_msg_build, */ + pfkey_sa_build, + pfkey_lifetime_build, + pfkey_lifetime_build, + pfkey_lifetime_build, + pfkey_address_build, + pfkey_address_build, + pfkey_address_build, + pfkey_key_build, + pfkey_key_build, + pfkey_ident_build, + pfkey_ident_build, + pfkey_sens_build, + pfkey_prop_build, + pfkey_supported_build, + pfkey_supported_build, + pfkey_spirange_build, + pfkey_x_kmprivate_build, + pfkey_x_satype_build, + pfkey_sa_build, + pfkey_address_build, + pfkey_address_build, + pfkey_address_build, + pfkey_address_build, + pfkey_address_build, + pfkey_x_ext_debug_build +}; +#endif + +int +pfkey_msg_build(struct sadb_msg **pfkey_msg, struct sadb_ext *extensions[], int dir) +{ + int error = 0; + unsigned ext; + unsigned total_size; + struct sadb_ext *pfkey_ext; + int extensions_seen = 0; + struct sadb_ext *extensions_check[SADB_EXT_MAX + 1]; + + if (!extensions[0]) { + DEBUGGING( + "pfkey_msg_build: " + "extensions[0] must be specified (struct sadb_msg).\n"); + SENDERR(EINVAL); + } + + total_size = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; + for (ext = 1; ext <= SADB_EXT_MAX; ext++) { + if(extensions[ext]) { + total_size += (extensions[ext])->sadb_ext_len; + } + } + + if (!(*pfkey_msg = (struct sadb_msg*)MALLOC(total_size * IPSEC_PFKEYv2_ALIGN))) { + DEBUGGING( + "pfkey_msg_build: " + "memory allocation failed\n"); + SENDERR(ENOMEM); + } + + DEBUGGING( + "pfkey_msg_build: " + "pfkey_msg=0p%p allocated %lu bytes, &(extensions[0])=0p%p\n", + *pfkey_msg, + (unsigned long)(total_size * IPSEC_PFKEYv2_ALIGN), + &(extensions[0])); + memcpy(*pfkey_msg, + extensions[0], + sizeof(struct sadb_msg)); + (*pfkey_msg)->sadb_msg_len = total_size; + (*pfkey_msg)->sadb_msg_reserved = 0; + extensions_seen = 1 ; + + pfkey_ext = (struct sadb_ext*)(((char*)(*pfkey_msg)) + sizeof(struct sadb_msg)); + + for (ext = 1; ext <= SADB_EXT_MAX; ext++) { + /* copy from extension[ext] to buffer */ + if (extensions[ext]) { + /* Is this type of extension permitted for this type of message? */ + if (!(extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type] & + 1<sadb_msg_type], + 1<sadb_ext_len * IPSEC_PFKEYv2_ALIGN), + ext, + extensions[ext], + pfkey_ext); + memcpy(pfkey_ext, + extensions[ext], + (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); + { + char *pfkey_ext_c = (char *)pfkey_ext; + + pfkey_ext_c += (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN; + pfkey_ext = (struct sadb_ext *)pfkey_ext_c; + } + /* Mark that we have seen this extension and remember the header location */ + extensions_seen |= ( 1 << ext ); + } + } + + /* check required extensions */ + DEBUGGING( + "pfkey_msg_build: " + "extensions permitted=%08x, seen=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type], + extensions_seen, + extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]); + + if ((extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) != + extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) { + DEBUGGING( + "pfkey_msg_build: " + "required extensions missing:%08x.\n", + extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type] - + (extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) ); + SENDERR(EINVAL); + } + + error = pfkey_msg_parse(*pfkey_msg, NULL, extensions_check, dir); + if (error) { + DEBUGGING( + "pfkey_msg_build: " + "Trouble parsing newly built pfkey message, error=%d.\n", + error); + SENDERR(-error); + } + +errlab: + + return error; +} diff --git a/src/libfreeswan/pfkey_v2_debug.c b/src/libfreeswan/pfkey_v2_debug.c new file mode 100644 index 000000000..8430766aa --- /dev/null +++ b/src/libfreeswan/pfkey_v2_debug.c @@ -0,0 +1,177 @@ +/* + * @(#) pfkey version 2 debugging messages + * + * Copyright (C) 2001 Richard Guy Briggs + * and Michael Richardson + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pfkey_v2_debug.c,v 1.2 2004/03/22 21:53:18 as Exp $ + * + */ + +#ifdef __KERNEL__ + +# include /* for printk */ + +# include "freeswan/ipsec_kversion.h" /* for malloc switch */ +# ifdef MALLOC_SLAB +# include /* kmalloc() */ +# else /* MALLOC_SLAB */ +# include /* kmalloc() */ +# endif /* MALLOC_SLAB */ +# include /* error codes */ +# include /* size_t */ +# include /* mark_bh */ + +# include /* struct device, and other headers */ +# include /* eth_type_trans */ +extern int debug_pfkey; + +#else /* __KERNEL__ */ + +# include +# include +# include + +#endif /* __KERNEL__ */ + +#include "freeswan.h" +#include "pfkeyv2.h" +#include "pfkey.h" + +/* + * This file provides ASCII translations of PF_KEY magic numbers. + * + */ + +static char *pfkey_sadb_ext_strings[]={ + "reserved", /* SADB_EXT_RESERVED 0 */ + "security-association", /* SADB_EXT_SA 1 */ + "lifetime-current", /* SADB_EXT_LIFETIME_CURRENT 2 */ + "lifetime-hard", /* SADB_EXT_LIFETIME_HARD 3 */ + "lifetime-soft", /* SADB_EXT_LIFETIME_SOFT 4 */ + "source-address", /* SADB_EXT_ADDRESS_SRC 5 */ + "destination-address", /* SADB_EXT_ADDRESS_DST 6 */ + "proxy-address", /* SADB_EXT_ADDRESS_PROXY 7 */ + "authentication-key", /* SADB_EXT_KEY_AUTH 8 */ + "cipher-key", /* SADB_EXT_KEY_ENCRYPT 9 */ + "source-identity", /* SADB_EXT_IDENTITY_SRC 10 */ + "destination-identity", /* SADB_EXT_IDENTITY_DST 11 */ + "sensitivity-label", /* SADB_EXT_SENSITIVITY 12 */ + "proposal", /* SADB_EXT_PROPOSAL 13 */ + "supported-auth", /* SADB_EXT_SUPPORTED_AUTH 14 */ + "supported-cipher", /* SADB_EXT_SUPPORTED_ENCRYPT 15 */ + "spi-range", /* SADB_EXT_SPIRANGE 16 */ + "X-kmpprivate", /* SADB_X_EXT_KMPRIVATE 17 */ + "X-satype2", /* SADB_X_EXT_SATYPE2 18 */ + "X-security-association", /* SADB_X_EXT_SA2 19 */ + "X-destination-address2", /* SADB_X_EXT_ADDRESS_DST2 20 */ + "X-source-flow-address", /* SADB_X_EXT_ADDRESS_SRC_FLOW 21 */ + "X-dest-flow-address", /* SADB_X_EXT_ADDRESS_DST_FLOW 22 */ + "X-source-mask", /* SADB_X_EXT_ADDRESS_SRC_MASK 23 */ + "X-dest-mask", /* SADB_X_EXT_ADDRESS_DST_MASK 24 */ + "X-set-debug", /* SADB_X_EXT_DEBUG 25 */ + "X-NAT-T-type", /* SADB_X_EXT_NAT_T_TYPE 26 */ + "X-NAT-T-sport", /* SADB_X_EXT_NAT_T_SPORT 27 */ + "X-NAT-T-dport", /* SADB_X_EXT_NAT_T_DPORT 28 */ + "X-NAT-T-OA", /* SADB_X_EXT_NAT_T_OA 29 */ +}; + +const char * +pfkey_v2_sadb_ext_string(int ext) +{ + if(ext <= SADB_EXT_MAX) { + return pfkey_sadb_ext_strings[ext]; + } else { + return "unknown-ext"; + } +} + + +static char *pfkey_sadb_type_strings[]={ + "reserved", /* SADB_RESERVED */ + "getspi", /* SADB_GETSPI */ + "update", /* SADB_UPDATE */ + "add", /* SADB_ADD */ + "delete", /* SADB_DELETE */ + "get", /* SADB_GET */ + "acquire", /* SADB_ACQUIRE */ + "register", /* SADB_REGISTER */ + "expire", /* SADB_EXPIRE */ + "flush", /* SADB_FLUSH */ + "dump", /* SADB_DUMP */ + "x-promisc", /* SADB_X_PROMISC */ + "x-pchange", /* SADB_X_PCHANGE */ + "x-groupsa", /* SADB_X_GRPSA */ + "x-addflow(eroute)", /* SADB_X_ADDFLOW */ + "x-delflow(eroute)", /* SADB_X_DELFLOW */ + "x-debug", /* SADB_X_DEBUG */ +}; + +const char * +pfkey_v2_sadb_type_string(int sadb_type) +{ + if(sadb_type <= SADB_MAX) { + return pfkey_sadb_type_strings[sadb_type]; + } else { + return "unknown-sadb-type"; + } +} + + + + +/* + * $Log: pfkey_v2_debug.c,v $ + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.2.1 2004/03/15 22:30:06 as + * nat-0.6c patch merged + * + * Revision 1.1 2004/03/15 20:35:26 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.7 2002/09/20 05:01:26 rgb + * Fixed limit inclusion error in both type and ext string conversion. + * + * Revision 1.6 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.5 2002/04/24 07:36:40 mcr + * Moved from ./lib/pfkey_v2_debug.c,v + * + * Revision 1.4 2002/01/29 22:25:36 rgb + * Re-add ipsec_kversion.h to keep MALLOC happy. + * + * Revision 1.3 2002/01/29 01:59:09 mcr + * removal of kversions.h - sources that needed it now use ipsec_param.h. + * updating of IPv6 structures to match latest in6.h version. + * removed dead code from freeswan.h that also duplicated kversions.h + * code. + * + * Revision 1.2 2002/01/20 20:34:50 mcr + * added pfkey_v2_sadb_type_string to decode sadb_type to string. + * + * Revision 1.1 2001/11/27 05:30:06 mcr + * initial set of debug strings for pfkey debugging. + * this will eventually only be included for debug builds. + * + * Revision 1.1 2001/09/21 04:12:03 mcr + * first compilable version. + * + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/pfkey_v2_ext_bits.c b/src/libfreeswan/pfkey_v2_ext_bits.c new file mode 100644 index 000000000..b41941848 --- /dev/null +++ b/src/libfreeswan/pfkey_v2_ext_bits.c @@ -0,0 +1,789 @@ +/* + * RFC2367 PF_KEYv2 Key management API message parser + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pfkey_v2_ext_bits.c,v 1.2 2004/03/22 21:53:18 as Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_parse.c. + */ + +char pfkey_v2_ext_bits_c_version[] = "$Id: pfkey_v2_ext_bits.c,v 1.2 2004/03/22 21:53:18 as Exp $"; + +/* + * Some ugly stuff to allow consistent debugging code for use in the + * kernel and in user space +*/ + +#ifdef __KERNEL__ + +# include /* for printk */ + +# include "freeswan/ipsec_kversion.h" /* for malloc switch */ +# ifdef MALLOC_SLAB +# include /* kmalloc() */ +# else /* MALLOC_SLAB */ +# include /* kmalloc() */ +# endif /* MALLOC_SLAB */ +# include /* error codes */ +# include /* size_t */ +# include /* mark_bh */ + +# include /* struct device, and other headers */ +# include /* eth_type_trans */ +# include /* struct iphdr */ +# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# include +# endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + +#else /* __KERNEL__ */ + +# include +# include +# include +#endif + +#include +#include +#include + +unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/] = { + +/* INBOUND EXTENSIONS */ +{ + +/* PERMITTED IN */ +{ +/* SADB_RESERVED */ +0 +, +/* SADB_GETSPI */ +1<. + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pfkey_v2_parse.c,v 1.4 2004/06/13 20:35:07 as Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_parser.c. + */ + +char pfkey_v2_parse_c_version[] = "$Id: pfkey_v2_parse.c,v 1.4 2004/06/13 20:35:07 as Exp $"; + +/* + * Some ugly stuff to allow consistent debugging code for use in the + * kernel and in user space +*/ + +#ifdef __KERNEL__ + +# include /* for printk */ + +#include "freeswan/ipsec_kversion.h" /* for malloc switch */ + +# ifdef MALLOC_SLAB +# include /* kmalloc() */ +# else /* MALLOC_SLAB */ +# include /* kmalloc() */ +# endif /* MALLOC_SLAB */ +# include /* error codes */ +# include /* size_t */ +# include /* mark_bh */ + +# include /* struct device, and other headers */ +# include /* eth_type_trans */ +# include /* struct iphdr */ +# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# include /* struct ipv6hdr */ +# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +extern int debug_pfkey; + +#include "freeswan.h" + +#include "ipsec_encap.h" + +#else /* __KERNEL__ */ + +# include +# include +# include + +# include +# include +# include /* for PRINTF_LIKE */ +# include /* for debugging and DBG_log */ + +/* #define PLUTO */ + +# ifdef PLUTO +# define DEBUGGING(level, args...) { DBG_log("pfkey_lib_debug:" args); } +# else +# define DEBUGGING(level, args...) if(pfkey_lib_debug & level) { printf("pfkey_lib_debug:" args); } else { ; } +# endif + +#endif /* __KERNEL__ */ + + +#include +#include + +#ifdef __KERNEL__ +extern int sysctl_ipsec_debug_verbose; +# define DEBUGGING(level, args...) \ + KLIPS_PRINT( \ + ((debug_pfkey & level & (PF_KEY_DEBUG_PARSE_STRUCT | PF_KEY_DEBUG_PARSE_PROBLEM)) \ + || (sysctl_ipsec_debug_verbose && (debug_pfkey & level & PF_KEY_DEBUG_PARSE_FLOW))) \ + , "klips_debug:" args) +#endif /* __KERNEL__ */ +#include "ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */ + + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +struct satype_tbl { + uint8_t proto; + uint8_t satype; + char* name; +} static satype_tbl[] = { +#ifdef __KERNEL__ + { IPPROTO_ESP, SADB_SATYPE_ESP, "ESP" }, + { IPPROTO_AH, SADB_SATYPE_AH, "AH" }, + { IPPROTO_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, +#ifdef CONFIG_IPSEC_IPCOMP + { IPPROTO_COMP, SADB_X_SATYPE_COMP, "COMP" }, +#endif /* CONFIG_IPSEC_IPCOMP */ + { IPPROTO_INT, SADB_X_SATYPE_INT, "INT" }, +#else /* __KERNEL__ */ + { SA_ESP, SADB_SATYPE_ESP, "ESP" }, + { SA_AH, SADB_SATYPE_AH, "AH" }, + { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, + { SA_COMP, SADB_X_SATYPE_COMP, "COMP" }, + { SA_INT, SADB_X_SATYPE_INT, "INT" }, +#endif /* __KERNEL__ */ + { 0, 0, "UNKNOWN" } +}; + +uint8_t +satype2proto(uint8_t satype) +{ + int i =0; + + while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { + i++; + } + return satype_tbl[i].proto; +} + +uint8_t +proto2satype(uint8_t proto) +{ + int i = 0; + + while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { + i++; + } + return satype_tbl[i].satype; +} + +char* +satype2name(uint8_t satype) +{ + int i = 0; + + while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { + i++; + } + return satype_tbl[i].name; +} + +char* +proto2name(uint8_t proto) +{ + int i = 0; + + while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { + i++; + } + return satype_tbl[i].name; +} + +/* Default extension parsers taken from the KLIPS code */ + +DEBUG_NO_STATIC int +pfkey_sa_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext; +#if 0 + struct sadb_sa sav2; +#endif + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_sa_parse: entry\n"); + /* sanity checks... */ + if(!pfkey_sa) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + +#if 0 + /* check if this structure is short, and if so, fix it up. + * XXX this is NOT the way to do things. + */ + if(pfkey_sa->sadb_sa_len == sizeof(struct sadb_sa_v1)/IPSEC_PFKEYv2_ALIGN) { + + /* yes, so clear out a temporary structure, and copy first */ + memset(&sav2, 0, sizeof(sav2)); + memcpy(&sav2, pfkey_sa, sizeof(struct sadb_sa_v1)); + sav2.sadb_x_sa_ref=-1; + sav2.sadb_sa_len = sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN; + + pfkey_sa = &sav2; + } +#endif + + + if(pfkey_sa->sadb_sa_len != sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "length wrong pfkey_sa->sadb_sa_len=%d sizeof(struct sadb_sa)=%d.\n", + pfkey_sa->sadb_sa_len, + (int)sizeof(struct sadb_sa)); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_encrypt > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "pfkey_sa->sadb_sa_encrypt=%d > SADB_EALG_MAX=%d.\n", + pfkey_sa->sadb_sa_encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_auth > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "pfkey_sa->sadb_sa_auth=%d > SADB_AALG_MAX=%d.\n", + pfkey_sa->sadb_sa_auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_state > SADB_SASTATE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "state=%d exceeds MAX=%d.\n", + pfkey_sa->sadb_sa_state, + SADB_SASTATE_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_state == SADB_SASTATE_DEAD) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "state=%d is DEAD=%d.\n", + pfkey_sa->sadb_sa_state, + SADB_SASTATE_DEAD); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_replay > 64) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "replay window size: %d -- must be 0 <= size <= 64\n", + pfkey_sa->sadb_sa_replay); + SENDERR(EINVAL); + } + + if(! ((pfkey_sa->sadb_sa_exttype == SADB_EXT_SA) || + (pfkey_sa->sadb_sa_exttype == SADB_X_EXT_SA2))) + { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "unknown exttype=%d, expecting SADB_EXT_SA=%d or SADB_X_EXT_SA2=%d.\n", + pfkey_sa->sadb_sa_exttype, + SADB_EXT_SA, + SADB_X_EXT_SA2); + SENDERR(EINVAL); + } + + if((IPSEC_SAREF_NULL != pfkey_sa->sadb_x_sa_ref) && (pfkey_sa->sadb_x_sa_ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n", + pfkey_sa->sadb_x_sa_ref, + IPSEC_SAREF_NULL, + IPSEC_SA_REF_TABLE_NUM_ENTRIES); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_sa_parse: " + "successfully found len=%d exttype=%d(%s) spi=%08lx replay=%d state=%d auth=%d encrypt=%d flags=%d ref=%d.\n", + pfkey_sa->sadb_sa_len, + pfkey_sa->sadb_sa_exttype, + pfkey_v2_sadb_ext_string(pfkey_sa->sadb_sa_exttype), + (long unsigned int)ntohl(pfkey_sa->sadb_sa_spi), + pfkey_sa->sadb_sa_replay, + pfkey_sa->sadb_sa_state, + pfkey_sa->sadb_sa_auth, + pfkey_sa->sadb_sa_encrypt, + pfkey_sa->sadb_sa_flags, + pfkey_sa->sadb_x_sa_ref); + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_lifetime_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_lifetime_parse:enter\n"); + /* sanity checks... */ + if(!pfkey_lifetime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_lifetime->sadb_lifetime_len != + sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "length wrong pfkey_lifetime->sadb_lifetime_len=%d sizeof(struct sadb_lifetime)=%d.\n", + pfkey_lifetime->sadb_lifetime_len, + (int)sizeof(struct sadb_lifetime)); + SENDERR(EINVAL); + } + + if((pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_HARD) && + (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_SOFT) && + (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_CURRENT)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "unexpected ext_type=%d.\n", + pfkey_lifetime->sadb_lifetime_exttype); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_lifetime_parse: " + "life_type=%d(%s) alloc=%u bytes=%u add=%u use=%u pkts=%u.\n", + pfkey_lifetime->sadb_lifetime_exttype, + pfkey_v2_sadb_ext_string(pfkey_lifetime->sadb_lifetime_exttype), + pfkey_lifetime->sadb_lifetime_allocations, + (unsigned)pfkey_lifetime->sadb_lifetime_bytes, + (unsigned)pfkey_lifetime->sadb_lifetime_addtime, + (unsigned)pfkey_lifetime->sadb_lifetime_usetime, + pfkey_lifetime->sadb_x_lifetime_packets); +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_address_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int saddr_len = 0; + struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext; + struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address)); + char ipaddr_txt[ADDRTOT_BUF]; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_address_parse:enter\n"); + /* sanity checks... */ + if(!pfkey_address) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_len < + (sizeof(struct sadb_address) + sizeof(struct sockaddr))/ + IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "size wrong 1 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", + pfkey_address->sadb_address_len, + (int)sizeof(struct sadb_address), + (int)sizeof(struct sockaddr)); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "res=%d, must be zero.\n", + pfkey_address->sadb_address_reserved); + SENDERR(EINVAL); + } + + switch(pfkey_address->sadb_address_exttype) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_X_EXT_ADDRESS_DST2: + case SADB_X_EXT_ADDRESS_SRC_FLOW: + case SADB_X_EXT_ADDRESS_DST_FLOW: + case SADB_X_EXT_ADDRESS_SRC_MASK: + case SADB_X_EXT_ADDRESS_DST_MASK: + case SADB_X_EXT_NAT_T_OA: + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "unexpected ext_type=%d.\n", + pfkey_address->sadb_address_exttype); + SENDERR(EINVAL); + } + + switch(s->sa_family) { + case AF_INET: + saddr_len = sizeof(struct sockaddr_in); + sprintf(ipaddr_txt, "%d.%d.%d.%d" + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 0) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 8) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 16) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 24) & 0xFF); + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_address_parse: " + "found exttype=%u(%s) family=%d(AF_INET) address=%s proto=%u port=%u.\n", + pfkey_address->sadb_address_exttype, + pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), + s->sa_family, + ipaddr_txt, + pfkey_address->sadb_address_proto, + ntohs(((struct sockaddr_in*)s)->sin_port)); + break; + case AF_INET6: + saddr_len = sizeof(struct sockaddr_in6); + sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x" + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[0]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[1]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[2]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[3]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[4]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[5]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[6]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[7])); + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_address_parse: " + "found exttype=%u(%s) family=%d(AF_INET6) address=%s proto=%u port=%u.\n", + pfkey_address->sadb_address_exttype, + pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), + s->sa_family, + ipaddr_txt, + pfkey_address->sadb_address_proto, + ((struct sockaddr_in6*)s)->sin6_port); + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "s->sa_family=%d not supported.\n", + s->sa_family); + SENDERR(EPFNOSUPPORT); + } + + if(pfkey_address->sadb_address_len != + DIVUP(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "size wrong 2 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", + pfkey_address->sadb_address_len, + (int)sizeof(struct sadb_address), + saddr_len); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_prefixlen != 0) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "address prefixes not supported yet.\n"); + SENDERR(EAFNOSUPPORT); /* not supported yet */ + } + + /* XXX check if port!=0 */ + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_address_parse: successful.\n"); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_key_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_key_parse:enter\n"); + /* sanity checks... */ + + if(!pfkey_key) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_len < sizeof(struct sadb_key) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_key->sadb_key_len, + (int)sizeof(struct sadb_key)); + SENDERR(EINVAL); + } + + if(!pfkey_key->sadb_key_bits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "key length set to zero, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_len != + DIVUP(sizeof(struct sadb_key) * OCTETBITS + pfkey_key->sadb_key_bits, + PFKEYBITS)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "key length=%d does not agree with extension length=%d.\n", + pfkey_key->sadb_key_bits, + pfkey_key->sadb_key_len); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "res=%d, must be zero.\n", + pfkey_key->sadb_key_reserved); + SENDERR(EINVAL); + } + + if(! ( (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_AUTH) || + (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_ENCRYPT))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "expecting extension type AUTH or ENCRYPT, got %d.\n", + pfkey_key->sadb_key_exttype); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_key_parse: " + "success, found len=%d exttype=%d(%s) bits=%d reserved=%d.\n", + pfkey_key->sadb_key_len, + pfkey_key->sadb_key_exttype, + pfkey_v2_sadb_ext_string(pfkey_key->sadb_key_exttype), + pfkey_key->sadb_key_bits, + pfkey_key->sadb_key_reserved); + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_ident_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_ident->sadb_ident_len < sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_ident->sadb_ident_len, + (int)sizeof(struct sadb_ident)); + SENDERR(EINVAL); + } + + if(pfkey_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "ident_type=%d out of range, must be less than %d.\n", + pfkey_ident->sadb_ident_type, + SADB_IDENTTYPE_MAX); + SENDERR(EINVAL); + } + + if(pfkey_ident->sadb_ident_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "res=%d, must be zero.\n", + pfkey_ident->sadb_ident_reserved); + SENDERR(EINVAL); + } + + /* string terminator/padding must be zero */ + if(pfkey_ident->sadb_ident_len > sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { + if(*((char*)pfkey_ident + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "string padding must be zero, last is 0x%02x.\n", + *((char*)pfkey_ident + + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)); + SENDERR(EINVAL); + } + } + + if( ! ((pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) || + (pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_DST))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "expecting extension type IDENTITY_SRC or IDENTITY_DST, got %d.\n", + pfkey_ident->sadb_ident_exttype); + SENDERR(EINVAL); + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_sens_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_sens *pfkey_sens = (struct sadb_sens *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_sens->sadb_sens_len < sizeof(struct sadb_sens) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sens_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_sens->sadb_sens_len, + (int)sizeof(struct sadb_sens)); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sens_parse: " + "Sorry, I can't parse exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); +#if 0 + SENDERR(EINVAL); /* don't process these yet */ +#endif + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_prop_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i, num_comb; + struct sadb_prop *pfkey_prop = (struct sadb_prop *)pfkey_ext; + struct sadb_comb *pfkey_comb = (struct sadb_comb *)((char*)pfkey_ext + sizeof(struct sadb_prop)); + + /* sanity checks... */ + if((pfkey_prop->sadb_prop_len < sizeof(struct sadb_prop) / IPSEC_PFKEYv2_ALIGN) || + (((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) % sizeof(struct sadb_comb))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "size wrong ext_len=%d, prop_ext_len=%d comb_ext_len=%d.\n", + pfkey_prop->sadb_prop_len, + (int)sizeof(struct sadb_prop), + (int)sizeof(struct sadb_comb)); + SENDERR(EINVAL); + } + + if(pfkey_prop->sadb_prop_replay > 64) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "replay window size: %d -- must be 0 <= size <= 64\n", + pfkey_prop->sadb_prop_replay); + SENDERR(EINVAL); + } + + for(i=0; i<3; i++) { + if(pfkey_prop->sadb_prop_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "res[%d]=%d, must be zero.\n", + i, pfkey_prop->sadb_prop_reserved[i]); + SENDERR(EINVAL); + } + } + + num_comb = ((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) / sizeof(struct sadb_comb); + + for(i = 0; i < num_comb; i++) { + if(pfkey_comb->sadb_comb_auth > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth=%d > SADB_AALG_MAX=%d.\n", + i, + pfkey_comb->sadb_comb_auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_auth) { + if(!pfkey_comb->sadb_comb_auth_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(!pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_maxbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_auth_minbits > pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=%d > maxbits=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_minbits, + pfkey_comb->sadb_comb_auth_maxbits); + SENDERR(EINVAL); + } + } else { + if(pfkey_comb->sadb_comb_auth_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_minbits); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_maxbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_maxbits); + SENDERR(EINVAL); + } + } + + if(pfkey_comb->sadb_comb_encrypt > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_comb_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt=%d > SADB_EALG_MAX=%d.\n", + i, + pfkey_comb->sadb_comb_encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_encrypt) { + if(!pfkey_comb->sadb_comb_encrypt_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(!pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_encrypt_minbits > pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d > maxbits=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_minbits, + pfkey_comb->sadb_comb_encrypt_maxbits); + SENDERR(EINVAL); + } + } else { + if(pfkey_comb->sadb_comb_encrypt_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_minbits); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_maxbits); + SENDERR(EINVAL); + } + } + + /* XXX do sanity check on flags */ + + if(pfkey_comb->sadb_comb_hard_allocations && pfkey_comb->sadb_comb_soft_allocations > pfkey_comb->sadb_comb_hard_allocations) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_allocations=%d > hard_allocations=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_soft_allocations, + pfkey_comb->sadb_comb_hard_allocations); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_bytes && pfkey_comb->sadb_comb_soft_bytes > pfkey_comb->sadb_comb_hard_bytes) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_bytes=%Ld > hard_bytes=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_bytes, + (unsigned long long int)pfkey_comb->sadb_comb_hard_bytes); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_addtime && pfkey_comb->sadb_comb_soft_addtime > pfkey_comb->sadb_comb_hard_addtime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_addtime=%Ld > hard_addtime=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_addtime, + (unsigned long long int)pfkey_comb->sadb_comb_hard_addtime); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_usetime && pfkey_comb->sadb_comb_soft_usetime > pfkey_comb->sadb_comb_hard_usetime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_usetime=%Ld > hard_usetime=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_usetime, + (unsigned long long int)pfkey_comb->sadb_comb_hard_usetime); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_x_comb_hard_packets && pfkey_comb->sadb_x_comb_soft_packets > pfkey_comb->sadb_x_comb_hard_packets) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_x_comb_soft_packets=%d > hard_packets=%d, fatal.\n", + i, + pfkey_comb->sadb_x_comb_soft_packets, + pfkey_comb->sadb_x_comb_hard_packets); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "comb[%d].res=%d, must be zero.\n", + i, + pfkey_comb->sadb_comb_reserved); + SENDERR(EINVAL); + } + pfkey_comb++; + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_supported_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + unsigned int i, num_alg; + struct sadb_supported *pfkey_supported = (struct sadb_supported *)pfkey_ext; + struct sadb_alg *pfkey_alg = (struct sadb_alg*)((char*)pfkey_ext + sizeof(struct sadb_supported)); + + /* sanity checks... */ + if((pfkey_supported->sadb_supported_len < + sizeof(struct sadb_supported) / IPSEC_PFKEYv2_ALIGN) || + (((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - + sizeof(struct sadb_supported)) % sizeof(struct sadb_alg))) { + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "size wrong ext_len=%d, supported_ext_len=%d alg_ext_len=%d.\n", + pfkey_supported->sadb_supported_len, + (int)sizeof(struct sadb_supported), + (int)sizeof(struct sadb_alg)); + SENDERR(EINVAL); + } + + if(pfkey_supported->sadb_supported_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "res=%d, must be zero.\n", + pfkey_supported->sadb_supported_reserved); + SENDERR(EINVAL); + } + + num_alg = ((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_supported)) / sizeof(struct sadb_alg); + + for(i = 0; i < num_alg; i++) { + /* process algo description */ + if(pfkey_alg->sadb_alg_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], id=%d, ivlen=%d, minbits=%d, maxbits=%d, res=%d, must be zero.\n", + i, + pfkey_alg->sadb_alg_id, + pfkey_alg->sadb_alg_ivlen, + pfkey_alg->sadb_alg_minbits, + pfkey_alg->sadb_alg_maxbits, + pfkey_alg->sadb_alg_reserved); + SENDERR(EINVAL); + } + + /* XXX can alg_id auth/enc be determined from info given? + Yes, but OpenBSD's method does not iteroperate with rfc2367. + rgb, 2000-04-06 */ + + switch(pfkey_supported->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + if(pfkey_alg->sadb_alg_id > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_AALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + if(pfkey_alg->sadb_alg_id > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + pfkey_alg++; + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_spirange_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_spirange->sadb_spirange_len != + sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_spirange->sadb_spirange_len, + (int)sizeof(struct sadb_spirange)); + SENDERR(EINVAL); + } + + if(pfkey_spirange->sadb_spirange_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "reserved=%d must be set to zero.\n", + pfkey_spirange->sadb_spirange_reserved); + SENDERR(EINVAL); + } + + if(ntohl(pfkey_spirange->sadb_spirange_max) < ntohl(pfkey_spirange->sadb_spirange_min)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "minspi=%08x must be < maxspi=%08x.\n", + ntohl(pfkey_spirange->sadb_spirange_min), + ntohl(pfkey_spirange->sadb_spirange_max)); + SENDERR(EINVAL); + } + + if(ntohl(pfkey_spirange->sadb_spirange_min) <= 255) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "minspi=%08x must be > 255.\n", + ntohl(pfkey_spirange->sadb_spirange_min)); + SENDERR(EEXIST); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_spirange_parse: " + "ext_len=%u ext_type=%u(%s) min=%u max=%u res=%u.\n", + pfkey_spirange->sadb_spirange_len, + pfkey_spirange->sadb_spirange_exttype, + pfkey_v2_sadb_ext_string(pfkey_spirange->sadb_spirange_exttype), + pfkey_spirange->sadb_spirange_min, + pfkey_spirange->sadb_spirange_max, + pfkey_spirange->sadb_spirange_reserved); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_x_kmprivate->sadb_x_kmprivate_len < + sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_kmprivate->sadb_x_kmprivate_len, + (int)sizeof(struct sadb_x_kmprivate)); + SENDERR(EINVAL); + } + + if(pfkey_x_kmprivate->sadb_x_kmprivate_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "reserved=%d must be set to zero.\n", + pfkey_x_kmprivate->sadb_x_kmprivate_reserved); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "Sorry, I can't parse exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_satype_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i; + struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_x_satype_parse: enter\n"); + /* sanity checks... */ + if(pfkey_x_satype->sadb_x_satype_len != + sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_satype->sadb_x_satype_len, + (int)sizeof(struct sadb_x_satype)); + SENDERR(EINVAL); + } + + if(!pfkey_x_satype->sadb_x_satype_satype) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "satype is zero, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if(pfkey_x_satype->sadb_x_satype_satype > SADB_SATYPE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "satype %d > max %d, invalid.\n", + pfkey_x_satype->sadb_x_satype_satype, SADB_SATYPE_MAX); + SENDERR(EINVAL); + } + + if(!(satype2proto(pfkey_x_satype->sadb_x_satype_satype))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "proto lookup from satype=%d failed.\n", + pfkey_x_satype->sadb_x_satype_satype); + SENDERR(EINVAL); + } + + for(i = 0; i < 3; i++) { + if(pfkey_x_satype->sadb_x_satype_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "reserved[%d]=%d must be set to zero.\n", + i, pfkey_x_satype->sadb_x_satype_reserved[i]); + SENDERR(EINVAL); + } + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_x_satype_parse: " + "len=%u ext=%u(%s) satype=%u(%s) res=%u,%u,%u.\n", + pfkey_x_satype->sadb_x_satype_len, + pfkey_x_satype->sadb_x_satype_exttype, + pfkey_v2_sadb_ext_string(pfkey_x_satype->sadb_x_satype_exttype), + pfkey_x_satype->sadb_x_satype_satype, + satype2name(pfkey_x_satype->sadb_x_satype_satype), + pfkey_x_satype->sadb_x_satype_reserved[0], + pfkey_x_satype->sadb_x_satype_reserved[1], + pfkey_x_satype->sadb_x_satype_reserved[2]); +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i; + struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_x_debug_parse: enter\n"); + /* sanity checks... */ + if(pfkey_x_debug->sadb_x_debug_len != + sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_debug_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_debug->sadb_x_debug_len, + (int)sizeof(struct sadb_x_debug)); + SENDERR(EINVAL); + } + + for(i = 0; i < 4; i++) { + if(pfkey_x_debug->sadb_x_debug_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_debug_parse: " + "reserved[%d]=%d must be set to zero.\n", + i, pfkey_x_debug->sadb_x_debug_reserved[i]); + SENDERR(EINVAL); + } + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_ext_protocol_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_protocol *p = (struct sadb_protocol *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, "pfkey_x_protocol_parse:\n"); + /* sanity checks... */ + + if (p->sadb_protocol_len != sizeof(*p)/IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_protocol_parse: size wrong ext_len=%d, key_ext_len=%d.\n", + p->sadb_protocol_len, (int)sizeof(*p)); + SENDERR(EINVAL); + } + + if (p->sadb_protocol_reserved2 != 0) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_protocol_parse: res=%d, must be zero.\n", + p->sadb_protocol_reserved2); + SENDERR(EINVAL); + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_ext_nat_t_type_parse(struct sadb_ext *pfkey_ext) +{ + return 0; +} + +DEBUG_NO_STATIC int +pfkey_x_ext_nat_t_port_parse(struct sadb_ext *pfkey_ext) +{ + return 0; +} + +#define DEFINEPARSER(NAME) static struct pf_key_ext_parsers_def NAME##_def={NAME, #NAME}; + +DEFINEPARSER(pfkey_sa_parse); +DEFINEPARSER(pfkey_lifetime_parse); +DEFINEPARSER(pfkey_address_parse); +DEFINEPARSER(pfkey_key_parse); +DEFINEPARSER(pfkey_ident_parse); +DEFINEPARSER(pfkey_sens_parse); +DEFINEPARSER(pfkey_prop_parse); +DEFINEPARSER(pfkey_supported_parse); +DEFINEPARSER(pfkey_spirange_parse); +DEFINEPARSER(pfkey_x_kmprivate_parse); +DEFINEPARSER(pfkey_x_satype_parse); +DEFINEPARSER(pfkey_x_ext_debug_parse); +DEFINEPARSER(pfkey_x_ext_protocol_parse); +DEFINEPARSER(pfkey_x_ext_nat_t_type_parse); +DEFINEPARSER(pfkey_x_ext_nat_t_port_parse); + +struct pf_key_ext_parsers_def *ext_default_parsers[]= +{ + NULL, /* pfkey_msg_parse, */ + &pfkey_sa_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_key_parse_def, + &pfkey_key_parse_def, + &pfkey_ident_parse_def, + &pfkey_ident_parse_def, + &pfkey_sens_parse_def, + &pfkey_prop_parse_def, + &pfkey_supported_parse_def, + &pfkey_supported_parse_def, + &pfkey_spirange_parse_def, + &pfkey_x_kmprivate_parse_def, + &pfkey_x_satype_parse_def, + &pfkey_sa_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_x_ext_debug_parse_def, + &pfkey_x_ext_protocol_parse_def , + &pfkey_x_ext_nat_t_type_parse_def, + &pfkey_x_ext_nat_t_port_parse_def, + &pfkey_x_ext_nat_t_port_parse_def, + &pfkey_address_parse_def +}; + +int +pfkey_msg_parse(struct sadb_msg *pfkey_msg, + struct pf_key_ext_parsers_def *ext_parsers[], + struct sadb_ext *extensions[], + int dir) +{ + int error = 0; + int remain; + struct sadb_ext *pfkey_ext; + int extensions_seen = 0; + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", + pfkey_msg->sadb_msg_version, + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type), + pfkey_msg->sadb_msg_errno, + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_len, + pfkey_msg->sadb_msg_reserved, + pfkey_msg->sadb_msg_seq, + pfkey_msg->sadb_msg_pid); + + if(ext_parsers == NULL) ext_parsers = ext_default_parsers; + + pfkey_extensions_init(extensions); + + remain = pfkey_msg->sadb_msg_len; + remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; + + pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg + + sizeof(struct sadb_msg)); + + extensions[0] = (struct sadb_ext *) pfkey_msg; + + + if(pfkey_msg->sadb_msg_version != PF_KEY_V2) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "not PF_KEY_V2 msg, found %d, should be %d.\n", + pfkey_msg->sadb_msg_version, + PF_KEY_V2); + SENDERR(EINVAL); + } + + if(!pfkey_msg->sadb_msg_type) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "msg type not set, must be non-zero..\n"); + SENDERR(EINVAL); + } + + if(pfkey_msg->sadb_msg_type > SADB_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "msg type=%d > max=%d.\n", + pfkey_msg->sadb_msg_type, + SADB_MAX); + SENDERR(EINVAL); + } + + switch(pfkey_msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_GET: + case SADB_X_GRPSA: + case SADB_X_ADDFLOW: + if(!satype2proto(pfkey_msg->sadb_msg_satype)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype %d conversion to proto failed for msg_type %d (%s).\n", + pfkey_msg->sadb_msg_satype, + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + SENDERR(EINVAL); + } else { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n", + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + satype2proto(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + } + case SADB_ACQUIRE: + case SADB_REGISTER: + case SADB_EXPIRE: + if(!pfkey_msg->sadb_msg_satype) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype is zero, must be non-zero for msg_type %d(%s).\n", + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + SENDERR(EINVAL); + } + default: + break; + } + + /* errno must not be set in downward messages */ + /* this is not entirely true... a response to an ACQUIRE could return an error */ + if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "errno set to %d.\n", + pfkey_msg->sadb_msg_errno); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "remain=%d, ext_type=%d(%s), ext_len=%d.\n", + remain, + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + pfkey_ext->sadb_ext_len); + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "extensions permitted=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); + + extensions_seen = 1; + + while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) { + /* Is there enough message left to support another extension header? */ + if(remain < pfkey_ext->sadb_ext_len) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "remain %d less than ext len %d.\n", + remain, pfkey_ext->sadb_ext_len); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "parsing ext type=%d(%s) remain=%d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + remain); + + /* Is the extension header type valid? */ + if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) invalid, SADB_EXT_MAX=%d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + SADB_EXT_MAX); + SENDERR(EINVAL); + } + + /* Have we already seen this type of extension? */ + if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0) + { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) already seen.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + SENDERR(EINVAL); + } + + /* Do I even know about this type of extension? */ + if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) unknown, ignoring.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + goto next_ext; + } + + /* Is this type of extension permitted for this type of message? */ + if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] & + 1<sadb_ext_type)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) not permitted, exts_perm_in=%08x, 1<sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + 1<sadb_ext_type); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "remain=%d ext_type=%d(%s) ext_len=%d parsing ext 0p%p with parser %s.\n", + remain, + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + pfkey_ext->sadb_ext_len, + pfkey_ext, + ext_parsers[pfkey_ext->sadb_ext_type]->parser_name); + + /* Parse the extension */ + if((error = + (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "extension parsing for type %d(%s) failed with error %d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + error); + SENDERR(-error); + } + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "Extension %d(%s) parsed.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + + /* Mark that we have seen this extension and remember the header location */ + extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type ); + extensions[pfkey_ext->sadb_ext_type] = pfkey_ext; + + next_ext: + /* Calculate how much message remains */ + remain -= pfkey_ext->sadb_ext_len; + + if(!remain) { + break; + } + /* Find the next extension header */ + pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext + + pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); + } + + if(remain) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "unexpected remainder of %d.\n", + remain); + /* why is there still something remaining? */ + SENDERR(EINVAL); + } + + /* check required extensions */ + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "extensions permitted=%08x, seen=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + extensions_seen, + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); + + /* don't check further if it is an error return message since it + may not have a body */ + if(pfkey_msg->sadb_msg_errno) { + SENDERR(-error); + } + + if((extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) != + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "required extensions missing:%08x.\n", + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] - + (extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type])); + SENDERR(EINVAL); + } + + if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW) + && ((extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW) + != SADB_X_EXT_ADDRESS_DELFLOW) + && (((extensions_seen & (1<sadb_sa_flags + & SADB_X_SAFLAGS_CLEARFLOW) + != SADB_X_SAFLAGS_CLEARFLOW))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n", + SADB_X_EXT_ADDRESS_DELFLOW + - (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW), + (1<sadb_msg_type) { + case SADB_ADD: + case SADB_UPDATE: + /* check maturity */ + if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != + SADB_SASTATE_MATURE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "state=%d for add or update should be MATURE=%d.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, + SADB_SASTATE_MATURE); + SENDERR(EINVAL); + } + + /* check AH and ESP */ + switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) { + case SADB_SATYPE_AH: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth != + SADB_AALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "auth alg is zero, must be non-zero for AH SAs.\n"); + SENDERR(EINVAL); + } + if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt != + SADB_EALG_NONE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "AH handed encalg=%d, must be zero.\n", + ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt); + SENDERR(EINVAL); + } + break; + case SADB_SATYPE_ESP: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != + SADB_EALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt == + SADB_EALG_NULL) && + (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth == + SADB_AALG_NONE) ) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ESP handed encNULL+authNONE, illegal combination.\n"); + SENDERR(EINVAL); + } + break; + case SADB_X_SATYPE_COMP: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != + SADB_EALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth != + SADB_AALG_NONE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "COMP handed auth=%d, must be zero.\n", + ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth); + SENDERR(EINVAL); + } + break; + default: + break; + } + if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "spi=%08x must be > 255.\n", + ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi)); + SENDERR(EINVAL); + } + default: + break; + } +errlab: + + return error; +} + +/* + * $Log: pfkey_v2_parse.c,v $ + * Revision 1.4 2004/06/13 20:35:07 as + * removed references to ipsec_netlink.h + * + * Revision 1.3 2004/03/30 10:00:17 as + * 64 bit issues + * + * Revision 1.2 2004/03/22 21:53:18 as + * merged alg-0.8.1 branch with HEAD + * + * Revision 1.1.2.1 2004/03/15 22:30:06 as + * nat-0.6c patch merged + * + * Revision 1.1 2004/03/15 20:35:26 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.53 2003/01/30 02:32:09 rgb + * + * Rename SAref table macro names for clarity. + * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. + * + * Revision 1.52 2002/12/30 06:53:07 mcr + * deal with short SA structures... #if 0 out for now. Probably + * not quite the right way. + * + * Revision 1.51 2002/12/13 18:16:02 mcr + * restored sa_ref code + * + * Revision 1.50 2002/12/13 18:06:52 mcr + * temporarily removed sadb_x_sa_ref reference for 2.xx + * + * Revision 1.49 2002/10/05 05:02:58 dhr + * + * C labels go on statements + * + * Revision 1.48 2002/09/20 15:40:45 rgb + * Added sadb_x_sa_ref to struct sadb_sa. + * + * Revision 1.47 2002/09/20 05:01:31 rgb + * Fixed usage of pfkey_lib_debug. + * Format for function declaration style consistency. + * Added text labels to elucidate numeric values presented. + * Re-organised debug output to reduce noise in output. + * + * Revision 1.46 2002/07/24 18:44:54 rgb + * Type fiddling to tame ia64 compiler. + * + * Revision 1.45 2002/05/23 07:14:11 rgb + * Cleaned up %p variants to 0p%p for test suite cleanup. + * + * Revision 1.44 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.43 2002/04/24 07:36:40 mcr + * Moved from ./lib/pfkey_v2_parse.c,v + * + * Revision 1.42 2002/01/29 22:25:36 rgb + * Re-add ipsec_kversion.h to keep MALLOC happy. + * + * Revision 1.41 2002/01/29 01:59:10 mcr + * removal of kversions.h - sources that needed it now use ipsec_param.h. + * updating of IPv6 structures to match latest in6.h version. + * removed dead code from freeswan.h that also duplicated kversions.h + * code. + * + * Revision 1.40 2002/01/20 20:34:50 mcr + * added pfkey_v2_sadb_type_string to decode sadb_type to string. + * + * Revision 1.39 2001/11/27 05:29:22 mcr + * pfkey parses are now maintained by a structure + * that includes their name for debug purposes. + * DEBUGGING() macro changed so that it takes a debug + * level so that pf_key() can use this to decode the + * structures without innundanting humans. + * Also uses pfkey_v2_sadb_ext_string() in messages. + * + * Revision 1.38 2001/11/06 19:47:47 rgb + * Added packet parameter to lifetime and comb structures. + * + * Revision 1.37 2001/10/18 04:45:24 rgb + * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, + * lib/freeswan.h version macros moved to lib/kversions.h. + * Other compiler directive cleanups. + * + * Revision 1.36 2001/06/14 19:35:16 rgb + * Update copyright date. + * + * Revision 1.35 2001/05/03 19:44:51 rgb + * Standardise on SENDERR() macro. + * + * Revision 1.34 2001/03/16 07:41:51 rgb + * Put freeswan.h include before pluto includes. + * + * Revision 1.33 2001/02/27 07:13:51 rgb + * Added satype2name() function. + * Added text to default satype_tbl entry. + * Added satype2name() conversions for most satype debug output. + * + * Revision 1.32 2001/02/26 20:01:09 rgb + * Added internal IP protocol 61 for magic SAs. + * Ditch unused sadb_satype2proto[], replaced by satype2proto(). + * Re-formatted debug output (split lines, consistent spacing). + * Removed acquire, register and expire requirements for a known satype. + * Changed message type checking to a switch structure. + * Verify expected NULL auth for IPCOMP. + * Enforced spi > 0x100 requirement, now that pass uses a magic SA for + * appropriate message types. + * + * Revision 1.31 2000/12/01 07:09:00 rgb + * Added ipcomp sanity check to require encalgo is set. + * + * Revision 1.30 2000/11/17 18:10:30 rgb + * Fixed bugs mostly relating to spirange, to treat all spi variables as + * network byte order since this is the way PF_KEYv2 stored spis. + * + * Revision 1.29 2000/10/12 00:02:39 rgb + * Removed 'format, ##' nonsense from debug macros for RH7.0. + * + * Revision 1.28 2000/09/20 16:23:04 rgb + * Remove over-paranoid extension check in the presence of sadb_msg_errno. + * + * Revision 1.27 2000/09/20 04:04:21 rgb + * Changed static functions to DEBUG_NO_STATIC to reveal function names in + * oopsen. + * + * Revision 1.26 2000/09/15 11:37:02 rgb + * Merge in heavily modified Svenning Soerensen's + * IPCOMP zlib deflate code. + * + * Revision 1.25 2000/09/12 22:35:37 rgb + * Restructured to remove unused extensions from CLEARFLOW messages. + * + * Revision 1.24 2000/09/12 18:59:54 rgb + * Added Gerhard's IPv6 support to pfkey parts of libfreeswan. + * + * Revision 1.23 2000/09/12 03:27:00 rgb + * Moved DEBUGGING definition to compile kernel with debug off. + * + * Revision 1.22 2000/09/09 06:39:27 rgb + * Restrict pfkey errno check to downward messages only. + * + * Revision 1.21 2000/09/08 19:22:34 rgb + * Enabled pfkey_sens_parse(). + * Added check for errno on downward acquire messages only. + * + * Revision 1.20 2000/09/01 18:48:23 rgb + * Fixed reserved check bug and added debug output in + * pfkey_supported_parse(). + * Fixed debug output label bug in pfkey_ident_parse(). + * + * Revision 1.19 2000/08/27 01:55:26 rgb + * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code. + * + * Revision 1.18 2000/08/24 17:00:36 rgb + * Ignore unknown extensions instead of failing. + * + * Revision 1.17 2000/06/02 22:54:14 rgb + * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support. + * + * Revision 1.16 2000/05/10 19:25:11 rgb + * Fleshed out proposal and supported extensions. + * + * Revision 1.15 2000/01/24 21:15:31 rgb + * Added disabled pluto pfkey lib debug flag. + * Added algo debugging reporting. + * + * Revision 1.14 2000/01/22 23:24:29 rgb + * Added new functions proto2satype() and satype2proto() and lookup + * table satype_tbl. Also added proto2name() since it was easy. + * + * Revision 1.13 2000/01/21 09:43:59 rgb + * Cast ntohl(spi) as (unsigned long int) to shut up compiler. + * + * Revision 1.12 2000/01/21 06:28:19 rgb + * Added address cases for eroute flows. + * Indented compiler directives for readability. + * Added klipsdebug switching capability. + * + * Revision 1.11 1999/12/29 21:14:59 rgb + * Fixed debug text cut and paste typo. + * + * Revision 1.10 1999/12/10 17:45:24 rgb + * Added address debugging. + * + * Revision 1.9 1999/12/09 23:11:42 rgb + * Ditched include since we no longer use memset(). + * Use new pfkey_extensions_init() instead of memset(). + * Added check for SATYPE in pfkey_msg_build(). + * Tidy up comments and debugging comments. + * + * Revision 1.8 1999/12/07 19:55:26 rgb + * Removed unused first argument from extension parsers. + * Removed static pluto debug flag. + * Moved message type and state checking to pfkey_msg_parse(). + * Changed print[fk] type from lx to x to quiet compiler. + * Removed redundant remain check. + * Changed __u* types to uint* to avoid use of asm/types.h and + * sys/types.h in userspace code. + * + * Revision 1.7 1999/12/01 22:20:51 rgb + * Moved pfkey_lib_debug variable into the library. + * Added pfkey version check into header parsing. + * Added check for SATYPE only for those extensions that require a + * non-zero value. + * + * Revision 1.6 1999/11/27 11:58:05 rgb + * Added ipv6 headers. + * Moved sadb_satype2proto protocol lookup table from + * klips/net/ipsec/pfkey_v2_parser.c. + * Enable lifetime_current checking. + * Debugging error messages added. + * Add argument to pfkey_msg_parse() for direction. + * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. + * Add CVS log entry to bottom of file. + * Moved auth and enc alg check to pfkey_msg_parse(). + * Enable accidentally disabled spirange parsing. + * Moved protocol/algorithm checks from klips/net/ipsec/pfkey_v2_parser.c + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/src/libfreeswan/pfkeyv2.h b/src/libfreeswan/pfkeyv2.h new file mode 100644 index 000000000..07126f1b8 --- /dev/null +++ b/src/libfreeswan/pfkeyv2.h @@ -0,0 +1,375 @@ +/* + * RCSID $Id: pfkeyv2.h,v 1.5 2004/10/04 22:43:56 as Exp $ + */ + +/* +RFC 2367 PF_KEY Key Management API July 1998 + + +Appendix D: Sample Header File + +This file defines structures and symbols for the PF_KEY Version 2 +key management interface. It was written at the U.S. Naval Research +Laboratory. This file is in the public domain. The authors ask that +you leave this credit intact on any copies of this file. +*/ +#ifndef __PFKEY_V2_H +#define __PFKEY_V2_H 1 + +#define PF_KEY_V2 2 +#define PFKEYV2_REVISION 199806L + +#define SADB_RESERVED 0 +#define SADB_GETSPI 1 +#define SADB_UPDATE 2 +#define SADB_ADD 3 +#define SADB_DELETE 4 +#define SADB_GET 5 +#define SADB_ACQUIRE 6 +#define SADB_REGISTER 7 +#define SADB_EXPIRE 8 +#define SADB_FLUSH 9 +#define SADB_DUMP 10 +#define SADB_X_PROMISC 11 +#define SADB_X_PCHANGE 12 +#define SADB_X_GRPSA 13 +#define SADB_X_ADDFLOW 14 +#define SADB_X_DELFLOW 15 +#define SADB_X_DEBUG 16 +#define SADB_X_NAT_T_NEW_MAPPING 17 +#define SADB_MAX 17 + +struct sadb_msg { + uint8_t sadb_msg_version; + uint8_t sadb_msg_type; + uint8_t sadb_msg_errno; + uint8_t sadb_msg_satype; + uint16_t sadb_msg_len; + uint16_t sadb_msg_reserved; + uint32_t sadb_msg_seq; + uint32_t sadb_msg_pid; +}; + +struct sadb_ext { + uint16_t sadb_ext_len; + uint16_t sadb_ext_type; +}; + +struct sadb_sa { + uint16_t sadb_sa_len; + uint16_t sadb_sa_exttype; + uint32_t sadb_sa_spi; + uint8_t sadb_sa_replay; + uint8_t sadb_sa_state; + uint8_t sadb_sa_auth; + uint8_t sadb_sa_encrypt; + uint32_t sadb_sa_flags; + uint32_t /*IPsecSAref_t*/ sadb_x_sa_ref; /* 32 bits */ + uint8_t sadb_x_reserved[4]; +}; + +struct sadb_sa_v1 { + uint16_t sadb_sa_len; + uint16_t sadb_sa_exttype; + uint32_t sadb_sa_spi; + uint8_t sadb_sa_replay; + uint8_t sadb_sa_state; + uint8_t sadb_sa_auth; + uint8_t sadb_sa_encrypt; + uint32_t sadb_sa_flags; +}; + +struct sadb_lifetime { + uint16_t sadb_lifetime_len; + uint16_t sadb_lifetime_exttype; + uint32_t sadb_lifetime_allocations; + uint64_t sadb_lifetime_bytes; + uint64_t sadb_lifetime_addtime; + uint64_t sadb_lifetime_usetime; + uint32_t sadb_x_lifetime_packets; + uint32_t sadb_x_lifetime_reserved; +}; + +struct sadb_address { + uint16_t sadb_address_len; + uint16_t sadb_address_exttype; + uint8_t sadb_address_proto; + uint8_t sadb_address_prefixlen; + uint16_t sadb_address_reserved; +}; + +struct sadb_key { + uint16_t sadb_key_len; + uint16_t sadb_key_exttype; + uint16_t sadb_key_bits; + uint16_t sadb_key_reserved; +}; + +struct sadb_ident { + uint16_t sadb_ident_len; + uint16_t sadb_ident_exttype; + uint16_t sadb_ident_type; + uint16_t sadb_ident_reserved; + uint64_t sadb_ident_id; +}; + +struct sadb_sens { + uint16_t sadb_sens_len; + uint16_t sadb_sens_exttype; + uint32_t sadb_sens_dpd; + uint8_t sadb_sens_sens_level; + uint8_t sadb_sens_sens_len; + uint8_t sadb_sens_integ_level; + uint8_t sadb_sens_integ_len; + uint32_t sadb_sens_reserved; +}; + +struct sadb_prop { + uint16_t sadb_prop_len; + uint16_t sadb_prop_exttype; + uint8_t sadb_prop_replay; + uint8_t sadb_prop_reserved[3]; +}; + +struct sadb_comb { + uint8_t sadb_comb_auth; + uint8_t sadb_comb_encrypt; + uint16_t sadb_comb_flags; + uint16_t sadb_comb_auth_minbits; + uint16_t sadb_comb_auth_maxbits; + uint16_t sadb_comb_encrypt_minbits; + uint16_t sadb_comb_encrypt_maxbits; + uint32_t sadb_comb_reserved; + uint32_t sadb_comb_soft_allocations; + uint32_t sadb_comb_hard_allocations; + uint64_t sadb_comb_soft_bytes; + uint64_t sadb_comb_hard_bytes; + uint64_t sadb_comb_soft_addtime; + uint64_t sadb_comb_hard_addtime; + uint64_t sadb_comb_soft_usetime; + uint64_t sadb_comb_hard_usetime; + uint32_t sadb_x_comb_soft_packets; + uint32_t sadb_x_comb_hard_packets; +}; + +struct sadb_supported { + uint16_t sadb_supported_len; + uint16_t sadb_supported_exttype; + uint32_t sadb_supported_reserved; +}; + +struct sadb_alg { + uint8_t sadb_alg_id; + uint8_t sadb_alg_ivlen; + uint16_t sadb_alg_minbits; + uint16_t sadb_alg_maxbits; + uint16_t sadb_alg_reserved; +}; + +struct sadb_spirange { + uint16_t sadb_spirange_len; + uint16_t sadb_spirange_exttype; + uint32_t sadb_spirange_min; + uint32_t sadb_spirange_max; + uint32_t sadb_spirange_reserved; +}; + +struct sadb_x_kmprivate { + uint16_t sadb_x_kmprivate_len; + uint16_t sadb_x_kmprivate_exttype; + uint32_t sadb_x_kmprivate_reserved; +}; + +struct sadb_x_satype { + uint16_t sadb_x_satype_len; + uint16_t sadb_x_satype_exttype; + uint8_t sadb_x_satype_satype; + uint8_t sadb_x_satype_reserved[3]; +}; + +struct sadb_x_policy { + uint16_t sadb_x_policy_len; + uint16_t sadb_x_policy_exttype; + uint16_t sadb_x_policy_type; + uint8_t sadb_x_policy_dir; + uint8_t sadb_x_policy_reserved; + uint32_t sadb_x_policy_id; + uint32_t sadb_x_policy_reserved2; +}; + +struct sadb_x_debug { + uint16_t sadb_x_debug_len; + uint16_t sadb_x_debug_exttype; + uint32_t sadb_x_debug_tunnel; + uint32_t sadb_x_debug_netlink; + uint32_t sadb_x_debug_xform; + uint32_t sadb_x_debug_eroute; + uint32_t sadb_x_debug_spi; + uint32_t sadb_x_debug_radij; + uint32_t sadb_x_debug_esp; + uint32_t sadb_x_debug_ah; + uint32_t sadb_x_debug_rcv; + uint32_t sadb_x_debug_pfkey; + uint32_t sadb_x_debug_ipcomp; + uint32_t sadb_x_debug_verbose; + uint8_t sadb_x_debug_reserved[4]; +}; + +struct sadb_x_nat_t_type { + uint16_t sadb_x_nat_t_type_len; + uint16_t sadb_x_nat_t_type_exttype; + uint8_t sadb_x_nat_t_type_type; + uint8_t sadb_x_nat_t_type_reserved[3]; +}; +struct sadb_x_nat_t_port { + uint16_t sadb_x_nat_t_port_len; + uint16_t sadb_x_nat_t_port_exttype; + uint16_t sadb_x_nat_t_port_port; + uint16_t sadb_x_nat_t_port_reserved; +}; + +/* + * A protocol structure for passing through the transport level + * protocol. It contains more fields than are actually used/needed + * but it is this way to be compatible with the structure used in + * OpenBSD (http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/pfkeyv2.h) + */ +struct sadb_protocol { + uint16_t sadb_protocol_len; + uint16_t sadb_protocol_exttype; + uint8_t sadb_protocol_proto; + uint8_t sadb_protocol_direction; + uint8_t sadb_protocol_flags; + uint8_t sadb_protocol_reserved2; +}; + +#define SADB_EXT_RESERVED 0 +#define SADB_EXT_SA 1 +#define SADB_EXT_LIFETIME_CURRENT 2 +#define SADB_EXT_LIFETIME_HARD 3 +#define SADB_EXT_LIFETIME_SOFT 4 +#define SADB_EXT_ADDRESS_SRC 5 +#define SADB_EXT_ADDRESS_DST 6 +#define SADB_EXT_ADDRESS_PROXY 7 +#define SADB_EXT_KEY_AUTH 8 +#define SADB_EXT_KEY_ENCRYPT 9 +#define SADB_EXT_IDENTITY_SRC 10 +#define SADB_EXT_IDENTITY_DST 11 +#define SADB_EXT_SENSITIVITY 12 +#define SADB_EXT_PROPOSAL 13 +#define SADB_EXT_SUPPORTED_AUTH 14 +#define SADB_EXT_SUPPORTED_ENCRYPT 15 +#define SADB_EXT_SPIRANGE 16 +#define SADB_X_EXT_KMPRIVATE 17 +#define SADB_X_EXT_SATYPE2 18 +#ifdef KERNEL26_HAS_KAME_DUPLICATES +#define SADB_X_EXT_POLICY 18 +#endif +#define SADB_X_EXT_SA2 19 +#define SADB_X_EXT_ADDRESS_DST2 20 +#define SADB_X_EXT_ADDRESS_SRC_FLOW 21 +#define SADB_X_EXT_ADDRESS_DST_FLOW 22 +#define SADB_X_EXT_ADDRESS_SRC_MASK 23 +#define SADB_X_EXT_ADDRESS_DST_MASK 24 +#define SADB_X_EXT_DEBUG 25 +#define SADB_X_EXT_PROTOCOL 26 +#define SADB_X_EXT_NAT_T_TYPE 27 +#define SADB_X_EXT_NAT_T_SPORT 28 +#define SADB_X_EXT_NAT_T_DPORT 29 +#define SADB_X_EXT_NAT_T_OA 30 +#define SADB_EXT_MAX 30 + +/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */ +#define SADB_X_EXT_ADDRESS_DELFLOW \ + ( (1<" +.sp +.B "int portof(const ip_address *src);" +.br +.B "void setportof(int port, ip_address *dst);" +.br +.B "struct sockaddr *sockaddrof(ip_address *src);" +.br +.B "size_t sockaddrlenof(const ip_address *src);" +.SH DESCRIPTION +The +.B +internal type +.I ip_address +contains one of the +.I sockaddr +types internally. +\fIReliance on this feature is discouraged\fR, +but it may occasionally be necessary. +These functions provide low-level tools for this purpose. +.PP +.I Portof +and +.I setportof +respectively read and write the port-number field of the internal +.IR sockaddr . +The values are in network byte order. +.PP +.I Sockaddrof +returns a pointer to the internal +.IR sockaddr , +for passing to other functions. +.PP +.I Sockaddrlenof +reports the size of the internal +.IR sockaddr , +for use in storage allocation. +.SH SEE ALSO +inet(3), ipsec_initaddr(3) +.SH DIAGNOSTICS +.I Portof +returns +.BR \-1 , +.I sockaddrof +returns +.BR NULL , +and +.I sockaddrlenof +returns +.B 0 +if an unknown address family is found within the +.IR ip_address . +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +These functions all depend on low-level details of the +.I ip_address +type, which are in principle subject to change. +Avoid using them unless really necessary. diff --git a/src/libfreeswan/portof.c b/src/libfreeswan/portof.c new file mode 100644 index 000000000..d028ea034 --- /dev/null +++ b/src/libfreeswan/portof.c @@ -0,0 +1,96 @@ +/* + * low-level ip_address ugliness + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: portof.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - portof - get the port field of an ip_address + */ +int /* network order */ +portof(src) +const ip_address *src; +{ + switch (src->u.v4.sin_family) { + case AF_INET: + return src->u.v4.sin_port; + break; + case AF_INET6: + return src->u.v6.sin6_port; + break; + default: + return -1; /* "can't happen" */ + break; + } +} + +/* + - setportof - set the port field of an ip_address + */ +void +setportof(port, dst) +int port; /* network order */ +ip_address *dst; +{ + switch (dst->u.v4.sin_family) { + case AF_INET: + dst->u.v4.sin_port = port; + break; + case AF_INET6: + dst->u.v6.sin6_port = port; + break; + } +} + +/* + - sockaddrof - get a pointer to the sockaddr hiding inside an ip_address + */ +struct sockaddr * +sockaddrof(src) +ip_address *src; +{ + switch (src->u.v4.sin_family) { + case AF_INET: + return (struct sockaddr *)&src->u.v4; + break; + case AF_INET6: + return (struct sockaddr *)&src->u.v6; + break; + default: + return NULL; /* "can't happen" */ + break; + } +} + +/* + - sockaddrlenof - get length of the sockaddr hiding inside an ip_address + */ +size_t /* 0 for error */ +sockaddrlenof(src) +const ip_address *src; +{ + switch (src->u.v4.sin_family) { + case AF_INET: + return sizeof(src->u.v4); + break; + case AF_INET6: + return sizeof(src->u.v6); + break; + default: + return 0; + break; + } +} diff --git a/src/libfreeswan/prng.3 b/src/libfreeswan/prng.3 new file mode 100644 index 000000000..51f19364f --- /dev/null +++ b/src/libfreeswan/prng.3 @@ -0,0 +1,121 @@ +.TH IPSEC_PRNG 3 "1 April 2002" +.\" RCSID $Id: prng.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec prng_init \- initialize IPsec pseudorandom-number generator +.br +ipsec prng_bytes \- get bytes from IPsec pseudorandom-number generator +.br +ipsec prng_final \- close down IPsec pseudorandom-number generator +.SH SYNOPSIS +.B "#include +.sp +.B "void prng_init(struct prng *prng," +.ti +1c +.B "const unsigned char *key, size_t keylen);" +.br +.B "void prng_bytes(struct prng *prng, char *dst," +.ti +1c +.B "size_t dstlen);" +.br +.B "unsigned long prng_count(struct prng *prng);" +.br +.B "void prng_final(struct prng *prng);" +.SH DESCRIPTION +.I Prng_init +initializes a crypto-quality pseudo-random-number generator from a key; +.I prng_bytes +obtains pseudo-random bytes from it; +.I prng_count +reports the number of bytes extracted from it to date; +.I prng_final +closes it down. +It is the user's responsibility to initialize a PRNG before using it, +and not to use it again after it is closed down. +.PP +.I Prng_init +initializes, +or re-initializes, +the specified +.I prng +from the +.IR key , +whose length is given by +.IR keylen . +The user must allocate the +.B "struct prng" +pointed to by +.IR prng . +There is no particular constraint on the length of the key, +although a key longer than 256 bytes is unnecessary because +only the first 256 would be used. +Initialization requires on the order of 3000 integer operations, +independent of key length. +.PP +.I Prng_bytes +obtains +.I dstlen +pseudo-random bytes from the PRNG and puts them in +.IR buf . +This is quite fast, +on the order of 10 integer operations per byte. +.PP +.I Prng_count +reports the number of bytes obtained from the PRNG +since it was (last) initialized. +.PP +.I Prng_final +closes down a PRNG by +zeroing its internal memory, +obliterating all trace of the state used to generate its previous output. +This requires on the order of 250 integer operations. +.PP +The +.B +header file supplies the definition of the +.B prng +structure. +Examination of its innards is discouraged, as they may change. +.PP +The PRNG algorithm +used by these functions is currently identical to that of RC4(TM). +This algorithm is cryptographically strong, +sufficiently unpredictable that even a hostile observer will +have difficulty determining the next byte of output from past history, +provided it is initialized from a reasonably large key composed of +highly random bytes (see +.IR random (4)). +The usual run of software pseudo-random-number generators +(e.g. +.IR random (3)) +are +.I not +cryptographically strong. +.PP +The well-known attacks against RC4(TM), +e.g. as found in 802.11b's WEP encryption system, +apply only if multiple PRNGs are initialized with closely-related keys +(e.g., using a counter appended to a base key). +If such keys are used, the first few hundred pseudo-random bytes +from each PRNG should be discarded, +to give the PRNGs a chance to randomize their innards properly. +No useful attacks are known if the key is well randomized to begin with. +.SH SEE ALSO +random(3), random(4) +.br +Bruce Schneier, +\fIApplied Cryptography\fR, 2nd ed., 1996, ISBN 0-471-11709-9, +pp. 397-8. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +If an attempt is made to obtain more than 4e9 bytes +between initializations, +the PRNG will continue to work but +.IR prng_count 's +output will stick at +.BR 4000000000 . +Fixing this would require a longer integer type and does +not seem worth the trouble, +since you should probably re-initialize before then anyway... +.PP +``RC4'' is a trademark of RSA Data Security, Inc. diff --git a/src/libfreeswan/prng.c b/src/libfreeswan/prng.c new file mode 100644 index 000000000..e31836783 --- /dev/null +++ b/src/libfreeswan/prng.c @@ -0,0 +1,202 @@ +/* + * crypto-class pseudorandom number generator + * currently uses same algorithm as RC4(TM), from Schneier 2nd ed p397 + * Copyright (C) 2002 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: prng.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - prng_init - initialize PRNG from a key + */ +void +prng_init(prng, key, keylen) +struct prng *prng; +const unsigned char *key; +size_t keylen; +{ + unsigned char k[256]; + int i, j; + unsigned const char *p; + unsigned const char *keyend = key + keylen; + unsigned char t; + + for (i = 0; i <= 255; i++) + prng->sbox[i] = i; + p = key; + for (i = 0; i <= 255; i++) { + k[i] = *p++; + if (p >= keyend) + p = key; + } + j = 0; + for (i = 0; i <= 255; i++) { + j = (j + prng->sbox[i] + k[i]) & 0xff; + t = prng->sbox[i]; + prng->sbox[i] = prng->sbox[j]; + prng->sbox[j] = t; + k[i] = 0; /* clear out key memory */ + } + prng->i = 0; + prng->j = 0; + prng->count = 0; +} + +/* + - prng_bytes - get some pseudorandom bytes from PRNG + */ +void +prng_bytes(prng, dst, dstlen) +struct prng *prng; +unsigned char *dst; +size_t dstlen; +{ + int i, j, t; + unsigned char *p = dst; + size_t remain = dstlen; +# define MAX 4000000000ul + + while (remain > 0) { + i = (prng->i + 1) & 0xff; + prng->i = i; + j = (prng->j + prng->sbox[i]) & 0xff; + prng->j = j; + t = prng->sbox[i]; + prng->sbox[i] = prng->sbox[j]; + prng->sbox[j] = t; + t = (t + prng->sbox[i]) & 0xff; + *p++ = prng->sbox[t]; + remain--; + } + if (prng->count < MAX - dstlen) + prng->count += dstlen; + else + prng->count = MAX; +} + +/* + - prnt_count - how many bytes have been extracted from PRNG so far? + */ +unsigned long +prng_count(prng) +struct prng *prng; +{ + return prng->count; +} + +/* + - prng_final - clear out PRNG to ensure nothing left in memory + */ +void +prng_final(prng) +struct prng *prng; +{ + int i; + + for (i = 0; i <= 255; i++) + prng->sbox[i] = 0; + prng->i = 0; + prng->j = 0; + prng->count = 0; /* just for good measure */ +} + + + +#ifdef PRNG_MAIN + +#include + +void regress(); + +int +main(argc, argv) +int argc; +char *argv[]; +{ + struct prng pr; + unsigned char buf[100]; + unsigned char *p; + size_t n; + + if (argc < 2) { + fprintf(stderr, "Usage: %s {key|-r}\n", argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + prng_init(&pr, argv[1], strlen(argv[1])); + prng_bytes(&pr, buf, 32); + printf("0x"); + for (p = buf, n = 32; n > 0; p++, n--) + printf("%02x", *p); + printf("\n%lu bytes\n", prng_count(&pr)); + prng_final(&pr); + exit(0); +} + +void +regress() +{ + struct prng pr; + unsigned char buf[100]; + unsigned char *p; + size_t n; + /* somewhat non-random sample key */ + unsigned char key[] = "here we go gathering nuts in May"; + /* first thirty bytes of output from that key */ + unsigned char good[] = "\x3f\x02\x8e\x4a\x2a\xea\x23\x18\x92\x7c" + "\x09\x52\x83\x61\xaa\x26\xce\xbb\x9d\x71" + "\x71\xe5\x10\x22\xaf\x60\x54\x8d\x5b\x28"; + int nzero, none; + int show = 0; + + prng_init(&pr, key, strlen(key)); + prng_bytes(&pr, buf, sizeof(buf)); + for (p = buf, n = sizeof(buf); n > 0; p++, n--) { + if (*p == 0) + nzero++; + if (*p == 255) + none++; + } + if (nzero > 3 || none > 3) { + fprintf(stderr, "suspiciously non-random output!\n"); + show = 1; + } + if (memcmp(buf, good, strlen(good)) != 0) { + fprintf(stderr, "incorrect output!\n"); + show = 1; + } + if (show) { + fprintf(stderr, "0x"); + for (p = buf, n = sizeof(buf); n > 0; p++, n--) + fprintf(stderr, "%02x", *p); + fprintf(stderr, "\n"); + exit(1); + } + if (prng_count(&pr) != sizeof(buf)) { + fprintf(stderr, "got %u bytes, but count is %lu\n", + sizeof(buf), prng_count(&pr)); + exit(1); + } + prng_final(&pr); + exit(0); +} + +#endif /* PRNG_MAIN */ diff --git a/src/libfreeswan/radij.h b/src/libfreeswan/radij.h new file mode 100644 index 000000000..2a66093a0 --- /dev/null +++ b/src/libfreeswan/radij.h @@ -0,0 +1,280 @@ +/* + * RCSID $Id: radij.h,v 1.1 2004/03/15 20:35:25 as Exp $ + */ + +/* + * This file is defived from ${SRC}/sys/net/radix.h of BSD 4.4lite + * + * Variable and procedure names have been modified so that they don't + * conflict with the original BSD code, as a small number of modifications + * have been introduced and we may want to reuse this code in BSD. + * + * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek + * chi or a German ch sound (as `doch', not as in `milch'), or even a + * spanish j as in Juan. It is not as far back in the throat like + * the corresponding Hebrew sound, nor is it a soft breath like the English h. + * It has nothing to do with the Dutch ij sound. + * + * Here is the appropriate copyright notice: + */ + +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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. + * + * @(#)radix.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _RADIJ_H_ +#define _RADIJ_H_ + +/* +#define RJ_DEBUG +*/ + +#ifdef __KERNEL__ + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +/* + * Radix search tree node layout. + */ + +struct radij_node +{ + struct radij_mask *rj_mklist; /* list of masks contained in subtree */ + struct radij_node *rj_p; /* parent */ + short rj_b; /* bit offset; -1-index(netmask) */ + char rj_bmask; /* node: mask for bit test*/ + u_char rj_flags; /* enumerated next */ +#define RJF_NORMAL 1 /* leaf contains normal route */ +#define RJF_ROOT 2 /* leaf is root leaf for tree */ +#define RJF_ACTIVE 4 /* This node is alive (for rtfree) */ + union { + struct { /* leaf only data: */ + caddr_t rj_Key; /* object of search */ + caddr_t rj_Mask; /* netmask, if present */ + struct radij_node *rj_Dupedkey; + } rj_leaf; + struct { /* node only data: */ + int rj_Off; /* where to start compare */ + struct radij_node *rj_L;/* progeny */ + struct radij_node *rj_R;/* progeny */ + }rj_node; + } rj_u; +#ifdef RJ_DEBUG + int rj_info; + struct radij_node *rj_twin; + struct radij_node *rj_ybro; +#endif +}; + +#define rj_dupedkey rj_u.rj_leaf.rj_Dupedkey +#define rj_key rj_u.rj_leaf.rj_Key +#define rj_mask rj_u.rj_leaf.rj_Mask +#define rj_off rj_u.rj_node.rj_Off +#define rj_l rj_u.rj_node.rj_L +#define rj_r rj_u.rj_node.rj_R + +/* + * Annotations to tree concerning potential routes applying to subtrees. + */ + +extern struct radij_mask { + short rm_b; /* bit offset; -1-index(netmask) */ + char rm_unused; /* cf. rj_bmask */ + u_char rm_flags; /* cf. rj_flags */ + struct radij_mask *rm_mklist; /* more masks to try */ + caddr_t rm_mask; /* the mask */ + int rm_refs; /* # of references to this struct */ +} *rj_mkfreelist; + +#define MKGet(m) {\ + if (rj_mkfreelist) {\ + m = rj_mkfreelist; \ + rj_mkfreelist = (m)->rm_mklist; \ + } else \ + R_Malloc(m, struct radij_mask *, sizeof (*(m))); }\ + +#define MKFree(m) { (m)->rm_mklist = rj_mkfreelist; rj_mkfreelist = (m);} + +struct radij_node_head { + struct radij_node *rnh_treetop; + int rnh_addrsize; /* permit, but not require fixed keys */ + int rnh_pktsize; /* permit, but not require fixed keys */ +#if 0 + struct radij_node *(*rnh_addaddr) /* add based on sockaddr */ + __P((void *v, void *mask, + struct radij_node_head *head, struct radij_node nodes[])); +#endif + int (*rnh_addaddr) /* add based on sockaddr */ + __P((void *v, void *mask, + struct radij_node_head *head, struct radij_node nodes[])); + struct radij_node *(*rnh_addpkt) /* add based on packet hdr */ + __P((void *v, void *mask, + struct radij_node_head *head, struct radij_node nodes[])); +#if 0 + struct radij_node *(*rnh_deladdr) /* remove based on sockaddr */ + __P((void *v, void *mask, struct radij_node_head *head)); +#endif + int (*rnh_deladdr) /* remove based on sockaddr */ + __P((void *v, void *mask, struct radij_node_head *head, struct radij_node **node)); + struct radij_node *(*rnh_delpkt) /* remove based on packet hdr */ + __P((void *v, void *mask, struct radij_node_head *head)); + struct radij_node *(*rnh_matchaddr) /* locate based on sockaddr */ + __P((void *v, struct radij_node_head *head)); + struct radij_node *(*rnh_matchpkt) /* locate based on packet hdr */ + __P((void *v, struct radij_node_head *head)); + int (*rnh_walktree) /* traverse tree */ + __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w)); + struct radij_node rnh_nodes[3]; /* empty tree for common case */ +}; + + +#define Bcmp(a, b, n) memcmp(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n)) +#define Bcopy(a, b, n) memmove(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n)) +#define Bzero(p, n) memset((caddr_t)(p), 0, (unsigned)(n)) +#define R_Malloc(p, t, n) ((p = (t) kmalloc((size_t)(n), GFP_ATOMIC)), Bzero((p),(n))) +#define Free(p) kfree((caddr_t)p); + +void rj_init __P((void)); +int rj_inithead __P((void **, int)); +int rj_refines __P((void *, void *)); +int rj_walktree __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w)); +struct radij_node + *rj_addmask __P((void *, int, int)) /* , rgb */ ; +int /* * */ rj_addroute __P((void *, void *, struct radij_node_head *, + struct radij_node [2])) /* , rgb */ ; +int /* * */ rj_delete __P((void *, void *, struct radij_node_head *, struct radij_node **)) /* , rgb */ ; +struct radij_node /* rgb */ + *rj_insert __P((void *, struct radij_node_head *, int *, + struct radij_node [2])), + *rj_match __P((void *, struct radij_node_head *)), + *rj_newpair __P((void *, int, struct radij_node[2])), + *rj_search __P((void *, struct radij_node *)), + *rj_search_m __P((void *, struct radij_node *, void *)); + +void rj_deltree(struct radij_node_head *); +void rj_delnodes(struct radij_node *); +void rj_free_mkfreelist(void); +int radijcleartree(void); +int radijcleanup(void); + +extern struct radij_node_head *mask_rjhead; +extern int maj_keylen; +#endif /* __KERNEL__ */ + +#endif /* _RADIJ_H_ */ + + +/* + * $Log: radij.h,v $ + * Revision 1.1 2004/03/15 20:35:25 as + * added files from freeswan-2.04-x509-1.5.3 + * + * Revision 1.12 2002/04/24 07:36:48 mcr + * Moved from ./klips/net/ipsec/radij.h,v + * + * Revision 1.11 2001/09/20 15:33:00 rgb + * Min/max cleanup. + * + * Revision 1.10 1999/11/18 04:09:20 rgb + * Replaced all kernel version macros to shorter, readable form. + * + * Revision 1.9 1999/05/05 22:02:33 rgb + * Add a quick and dirty port to 2.2 kernels by Marc Boucher . + * + * Revision 1.8 1999/04/29 15:24:58 rgb + * Add check for existence of macros min/max. + * + * Revision 1.7 1999/04/11 00:29:02 henry + * GPL boilerplate + * + * Revision 1.6 1999/04/06 04:54:29 rgb + * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes + * patch shell fixes. + * + * Revision 1.5 1999/01/22 06:30:32 rgb + * 64-bit clean-up. + * + * Revision 1.4 1998/11/30 13:22:55 rgb + * Rationalised all the klips kernel file headers. They are much shorter + * now and won't conflict under RH5.2. + * + * Revision 1.3 1998/10/25 02:43:27 rgb + * Change return type on rj_addroute and rj_delete and add and argument + * to the latter to be able to transmit more infomation about errors. + * + * Revision 1.2 1998/07/14 18:09:51 rgb + * Add a routine to clear eroute table. + * Added #ifdef __KERNEL__ directives to restrict scope of header. + * + * Revision 1.1 1998/06/18 21:30:22 henry + * move sources from klips/src to klips/net/ipsec to keep stupid kernel + * build scripts happier about symlinks + * + * Revision 1.4 1998/05/25 20:34:16 rgb + * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions. + * + * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and + * add ipsec_rj_walker_delete. + * + * Recover memory for eroute table on unload of module. + * + * Revision 1.3 1998/04/22 16:51:37 rgb + * Tidy up radij debug code from recent rash of modifications to debug code. + * + * Revision 1.2 1998/04/14 17:30:38 rgb + * Fix up compiling errors for radij tree memory reclamation. + * + * Revision 1.1 1998/04/09 03:06:16 henry + * sources moved up from linux/net/ipsec + * + * Revision 1.1.1.1 1998/04/08 05:35:04 henry + * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8 + * + * Revision 0.4 1997/01/15 01:28:15 ji + * No changes. + * + * Revision 0.3 1996/11/20 14:44:45 ji + * Release update only. + * + * Revision 0.2 1996/11/02 00:18:33 ji + * First limited release. + * + * + */ diff --git a/src/libfreeswan/rangetoa.c b/src/libfreeswan/rangetoa.c new file mode 100644 index 000000000..e63b432f8 --- /dev/null +++ b/src/libfreeswan/rangetoa.c @@ -0,0 +1,61 @@ +/* + * convert binary form of address range to ASCII + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: rangetoa.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - rangetoa - convert address range to ASCII + */ +size_t /* space needed for full conversion */ +rangetoa(addrs, format, dst, dstlen) +struct in_addr addrs[2]; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + size_t len; + size_t rest; + int n; + char *p; + + switch (format) { + case 0: + break; + default: + return 0; + break; + } + + len = addrtoa(addrs[0], 0, dst, dstlen); + if (len < dstlen) + for (p = dst + len - 1, n = 3; len < dstlen && n > 0; + p++, len++, n--) + *p = '.'; + else + p = NULL; + if (len < dstlen) + rest = dstlen - len; + else { + if (dstlen > 0) + *(dst + dstlen - 1) = '\0'; + rest = 0; + } + + len += addrtoa(addrs[1], 0, p, rest); + + return len; +} diff --git a/src/libfreeswan/rangetosubnet.3 b/src/libfreeswan/rangetosubnet.3 new file mode 100644 index 000000000..7d707545e --- /dev/null +++ b/src/libfreeswan/rangetosubnet.3 @@ -0,0 +1,59 @@ +.TH IPSEC_RANGETOSUBNET 3 "8 Sept 2000" +.\" RCSID $Id: rangetosubnet.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec rangetosubnet \- convert address range to subnet +.SH SYNOPSIS +.B "#include " +.sp +.B "const char *rangetosubnet(const ip_address *start," +.ti +1c +.B "const ip_address *stop, ip_subnet *dst);" +.SH DESCRIPTION +.I Rangetosubnet +accepts two IP addresses which define an address range, +from +.I start +to +.I stop +inclusive, +and converts this to a subnet if possible. +The addresses must both be IPv4 or both be IPv6, +and the address family of the resulting subnet is the same. +.PP +.I Rangetosubnet +returns NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.SH SEE ALSO +ipsec_initsubnet(3), ipsec_ttosubnet(3) +.SH DIAGNOSTICS +Fatal errors in +.I rangetosubnet +are: +mixed address families; +unknown address family; +.I start +and +.I stop +do not define a subnet. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The restriction of error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = rangetosubnet( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/rangetosubnet.c b/src/libfreeswan/rangetosubnet.c new file mode 100644 index 000000000..048b10556 --- /dev/null +++ b/src/libfreeswan/rangetosubnet.c @@ -0,0 +1,226 @@ +/* + * express an address range as a subnet (if possible) + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: rangetosubnet.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - rangetosubnet - turn an address range into a subnet, if possible + * + * A range which is a valid subnet will have a network part which is the + * same in the from value and the to value, followed by a host part which + * is all 0 in the from value and all 1 in the to value. + */ +err_t +rangetosubnet(from, to, dst) +const ip_address *from; +const ip_address *to; +ip_subnet *dst; +{ + unsigned const char *fp; + unsigned const char *tp; + unsigned fb; + unsigned tb; + unsigned const char *f; + unsigned const char *t; + size_t n; + size_t n2; + int i; + int nnet; + unsigned m; + + if (addrtypeof(from) != addrtypeof(to)) + return "mismatched address types"; + n = addrbytesptr(from, &fp); + if (n == 0) + return "unknown address type"; + n2 = addrbytesptr(to, &tp); + if (n != n2) + return "internal size mismatch in rangetosubnet"; + + f = fp; + t = tp; + nnet = 0; + for (i = n; i > 0 && *f == *t; i--, f++, t++) + nnet += 8; + if (i > 0 && !(*f == 0x00 && *t == 0xff)) { /* mid-byte bdry. */ + fb = *f++; + tb = *t++; + i--; + m = 0x80; + while ((fb&m) == (tb&m)) { + fb &= ~m; + tb |= m; + m >>= 1; + nnet++; + } + if (fb != 0x00 || tb != 0xff) + return "not a valid subnet"; + } + for (; i > 0 && *f == 0x00 && *t == 0xff; i--, f++, t++) + continue; + + if (i != 0) + return "invalid subnet"; + + return initsubnet(from, nnet, 'x', dst); +} + + + +#ifdef RANGETOSUBNET_MAIN + +#include + +void regress(void); + +int +main(int argc, char *argv[]) +{ + ip_address start; + ip_address stop; + ip_subnet sub; + char buf[100]; + const char *oops; + size_t n; + int af; + int i; + + if (argc == 2 && strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + if (argc < 3) { + fprintf(stderr, "Usage: %s [-6] start stop\n", argv[0]); + fprintf(stderr, " or: %s -r\n", argv[0]); + exit(2); + } + + af = AF_INET; + i = 1; + if (strcmp(argv[i], "-6") == 0) { + af = AF_INET6; + i++; + } + + oops = ttoaddr(argv[i], 0, af, &start); + if (oops != NULL) { + fprintf(stderr, "%s: start conversion failed: %s\n", argv[0], oops); + exit(1); + } + oops = ttoaddr(argv[i+1], 0, af, &stop); + if (oops != NULL) { + fprintf(stderr, "%s: stop conversion failed: %s\n", argv[0], oops); + exit(1); + } + oops = rangetosubnet(&start, &stop, &sub); + if (oops != NULL) { + fprintf(stderr, "%s: rangetosubnet failed: %s\n", argv[0], oops); + exit(1); + } + n = subnettot(&sub, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + fprintf(stderr, "%s: reverse conversion", argv[0]); + fprintf(stderr, " failed: need %ld bytes, have only %ld\n", + (long)n, (long)sizeof(buf)); + exit(1); + } + printf("%s\n", buf); + + exit(0); +} + +struct rtab { + int family; + char *start; + char *stop; + char *output; /* NULL means error expected */ +} rtab[] = { + {4, "1.2.3.0", "1.2.3.255", "1.2.3.0/24"}, + {4, "1.2.3.0", "1.2.3.7", "1.2.3.0/29"}, + {4, "1.2.3.240", "1.2.3.255", "1.2.3.240/28"}, + {4, "0.0.0.0", "255.255.255.255", "0.0.0.0/0"}, + {4, "1.2.3.4", "1.2.3.4", "1.2.3.4/32"}, + {4, "1.2.3.0", "1.2.3.254", NULL}, + {4, "1.2.3.0", "1.2.3.126", NULL}, + {4, "1.2.3.0", "1.2.3.125", NULL}, + {4, "1.2.0.0", "1.2.255.255", "1.2.0.0/16"}, + {4, "1.2.0.0", "1.2.0.255", "1.2.0.0/24"}, + {4, "1.2.255.0", "1.2.255.255", "1.2.255.0/24"}, + {4, "1.2.255.0", "1.2.254.255", NULL}, + {4, "1.2.255.1", "1.2.255.255", NULL}, + {4, "1.2.0.1", "1.2.255.255", NULL}, + {6, "1:2:3:4:5:6:7:0", "1:2:3:4:5:6:7:ffff", "1:2:3:4:5:6:7:0/112"}, + {6, "1:2:3:4:5:6:7:0", "1:2:3:4:5:6:7:fff", "1:2:3:4:5:6:7:0/116"}, + {6, "1:2:3:4:5:6:7:f0", "1:2:3:4:5:6:7:ff", "1:2:3:4:5:6:7:f0/124"}, + {4, NULL, NULL, NULL}, +}; + +void +regress() +{ + struct rtab *r; + int status = 0; + ip_address start; + ip_address stop; + ip_subnet sub; + char buf[100]; + const char *oops; + size_t n; + int af; + + for (r = rtab; r->start != NULL; r++) { + af = (r->family == 4) ? AF_INET : AF_INET6; + oops = ttoaddr(r->start, 0, af, &start); + if (oops != NULL) { + printf("surprise failure converting `%s'\n", r->start); + exit(1); + } + oops = ttoaddr(r->stop, 0, af, &stop); + if (oops != NULL) { + printf("surprise failure converting `%s'\n", r->stop); + exit(1); + } + oops = rangetosubnet(&start, &stop, &sub); + if (oops != NULL && r->output == NULL) + {} /* okay, error expected */ + else if (oops != NULL) { + printf("`%s'-`%s' rangetosubnet failed: %s\n", + r->start, r->stop, oops); + status = 1; + } else if (r->output == NULL) { + printf("`%s'-`%s' rangetosubnet succeeded unexpectedly\n", + r->start, r->stop); + status = 1; + } else { + n = subnettot(&sub, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + printf("`%s'-`%s' subnettot failed: need %ld\n", + r->start, r->stop, (long)n); + status = 1; + } else if (strcmp(r->output, buf) != 0) { + printf("`%s'-`%s' gave `%s', expected `%s'\n", + r->start, r->stop, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* RANGETOSUBNET_MAIN */ diff --git a/src/libfreeswan/sameaddr.3 b/src/libfreeswan/sameaddr.3 new file mode 100644 index 000000000..71be10761 --- /dev/null +++ b/src/libfreeswan/sameaddr.3 @@ -0,0 +1,165 @@ +.TH IPSEC_ANYADDR 3 "28 Nov 2000" +.\" RCSID $Id: sameaddr.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec sameaddr \- are two addresses the same? +.br +ipsec addrcmp \- ordered comparison of addresses +.br +ipsec samesubnet \- are two subnets the same? +.br +ipsec addrinsubnet \- is an address within a subnet? +.br +ipsec subnetinsubnet \- is a subnet within another subnet? +.br +ipsec subnetishost \- is a subnet a single host? +.br +ipsec samesaid \- are two SA IDs the same? +.br +ipsec sameaddrtype \- are two addresses of the same address family? +.br +ipsec samesubnettype \- are two subnets of the same address family? +.SH SYNOPSIS +.B "#include +.sp +.B "int sameaddr(const ip_address *a, const ip_address *b);" +.br +.B "int addrcmp(const ip_address *a, const ip_address *b);" +.br +.B "int samesubnet(const ip_subnet *a, const ip_subnet *b);" +.br +.B "int addrinsubnet(const ip_address *a, const ip_subnet *s);" +.br +.B "int subnetinsubnet(const ip_subnet *a, const ip_subnet *b);" +.br +.B "int subnetishost(const ip_subnet *s);" +.br +.B "int samesaid(const ip_said *a, const ip_said *b);" +.br +.B "int sameaddrtype(const ip_address *a, const ip_address *b);" +.br +.B "int samesubnettype(const ip_subnet *a, const ip_subnet *b);" +.SH DESCRIPTION +These functions do various comparisons and tests on the +.I ip_address +type and +.I ip_subnet +types. +.PP +.I Sameaddr +returns +non-zero +if addresses +.I a +and +.IR b +are identical, +and +.B 0 +otherwise. +Addresses of different families are never identical. +.PP +.I Addrcmp +returns +.BR \-1 , +.BR 0 , +or +.BR 1 +respectively +if address +.I a +is less than, equal to, or greater than +.IR b . +If they are not of the same address family, +they are never equal; +the ordering reported in this case is arbitrary +(and probably not useful) but consistent. +.PP +.I Samesubnet +returns +non-zero +if subnets +.I a +and +.IR b +are identical, +and +.B 0 +otherwise. +Subnets of different address families are never identical. +.PP +.I Addrinsubnet +returns +non-zero +if address +.I a +is within subnet +.IR s +and +.B 0 +otherwise. +An address is never within a +subnet of a different address family. +.PP +.I Subnetinsubnet +returns +non-zero +if subnet +.I a +is a subset of subnet +.IR b +and +.B 0 +otherwise. +A subnet is deemed to be a subset of itself. +A subnet is never a subset of another +subnet if their address families differ. +.PP +.I Subnetishost +returns +non-zero +if subnet +.I s +is in fact only a single host, +and +.B 0 +otherwise. +.PP +.I Samesaid +returns +non-zero +if SA IDs +.I a +and +.IR b +are identical, +and +.B 0 +otherwise. +.PP +.I Sameaddrtype +returns +non-zero +if addresses +.I a +and +.IR b +are of the same address family, +and +.B 0 +otherwise. +.PP +.I Samesubnettype +returns +non-zero +if subnets +.I a +and +.IR b +are of the same address family, +and +.B 0 +otherwise. +.SH SEE ALSO +inet(3), ipsec_initaddr(3) +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. diff --git a/src/libfreeswan/sameaddr.c b/src/libfreeswan/sameaddr.c new file mode 100644 index 000000000..efc40796e --- /dev/null +++ b/src/libfreeswan/sameaddr.c @@ -0,0 +1,190 @@ +/* + * comparisons + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: sameaddr.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static int samenbits(const ip_address *a, const ip_address *b, int n); + +/* + - addrcmp - compare two addresses + * Caution, the order of the tests is subtle: doing type test before + * size test can yield cases where ac. + */ +int /* like memcmp */ +addrcmp(a, b) +const ip_address *a; +const ip_address *b; +{ + int at = addrtypeof(a); + int bt = addrtypeof(b); + const unsigned char *ap; + const unsigned char *bp; + size_t as = addrbytesptr(a, &ap); + size_t bs = addrbytesptr(b, &bp); + size_t n = (as < bs) ? as : bs; /* min(as, bs) */ + int c = memcmp(ap, bp, n); + + if (c != 0) /* bytes differ */ + return (c < 0) ? -1 : 1; + if (as != bs) /* comparison incomplete: lexical order */ + return (as < bs) ? -1 : 1; + if (at != bt) /* bytes same but not same type: break tie */ + return (at < bt) ? -1 : 1; + return 0; +} + +/* + - sameaddr - are two addresses the same? + */ +int +sameaddr(a, b) +const ip_address *a; +const ip_address *b; +{ + return (addrcmp(a, b) == 0) ? 1 : 0; +} + +/* + - samesubnet - are two subnets the same? + */ +int +samesubnet(a, b) +const ip_subnet *a; +const ip_subnet *b; +{ + if (!sameaddr(&a->addr, &b->addr)) /* also does type check */ + return 0; + if (a->maskbits != b->maskbits) + return 0; + return 1; +} + +/* + - subnetishost - is a subnet in fact a single host? + */ +int +subnetishost(a) +const ip_subnet *a; +{ + return (a->maskbits == addrlenof(&a->addr)*8) ? 1 : 0; +} + +/* + - samesaid - are two SA IDs the same? + */ +int +samesaid(a, b) +const ip_said *a; +const ip_said *b; +{ + if (a->spi != b->spi) /* test first, most likely to be different */ + return 0; + if (!sameaddr(&a->dst, &b->dst)) + return 0; + if (a->proto != b->proto) + return 0; + return 1; +} + +/* + - sameaddrtype - do two addresses have the same type? + */ +int +sameaddrtype(a, b) +const ip_address *a; +const ip_address *b; +{ + return (addrtypeof(a) == addrtypeof(b)) ? 1 : 0; +} + +/* + - samesubnettype - do two subnets have the same type? + */ +int +samesubnettype(a, b) +const ip_subnet *a; +const ip_subnet *b; +{ + return (subnettypeof(a) == subnettypeof(b)) ? 1 : 0; +} + +/* + - addrinsubnet - is this address in this subnet? + */ +int +addrinsubnet(a, s) +const ip_address *a; +const ip_subnet *s; +{ + if (addrtypeof(a) != subnettypeof(s)) + return 0; + if (!samenbits(a, &s->addr, s->maskbits)) + return 0; + return 1; +} + +/* + - subnetinsubnet - is one subnet within another? + */ +int +subnetinsubnet(a, b) +const ip_subnet *a; +const ip_subnet *b; +{ + if (subnettypeof(a) != subnettypeof(b)) + return 0; + if (a->maskbits < b->maskbits) /* a is bigger than b */ + return 0; + if (!samenbits(&a->addr, &b->addr, b->maskbits)) + return 0; + return 1; +} + +/* + - samenbits - do two addresses have the same first n bits? + */ +static int +samenbits(a, b, nbits) +const ip_address *a; +const ip_address *b; +int nbits; +{ + const unsigned char *ap; + const unsigned char *bp; + size_t n; + int m; + + if (addrtypeof(a) != addrtypeof(b)) + return 0; /* arbitrary */ + n = addrbytesptr(a, &ap); + if (n == 0) + return 0; /* arbitrary */ + (void) addrbytesptr(b, &bp); + if (nbits > n*8) + return 0; /* "can't happen" */ + + for (; nbits >= 8 && *ap == *bp; nbits -= 8, ap++, bp++) + continue; + if (nbits >= 8) + return 0; + if (nbits > 0) { /* partial byte */ + m = ~(0xff >> nbits); + if ((*ap & m) != (*bp & m)) + return 0; + } + return 1; +} diff --git a/src/libfreeswan/satoa.c b/src/libfreeswan/satoa.c new file mode 100644 index 000000000..410fb8437 --- /dev/null +++ b/src/libfreeswan/satoa.c @@ -0,0 +1,102 @@ +/* + * convert from binary form of SA ID to ASCII + * Copyright (C) 1998, 1999, 2001 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: satoa.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static struct typename { + char type; + char *name; +} typenames[] = { + { SA_AH, "ah" }, + { SA_ESP, "esp" }, + { SA_IPIP, "tun" }, + { SA_COMP, "comp" }, + { SA_INT, "int" }, + { 0, NULL } +}; + +/* + - satoa - convert SA to ASCII "ah507@1.2.3.4" + */ +size_t /* space needed for full conversion */ +satoa(sa, format, dst, dstlen) +struct sa_id sa; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + size_t len = 0; /* 0 means not handled yet */ + int base; + struct typename *tn; + char buf[30+ADDRTOA_BUF]; + + switch (format) { + case 0: + base = 16; /* temporarily at least */ + break; + case 'd': + base = 10; + break; + default: + return 0; + break; + } + + for (tn = typenames; tn->name != NULL; tn++) + if (sa.proto == tn->type) + break; + if (tn->name == NULL) + return 0; + + if (strcmp(tn->name, PASSTHROUGHTYPE) == 0 && + sa.spi == PASSTHROUGHSPI && + sa.dst.s_addr == PASSTHROUGHDST) { + strcpy(buf, PASSTHROUGHNAME); + len = strlen(buf); + } else if (sa.proto == SA_INT && sa.dst.s_addr == 0) { + char *p; + + switch (ntohl(sa.spi)) { + case SPI_PASS: p = "%pass"; break; + case SPI_DROP: p = "%drop"; break; + case SPI_REJECT: p = "%reject"; break; + case SPI_HOLD: p = "%hold"; break; + case SPI_TRAP: p = "%trap"; break; + case SPI_TRAPSUBNET: p = "%trapsubnet"; break; + default: p = NULL; break; + } + if (p != NULL) { + strcpy(buf, p); + len = strlen(buf); + } + } + + if (len == 0) { + strcpy(buf, tn->name); + len = strlen(buf); + len += ultoa(ntohl(sa.spi), base, buf+len, sizeof(buf)-len); + *(buf+len-1) = '@'; + len += addrtoa(sa.dst, 0, buf+len, sizeof(buf)-len); + } + + if (dst != NULL) { + if (len > dstlen) + *(buf+dstlen-1) = '\0'; + strcpy(dst, buf); + } + return len; +} diff --git a/src/libfreeswan/satot.c b/src/libfreeswan/satot.c new file mode 100644 index 000000000..927f4ca1f --- /dev/null +++ b/src/libfreeswan/satot.c @@ -0,0 +1,132 @@ +/* + * convert from binary form of SA ID to text + * Copyright (C) 2000, 2001 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: satot.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static struct typename { + char type; + char *name; +} typenames[] = { + { SA_AH, "ah" }, + { SA_ESP, "esp" }, + { SA_IPIP, "tun" }, + { SA_COMP, "comp" }, + { SA_INT, "int" }, + { 0, NULL } +}; + +/* + - satot - convert SA to text "ah507@1.2.3.4" + */ +size_t /* space needed for full conversion */ +satot(sa, format, dst, dstlen) +const ip_said *sa; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + size_t len = 0; /* 0 means "not recognized yet" */ + int base; + int showversion; /* use delimiter to show IP version? */ + struct typename *tn; + char *p; + char *pre; + char buf[10+1+ULTOT_BUF+ADDRTOT_BUF]; + char unk[10]; + + switch (format) { + case 0: + base = 16; + showversion = 1; + break; + case 'f': + base = 17; + showversion = 1; + break; + case 'x': + base = 'x'; + showversion = 0; + break; + case 'd': + base = 10; + showversion = 0; + break; + default: + return 0; + break; + } + + pre = NULL; + for (tn = typenames; tn->name != NULL; tn++) + if (sa->proto == tn->type) { + pre = tn->name; + break; /* NOTE BREAK OUT */ + } + if (pre == NULL) { /* unknown protocol */ + strcpy(unk, "unk"); + (void) ultot((unsigned char)sa->proto, 10, unk+strlen(unk), + sizeof(unk)-strlen(unk)); + pre = unk; + } + + if (strcmp(pre, PASSTHROUGHTYPE) == 0 && + sa->spi == PASSTHROUGHSPI && + isunspecaddr(&sa->dst)) { + strcpy(buf, (addrtypeof(&sa->dst) == AF_INET) ? + PASSTHROUGH4NAME : + PASSTHROUGH6NAME); + len = strlen(buf); + } + + if (sa->proto == SA_INT && addrtypeof(&sa->dst) == AF_INET && + isunspecaddr(&sa->dst)) { + switch (ntohl(sa->spi)) { + case SPI_PASS: p = "%pass"; break; + case SPI_DROP: p = "%drop"; break; + case SPI_REJECT: p = "%reject"; break; + case SPI_HOLD: p = "%hold"; break; + case SPI_TRAP: p = "%trap"; break; + case SPI_TRAPSUBNET: p = "%trapsubnet"; break; + default: p = NULL; break; + } + if (p != NULL) { + strcpy(buf, p); + len = strlen(buf); + } + } + + if (len == 0) { /* general case needed */ + strcpy(buf, pre); + len = strlen(buf); + if (showversion) { + *(buf+len) = (addrtypeof(&sa->dst) == AF_INET) ? '.' : + ':'; + len++; + *(buf+len) = '\0'; + } + len += ultot(ntohl(sa->spi), base, buf+len, sizeof(buf)-len); + *(buf+len-1) = '@'; + len += addrtot(&sa->dst, 0, buf+len, sizeof(buf)-len); + } + + if (dst != NULL) { + if (len > dstlen) + *(buf+dstlen-1) = '\0'; + strcpy(dst, buf); + } + return len; +} diff --git a/src/libfreeswan/subnetof.3 b/src/libfreeswan/subnetof.3 new file mode 100644 index 000000000..1911e499f --- /dev/null +++ b/src/libfreeswan/subnetof.3 @@ -0,0 +1,47 @@ +.TH IPSEC_SUBNETOF 3 "11 June 2001" +.\" RCSID $Id: subnetof.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec subnetof \- given Internet address and subnet mask, return subnet number +.br +ipsec hostof \- given Internet address and subnet mask, return host part +.br +ipsec broadcastof \- given Internet address and subnet mask, return broadcast address +.SH SYNOPSIS +.B "#include +.sp +.B "struct in_addr subnetof(struct in_addr addr," +.ti +1c +.B "struct in_addr mask);" +.br +.B "struct in_addr hostof(struct in_addr addr," +.ti +1c +.B "struct in_addr mask);" +.br +.B "struct in_addr broadcastof(struct in_addr addr," +.ti +1c +.B "struct in_addr mask);" +.SH DESCRIPTION +These functions are obsolete; see +.IR ipsec_networkof (3) +for their replacements. +.PP +.I Subnetof +takes an Internet +.I address +and a subnet +.I mask +and returns the network part of the address +(all in network byte order). +.I Hostof +similarly returns the host part, and +.I broadcastof +returns the broadcast address (all-1s convention) for the network. +.PP +These functions are provided to hide the Internet bit-munging inside +an API, in hopes of easing the eventual transition to IPv6. +.SH SEE ALSO +inet(3), ipsec_atosubnet(3) +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +Calling functions for this is more costly than doing it yourself. diff --git a/src/libfreeswan/subnetof.c b/src/libfreeswan/subnetof.c new file mode 100644 index 000000000..1b288c591 --- /dev/null +++ b/src/libfreeswan/subnetof.c @@ -0,0 +1,60 @@ +/* + * minor network-address manipulation utilities + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: subnetof.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - subnetof - given address and mask, return subnet part + */ +struct in_addr +subnetof(addr, mask) +struct in_addr addr; +struct in_addr mask; +{ + struct in_addr result; + + result.s_addr = addr.s_addr & mask.s_addr; + return result; +} + +/* + - hostof - given address and mask, return host part + */ +struct in_addr +hostof(addr, mask) +struct in_addr addr; +struct in_addr mask; +{ + struct in_addr result; + + result.s_addr = addr.s_addr & ~mask.s_addr; + return result; +} + +/* + - broadcastof - given (network) address and mask, return broadcast address + */ +struct in_addr +broadcastof(addr, mask) +struct in_addr addr; +struct in_addr mask; +{ + struct in_addr result; + + result.s_addr = addr.s_addr | ~mask.s_addr; + return result; +} diff --git a/src/libfreeswan/subnettoa.c b/src/libfreeswan/subnettoa.c new file mode 100644 index 000000000..36cad8b88 --- /dev/null +++ b/src/libfreeswan/subnettoa.c @@ -0,0 +1,62 @@ +/* + * convert binary form of subnet description to ASCII + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: subnettoa.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - subnettoa - convert address and mask to ASCII "addr/mask" + * Output expresses the mask as a bit count if possible, else dotted decimal. + */ +size_t /* space needed for full conversion */ +subnettoa(addr, mask, format, dst, dstlen) +struct in_addr addr; +struct in_addr mask; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + size_t len; + size_t rest; + int n; + char *p; + + switch (format) { + case 0: + break; + default: + return 0; + break; + } + + len = addrtoa(addr, 0, dst, dstlen); + if (len < dstlen) { + dst[len - 1] = '/'; + p = dst + len; + rest = dstlen - len; + } else { + p = NULL; + rest = 0; + } + + n = masktobits(mask); + if (n >= 0) + len += ultoa((unsigned long)n, 10, p, rest); + else + len += addrtoa(mask, 0, p, rest); + + return len; +} diff --git a/src/libfreeswan/subnettot.c b/src/libfreeswan/subnettot.c new file mode 100644 index 000000000..0385d25e5 --- /dev/null +++ b/src/libfreeswan/subnettot.c @@ -0,0 +1,56 @@ +/* + * convert binary form of subnet description to text + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: subnettot.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - subnettot - convert subnet to text "addr/bitcount" + */ +size_t /* space needed for full conversion */ +subnettot(sub, format, dst, dstlen) +const ip_subnet *sub; +int format; /* character */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + size_t len; + size_t rest; + char *p; + + switch (format) { + case 0: + break; + default: + return 0; + break; + } + + len = addrtot(&sub->addr, format, dst, dstlen); + if (len < dstlen) { + dst[len - 1] = '/'; + p = dst + len; + rest = dstlen - len; + } else { + p = NULL; + rest = 0; + } + + + len += ultoa((unsigned long)sub->maskbits, 10, p, rest); + + return len; +} diff --git a/src/libfreeswan/subnettypeof.c b/src/libfreeswan/subnettypeof.c new file mode 100644 index 000000000..6f44b2e4b --- /dev/null +++ b/src/libfreeswan/subnettypeof.c @@ -0,0 +1,109 @@ +/* + * extract parts of an ip_subnet, and related + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: subnettypeof.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - subnettypeof - get the address type of an ip_subnet + */ +int +subnettypeof(src) +const ip_subnet *src; +{ + return src->addr.u.v4.sin_family; +} + +/* + - networkof - get the network address of a subnet + */ +void +networkof(src, dst) +const ip_subnet *src; +ip_address *dst; +{ + *dst = src->addr; +} + +/* + - maskof - get the mask of a subnet, as an address + */ +void +maskof(src, dst) +const ip_subnet *src; +ip_address *dst; +{ + int b; + unsigned char buf[16]; + size_t n = addrlenof(&src->addr); + unsigned char *p; + + if (src->maskbits > n*8 || n > sizeof(buf)) + return; /* "can't happen" */ + + p = buf; + for (b = src->maskbits; b >= 8; b -= 8) + *p++ = 0xff; + if (b != 0) + *p++ = (0xff << (8 - b)) & 0xff; + while (p - buf < n) + *p++ = 0; + + (void) initaddr(buf, n, addrtypeof(&src->addr), dst); +} + +/* + - masktocount - convert a mask, expressed as an address, to a bit count + */ +int /* -1 if not valid mask */ +masktocount(src) +const ip_address *src; +{ + int b; + unsigned const char *bp; + size_t n; + unsigned const char *p; + unsigned const char *stop; + + n = addrbytesptr(src, &bp); + if (n == 0) + return -1; + + p = bp; + stop = bp + n; + + n = 0; + while (p < stop && *p == 0xff) { + p++; + n += 8; + } + if (p < stop && *p != 0) { /* boundary in mid-byte */ + b = *p++; + while (b&0x80) { + b <<= 1; + n++; + } + if ((b&0xff) != 0) + return -1; /* bits not contiguous */ + } + while (p < stop && *p == 0) + p++; + + if (p != stop) + return -1; + + return n; +} diff --git a/src/libfreeswan/ttoaddr.3 b/src/libfreeswan/ttoaddr.3 new file mode 100644 index 000000000..5bf48d4b2 --- /dev/null +++ b/src/libfreeswan/ttoaddr.3 @@ -0,0 +1,377 @@ +.TH IPSEC_TTOADDR 3 "28 Sept 2001" +.\" RCSID $Id: ttoaddr.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec ttoaddr, tnatoaddr, addrtot \- convert Internet addresses to and from text +.br +ipsec ttosubnet, subnettot \- convert subnet/mask text form to and from addresses +.SH SYNOPSIS +.B "#include +.sp +.B "const char *ttoaddr(const char *src, size_t srclen," +.ti +1c +.B "int af, ip_address *addr);" +.br +.B "const char *tnatoaddr(const char *src, size_t srclen," +.ti +1c +.B "int af, ip_address *addr);" +.br +.B "size_t addrtot(const ip_address *addr, int format," +.ti +1c +.B "char *dst, size_t dstlen);" +.sp +.B "const char *ttosubnet(const char *src, size_t srclen," +.ti +1c +.B "int af, ip_subnet *dst);" +.br +.B "size_t subnettot(const ip_subnet *sub, int format," +.ti +1c +.B "char *dst, size_t dstlen);" +.SH DESCRIPTION +.I Ttoaddr +converts a text-string name or numeric address into a binary address +(in network byte order). +.I Tnatoaddr +does the same conversion, +but the only text forms it accepts are +the ``official'' forms of +numeric address (dotted-decimal for IPv4, colon-hex for IPv6). +.I Addrtot +does the reverse conversion, from binary address back to a text form. +.I Ttosubnet +and +.I subnettot +do likewise for the ``address/mask'' form used to write a +specification of a subnet. +.PP +An IPv4 address is specified in text as a +dotted-decimal address (e.g. +.BR 1.2.3.4 ), +an eight-digit network-order hexadecimal number with the usual C prefix (e.g. +.BR 0x01020304 , +which is synonymous with +.BR 1.2.3.4 ), +an eight-digit host-order hexadecimal number with a +.B 0h +prefix (e.g. +.BR 0h01020304 , +which is synonymous with +.B 1.2.3.4 +on a big-endian host and +.B 4.3.2.1 +on a little-endian host), +a DNS name to be looked up via +.IR gethostbyname (3), +or an old-style network name to be looked up via +.IR getnetbyname (3). +.PP +A dotted-decimal address may be incomplete, in which case +text-to-binary conversion implicitly appends +as many instances of +.B .0 +as necessary to bring it up to four components. +The components of a dotted-decimal address are always taken as +decimal, and leading zeros are ignored. +For example, +.B 10 +is synonymous with +.BR 10.0.0.0 , +and +.B 128.009.000.032 +is synonymous with +.BR 128.9.0.32 +(the latter example is verbatim from RFC 1166). +The result of applying +.I addrtot +to an IPv4 address is always complete and does not contain leading zeros. +.PP +Use of hexadecimal addresses is +.B strongly +.BR discouraged ; +they are included only to save hassles when dealing with +the handful of perverted programs which already print +network addresses in hexadecimal. +.PP +An IPv6 address is specified in text with +colon-hex notation (e.g. +.BR 0:56:78ab:22:33:44:55:66 ), +colon-hex with +.B :: +abbreviating at most one subsequence of multiple zeros (e.g. +.BR 99:ab::54:068 , +which is synonymous with +.BR 99:ab:0:0:0:0:54:68 ), +or a DNS name to be looked up via +.IR gethostbyname (3). +The result of applying +.I addrtot +to an IPv6 address will use +.B :: +abbreviation if possible, +and will not contain leading zeros. +.PP +The letters in hexadecimal +may be uppercase or lowercase or any mixture thereof. +.PP +DNS names may be complete (optionally terminated with a ``.'') +or incomplete, and are looked up as specified by local system configuration +(see +.IR resolver (5)). +The +.I h_addr +value returned by +.IR gethostbyname2 (3) +is used, +so with current DNS implementations, +the result when the name corresponds to more than one address is +difficult to predict. +IPv4 name lookup resorts to +.IR getnetbyname (3) +only if +.IR gethostbyname2 (3) +fails. +.PP +A subnet specification is of the form \fInetwork\fB/\fImask\fR. +The +.I network +and +.I mask +can be any form acceptable to +.IR ttoaddr . +In addition, and preferably, the +.I mask +can be a decimal integer (leading zeros ignored) giving a bit count, +in which case +it stands for a mask with that number of high bits on and all others off +(e.g., +.B 24 +in IPv4 means +.BR 255.255.255.0 ). +In any case, the mask must be contiguous +(a sequence of high bits on and all remaining low bits off). +As a special case, the subnet specification +.B %default +is a synonym for +.B 0.0.0.0/0 +or +.B ::/0 +in IPv4 or IPv6 respectively. +.PP +.I Ttosubnet +ANDs the mask with the address before returning, +so that any non-network bits in the address are turned off +(e.g., +.B 10.1.2.3/24 +is synonymous with +.BR 10.1.2.0/24 ). +.I Subnettot +always generates the decimal-integer-bit-count +form of the mask, +with no leading zeros. +.PP +The +.I srclen +parameter of +.I ttoaddr +and +.I ttosubnet +specifies the length of the text string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I af +parameter of +.I ttoaddr +and +.I ttosubnet +specifies the address family of interest. +It should be either +.B AF_INET +or +.BR AF_INET6 . +.PP +The +.I dstlen +parameter of +.I addrtot +and +.I subnettot +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.I freeswan.h +header file defines constants, +.B ADDRTOT_BUF +and +.BR SUBNETTOT_BUF , +which are the sizes of buffers just large enough for worst-case results. +.PP +The +.I format +parameter of +.I addrtot +and +.I subnettot +specifies what format is to be used for the conversion. +The value +.B 0 +(not the character +.BR '0' , +but a zero value) +specifies a reasonable default, +and is in fact the only format currently available in +.IR subnettot . +.I Addrtot +also accepts format values +.B 'r' +(signifying a text form suitable for DNS reverse lookups, +e.g. +.B 4.3.2.1.IN-ADDR.ARPA. +for IPv4 and +RFC 2874 format for IPv6), +and +.B 'R' +(signifying an alternate reverse-lookup form, +an error for IPv4 and RFC 1886 format for IPv6). +Reverse-lookup names always end with a ``.''. +.PP +The text-to-binary functions return NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +The binary-to-text functions return +.B 0 +for a failure, and otherwise +always return the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.SH SEE ALSO +inet(3) +.SH DIAGNOSTICS +Fatal errors in +.I ttoaddr +are: +empty input; +unknown address family; +attempt to allocate temporary storage for a very long name failed; +name lookup failed; +syntax error in dotted-decimal or colon-hex form; +dotted-decimal or colon-hex component too large. +.PP +Fatal errors in +.I ttosubnet +are: +no +.B / +in +.IR src ; +.I ttoaddr +error in conversion of +.I network +or +.IR mask ; +bit-count mask too big; +mask non-contiguous. +.PP +Fatal errors in +.I addrtot +and +.I subnettot +are: +unknown format. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The interpretation of incomplete dotted-decimal addresses +(e.g. +.B 10/24 +means +.BR 10.0.0.0/24 ) +differs from that of some older conversion +functions, e.g. those of +.IR inet (3). +The behavior of the older functions has never been +particularly consistent or particularly useful. +.PP +Ignoring leading zeros in dotted-decimal components and bit counts +is arguably the most useful behavior in this application, +but it might occasionally cause confusion with the historical use of leading +zeros to denote octal numbers. +.PP +.I Ttoaddr +does not support the mixed colon-hex-dotted-decimal +convention used to embed an IPv4 address in an IPv6 address. +.PP +.I Addrtot +always uses the +.B :: +abbreviation (which can appear only once in an address) for the +.I first +sequence of multiple zeros in an IPv6 address. +One can construct addresses (unlikely ones) in which this is suboptimal. +.PP +.I Addrtot +.B 'r' +conversion of an IPv6 address uses lowercase hexadecimal, +not the uppercase used in RFC 2874's examples. +It takes careful reading of RFCs 2874, 2673, and 2234 to realize +that lowercase is technically legitimate here, +and there may be software which botches this +and hence would have trouble with lowercase hex. +.PP +Possibly +.I subnettot +ought to recognize the +.B %default +case and generate that string as its output. +Currently it doesn't. +.PP +It is barely possible that somebody, somewhere, +might have a legitimate use for non-contiguous subnet masks. +.PP +.IR Getnetbyname (3) +is a historical dreg. +.PP +.I Tnatoaddr +probably should enforce completeness of dotted-decimal addresses. +.PP +The restriction of text-to-binary error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The text-to-binary error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = ttoaddr( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/ttoaddr.c b/src/libfreeswan/ttoaddr.c new file mode 100644 index 000000000..efcb33e9f --- /dev/null +++ b/src/libfreeswan/ttoaddr.c @@ -0,0 +1,426 @@ +/* + * conversion from text forms of addresses to internal ones + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ttoaddr.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + * Legal ASCII characters in a domain name. Underscore technically is not, + * but is a common misunderstanding. Non-ASCII characters are simply + * exempted from checking at the moment, to allow for UTF-8 encoded stuff; + * the purpose of this check is merely to catch blatant errors. + */ +static const char namechars[] = "abcdefghijklmnopqrstuvwxyz0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_."; +#define ISASCII(c) (((c) & 0x80) == 0) + +static err_t tryname(const char *, size_t, int, int, ip_address *); +static err_t tryhex(const char *, size_t, int, ip_address *); +static err_t trydotted(const char *, size_t, ip_address *); +static err_t getbyte(const char **, const char *, int *); +static err_t colon(const char *, size_t, ip_address *); +static err_t getpiece(const char **, const char *, unsigned *); + +/* + - ttoaddr - convert text name or dotted-decimal address to binary address + */ +err_t /* NULL for success, else string literal */ +ttoaddr(src, srclen, af, dst) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +int af; /* address family */ +ip_address *dst; +{ + err_t oops; +# define HEXLEN 10 /* strlen("0x11223344") */ + int nultermd; + + if (srclen == 0) { + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + nultermd = 1; + } else + nultermd = 0; /* at least, not *known* to be terminated */ + + switch (af) { + case AF_INET: + case AF_INET6: + case 0: /* guess */ + break; + + default: + return "invalid address family"; + } + + if (af == AF_INET && srclen == HEXLEN && *src == '0') { + if (*(src+1) == 'x' || *(src+1) == 'X') + return tryhex(src+2, srclen-2, 'x', dst); + if (*(src+1) == 'h' || *(src+1) == 'H') + return tryhex(src+2, srclen-2, 'h', dst); + } + + if (memchr(src, ':', srclen) != NULL) { + if(af == 0) + { + af = AF_INET6; + } + + if (af != AF_INET6) + return "non-ipv6 address may not contain `:'"; + return colon(src, srclen, dst); + } + + if (af == 0 || af == AF_INET) { + oops = trydotted(src, srclen, dst); + if (oops == NULL) + return NULL; /* it worked */ + if (*oops != '?') + return oops; /* probably meant as d-d */ + } + + return tryname(src, srclen, nultermd, af, dst); +} + +/* + - tnatoaddr - convert text numeric address (only) to binary address + */ +err_t /* NULL for success, else string literal */ +tnatoaddr(src, srclen, af, dst) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +int af; /* address family */ +ip_address *dst; +{ + err_t oops; + + if (srclen == 0) { + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + } + + switch (af) { + case 0: /* guess */ + oops = colon(src, srclen, dst); + if(oops == NULL) + { + return NULL; + } + oops = trydotted(src, srclen, dst); + if(oops == NULL) + { + return NULL; + } + return "does not appear to be either IPv4 or IPv6 numeric address"; + break; + + case AF_INET6: + return colon(src, srclen, dst); + break; + case AF_INET: + oops = trydotted(src, srclen, dst); + if (oops == NULL) + return NULL; /* it worked */ + if (*oops != '?') + return oops; /* probably meant as d-d */ + return "does not appear to be numeric address"; + break; + default: + return "unknown address family in tnatoaddr"; + break; + } +} + +/* + - tryname - try it as a name + * Slightly complicated by lack of reliable NUL termination in source. + */ +static err_t +tryname(src, srclen, nultermd, af, dst) +const char *src; +size_t srclen; +int nultermd; /* is it known to be NUL-terminated? */ +int af; +ip_address *dst; +{ + struct hostent *h; + struct netent *ne = NULL; + char namebuf[100]; /* enough for most DNS names */ + const char *cp; + char *p = namebuf; + size_t n; + + for (cp = src, n = srclen; n > 0; cp++, n--) + if (ISASCII(*cp) && strchr(namechars, *cp) == NULL) + return "illegal (non-DNS-name) character in name"; + + if (nultermd) + cp = src; + else { + if (srclen+1 > sizeof(namebuf)) { + p = (char *) MALLOC(srclen+1); + if (p == NULL) + return "unable to get temporary space for name"; + } + p[0] = '\0'; /* strncpy semantics are wrong */ + strncat(p, src, srclen); + cp = (const char *)p; + } + + h = gethostbyname2(cp, af); + if (h == NULL && af == AF_INET) + ne = getnetbyname(cp); + if (p != namebuf) + FREE(p); + if (h == NULL && ne == NULL) + return "does not look numeric and name lookup failed"; + + if (h != NULL) { + if (h->h_addrtype != af) + return "address-type mismatch from gethostbyname2!!!"; + return initaddr((unsigned char *)h->h_addr, h->h_length, af, dst); + } else { + if (ne->n_addrtype != af) + return "address-type mismatch from getnetbyname!!!"; + ne->n_net = htonl(ne->n_net); + return initaddr((unsigned char *)&ne->n_net, sizeof(ne->n_net), + af, dst); + } +} + +/* + - tryhex - try conversion as an eight-digit hex number (AF_INET only) + */ +static err_t +tryhex(src, srclen, flavor, dst) +const char *src; +size_t srclen; /* should be 8 */ +int flavor; /* 'x' for network order, 'h' for host order */ +ip_address *dst; +{ + err_t oops; + unsigned long ul; + union { + uint32_t addr; + unsigned char buf[4]; + } u; + + if (srclen != 8) + return "internal error, tryhex called with bad length"; + + oops = ttoul(src, srclen, 16, &ul); + if (oops != NULL) + return oops; + + u.addr = (flavor == 'h') ? ul : htonl(ul); + return initaddr(u.buf, sizeof(u.buf), AF_INET, dst); +} + +/* + - trydotted - try conversion as dotted decimal (AF_INET only) + * + * If the first char of a complaint is '?', that means "didn't look like + * dotted decimal at all". + */ +static err_t +trydotted(src, srclen, dst) +const char *src; +size_t srclen; +ip_address *dst; +{ + const char *stop = src + srclen; /* just past end */ + int byte; + err_t oops; +# define NBYTES 4 + unsigned char buf[NBYTES]; + int i; + + memset(buf, 0, sizeof(buf)); + for (i = 0; i < NBYTES && src < stop; i++) { + oops = getbyte(&src, stop, &byte); + if (oops != NULL) { + if (*oops != '?') + return oops; /* bad number */ + if (i > 1) + return oops+1; /* failed number */ + return oops; /* with leading '?' */ + } + buf[i] = byte; + if (i < 3 && src < stop && *src++ != '.') { + if (i == 0) + return "?syntax error in dotted-decimal address"; + else + return "syntax error in dotted-decimal address"; + } + } + if (src != stop) + return "extra garbage on end of dotted-decimal address"; + + return initaddr(buf, sizeof(buf), AF_INET, dst); +} + +/* + - getbyte - try to scan a byte in dotted decimal + * A subtlety here is that all this arithmetic on ASCII digits really is + * highly portable -- ANSI C guarantees that digits 0-9 are contiguous. + * It's easier to just do it ourselves than set up for a call to ttoul(). + * + * If the first char of a complaint is '?', that means "didn't look like a + * number at all". + */ +err_t +getbyte(srcp, stop, retp) +const char **srcp; /* *srcp is updated */ +const char *stop; /* first untouchable char */ +int *retp; /* return-value pointer */ +{ + char c; + const char *p; + int no; + + if (*srcp >= stop) + return "?empty number in dotted-decimal address"; + + no = 0; + p = *srcp; + while (p < stop && no <= 255 && (c = *p) >= '0' && c <= '9') { + no = no*10 + (c - '0'); + p++; + } + if (p == *srcp) + return "?non-numeric component in dotted-decimal address"; + *srcp = p; + if (no > 255) + return "byte overflow in dotted-decimal address"; + *retp = no; + return NULL; +} + +/* + - colon - convert IPv6 "numeric" address + */ +static err_t +colon(src, srclen, dst) +const char *src; +size_t srclen; /* known to be >0 */ +ip_address *dst; +{ + const char *stop = src + srclen; /* just past end */ + unsigned piece; + int gapat; /* where was empty piece seen */ + err_t oops; +# define NPIECES 8 + unsigned char buf[NPIECES*2]; /* short may have wrong byte order */ + int i; + int j; +# define IT "IPv6 numeric address" + int naftergap; + + /* leading or trailing :: becomes single empty field */ + if (*src == ':') { /* legal only if leading :: */ + if (srclen == 1 || *(src+1) != ':') + return "illegal leading `:' in " IT; + if (srclen == 2) { + unspecaddr(AF_INET6, dst); + return NULL; + } + src++; /* past first but not second */ + srclen--; + } + if (*(stop-1) == ':') { /* legal only if trailing :: */ + if (srclen == 1 || *(stop-2) != ':') + return "illegal trailing `:' in " IT; + srclen--; /* leave one */ + } + + gapat = -1; + for (i = 0; i < NPIECES && src < stop; i++) { + oops = getpiece(&src, stop, &piece); + if (oops != NULL && *oops == ':') { /* empty field */ + if (gapat >= 0) + return "more than one :: in " IT; + gapat = i; + } else if (oops != NULL) + return oops; + buf[2*i] = piece >> 8; + buf[2*i + 1] = piece & 0xff; + if (i < NPIECES-1) { /* there should be more input */ + if (src == stop && gapat < 0) + return IT " ends prematurely"; + if (src != stop && *src++ != ':') + return "syntax error in " IT; + } + } + if (src != stop) + return "extra garbage on end of " IT; + + if (gapat < 0 && i < NPIECES) /* should have been caught earlier */ + return "incomplete " IT " (internal error)"; + if (gapat >= 0 && i == NPIECES) + return "non-abbreviating empty field in " IT; + if (gapat >= 0) { + naftergap = i - (gapat + 1); + for (i--, j = NPIECES-1; naftergap > 0; i--, j--, naftergap--) { + buf[2*j] = buf[2*i]; + buf[2*j + 1] = buf[2*i + 1]; + } + for (; j >= gapat; j--) + buf[2*j] = buf[2*j + 1] = 0; + } + + return initaddr(buf, sizeof(buf), AF_INET6, dst); +} + +/* + - getpiece - try to scan one 16-bit piece of an IPv6 address + */ +err_t /* ":" means "empty field seen" */ +getpiece(srcp, stop, retp) +const char **srcp; /* *srcp is updated */ +const char *stop; /* first untouchable char */ +unsigned *retp; /* return-value pointer */ +{ + const char *p; +# define NDIG 4 + int d; + unsigned long ret; + err_t oops; + + if (*srcp >= stop || **srcp == ':') { /* empty field */ + *retp = 0; + return ":"; + } + + p = *srcp; + d = 0; + while (p < stop && d < NDIG && isxdigit(*p)) { + p++; + d++; + } + if (d == 0) + return "non-hex field in IPv6 numeric address"; + if (p < stop && d == NDIG && isxdigit(*p)) + return "field in IPv6 numeric address longer than 4 hex digits"; + + oops = ttoul(*srcp, d, 16, &ret); + if (oops != NULL) /* shouldn't happen, really... */ + return oops; + + *srcp = p; + *retp = ret; + return NULL; +} diff --git a/src/libfreeswan/ttodata.3 b/src/libfreeswan/ttodata.3 new file mode 100644 index 000000000..98bbe4ab3 --- /dev/null +++ b/src/libfreeswan/ttodata.3 @@ -0,0 +1,281 @@ +.TH IPSEC_TTODATA 3 "16 August 2003" +.\" RCSID $Id: ttodata.3,v 1.2 2005/07/18 20:13:42 as Exp $ +.SH NAME +ipsec ttodata, datatot \- convert binary data bytes from and to text formats +.SH SYNOPSIS +.B "#include " +.sp +.B "const char *ttodata(const char *src, size_t srclen," +.ti +1c +.B "int base, char *dst, size_t dstlen, size_t *lenp);" +.br +.B "const char *ttodatav(const char *src, size_t srclen," +.ti +1c +.B "int base, char *dst, size_t dstlen, size_t *lenp," +.ti +1c +.B "char *errp, size_t errlen, int flags);" +.br +.B "size_t datatot(const char *src, size_t srclen," +.ti +1c +.B "int format, char *dst, size_t dstlen);" +.SH DESCRIPTION +.IR Ttodata , +.IR ttodatav , +and +.I datatot +convert arbitrary binary data (e.g. encryption or authentication keys) +from and to more-or-less human-readable text formats. +.PP +Currently supported formats are hexadecimal, base64, and characters. +.PP +A hexadecimal text value begins with a +.B 0x +(or +.BR 0X ) +prefix and continues with two-digit groups +of hexadecimal digits (0-9, and a-f or A-F), +each group encoding the value of one binary byte, high-order digit first. +A single +.B _ +(underscore) +between consecutive groups is ignored, permitting punctuation to improve +readability; doing this every eight digits seems about right. +.PP +A base64 text value begins with a +.B 0s +(or +.BR 0S ) +prefix +and continues with four-digit groups of base64 digits (A-Z, a-z, 0-9, +, and /), +each group encoding the value of three binary bytes as described in +section 6.8 of RFC 2045. +If +.B flags +has the +.B TTODATAV_IGNORESPACE +bit on, blanks are ignore (after the prefix). +Note that the last one or two digits of a base64 group can be +.B = +to indicate that fewer than three binary bytes are encoded. +.PP +A character text value begins with a +.B 0t +(or +.BR 0T ) +prefix +and continues with text characters, each being the value of one binary byte. +.PP +All these functions basically copy data from +.I src +(whose size is specified by +.IR srclen ) +to +.I dst +(whose size is specified by +.IR dstlen ), +doing the conversion en route. +If the result will not fit in +.IR dst , +it is truncated; +under no circumstances are more than +.I dstlen +bytes of result written to +.IR dst . +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result bytes are written at all. +.PP +The +.I base +parameter of +.I ttodata +and +.I ttodatav +specifies what format the input is in; +normally it should be +.B 0 +to signify that this gets figured out from the prefix. +Values of +.BR 16 , +.BR 64 , +and +.BR 256 +respectively signify hexadecimal, base64, and character-text formats +without prefixes. +.PP +The +.I format +parameter of +.IR datatot , +a single character used as a type code, +specifies which text format is wanted. +The value +.B 0 +(not ASCII +.BR '0' , +but a zero value) specifies a reasonable default. +Other currently-supported values are: +.RS 2 +.TP 4 +.B 'x' +continuous lower-case hexadecimal with a +.B 0x +prefix +.TP +.B 'h' +lower-case hexadecimal with a +.B 0x +prefix and a +.B _ +every eight digits +.TP +.B ':' +lower-case hexadecimal with no prefix and a +.B : +(colon) every two digits +.TP +.B 16 +lower-case hexadecimal with no prefix or +.B _ +.TP +.B 's' +continuous base64 with a +.B 0s +prefix +.TP +.B 64 +continuous base64 with no prefix +.RE +.PP +The default format is currently +.BR 'h' . +.PP +.I Ttodata +returns NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +On success, +if and only if +.I lenp +is non-NULL, +.B *lenp +is set to the number of bytes required to contain the full untruncated result. +It is the caller's responsibility to check this against +.I dstlen +to determine whether he has obtained a complete result. +The +.B *lenp +value is correct even if +.I dstlen +is zero, which offers a way to determine how much space would be needed +before having to allocate any. +.PP +.I Ttodatav +is just like +.I ttodata +except that in certain cases, +if +.I errp +is non-NULL, +the buffer pointed to by +.I errp +(whose length is given by +.IR errlen ) +is used to hold a more detailed error message. +The return value is NULL for success, +and is either +.I errp +or a pointer to a string literal for failure. +If the size of the error-message buffer is +inadequate for the desired message, +.I ttodatav +will fall back on returning a pointer to a literal string instead. +The +.I freeswan.h +header file defines a constant +.B TTODATAV_BUF +which is the size of a buffer large enough for worst-case results. +.PP +The normal return value of +.IR datatot +is the number of bytes required +to contain the full untruncated result. +It is the caller's responsibility to check this against +.I dstlen +to determine whether he has obtained a complete result. +The return value is correct even if +.I dstlen +is zero, which offers a way to determine how much space would be needed +before having to allocate any. +A return value of +.B 0 +signals a fatal error of some kind +(see DIAGNOSTICS). +.PP +A zero value for +.I srclen +in +.I ttodata +(but not +.IR datatot !) +is synonymous with +.BR strlen(src) . +A non-zero +.I srclen +in +.I ttodata +must not include the terminating NUL. +.PP +Unless +.I dstlen +is zero, +the result supplied by +.I datatot +is always NUL-terminated, +and its needed-size return value includes space for the terminating NUL. +.PP +Several obsolete variants of these functions +.RI ( atodata , +.IR datatoa , +.IR atobytes , +and +.IR bytestoa ) +are temporarily also supported. +.SH SEE ALSO +sprintf(3), ipsec_atoaddr(3) +.SH DIAGNOSTICS +Fatal errors in +.I ttodata +and +.I ttodatav +are: +unknown characters in the input; +unknown or missing prefix; +unknown base; +incomplete digit group; +non-zero padding in a base64 less-than-three-bytes digit group; +zero-length input. +.PP +Fatal errors in +.I datatot +are: +unknown format code; +zero-length input. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +.I Datatot +should have a format code to produce character-text output. +.PP +The +.B 0s +and +.B 0t +prefixes are the author's inventions and are not a standard +of any kind. +They have been chosen to avoid collisions with existing practice +(some C implementations use +.B 0b +for binary) +and possible confusion with unprefixed hexadecimal. diff --git a/src/libfreeswan/ttodata.c b/src/libfreeswan/ttodata.c new file mode 100644 index 000000000..e1bf7606a --- /dev/null +++ b/src/libfreeswan/ttodata.c @@ -0,0 +1,722 @@ +/* + * convert from text form of arbitrary data (e.g., keys) to binary + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ttodata.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* converters and misc */ +static int unhex(const char *, char *, size_t); +static int unb64(const char *, char *, size_t); +static int untext(const char *, char *, size_t); +static const char *badch(const char *, int, char *, size_t); + +/* internal error codes for converters */ +#define SHORT (-2) /* internal buffer too short */ +#define BADPAD (-3) /* bad base64 padding */ +#define BADCH0 (-4) /* invalid character 0 */ +#define BADCH1 (-5) /* invalid character 1 */ +#define BADCH2 (-6) /* invalid character 2 */ +#define BADCH3 (-7) /* invalid character 3 */ +#define BADOFF(code) (BADCH0-(code)) + +/* + - ttodatav - convert text to data, with verbose error reports + * If some of this looks slightly odd, it's because it has changed + * repeatedly (from the original atodata()) without a major rewrite. + */ +const char * /* NULL on success, else literal or errp */ +ttodatav(src, srclen, base, dst, dstlen, lenp, errp, errlen, flags) +const char *src; +size_t srclen; /* 0 means apply strlen() */ +int base; /* 0 means figure it out */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +size_t *lenp; /* where to record length (NULL is nowhere) */ +char *errp; /* error buffer */ +size_t errlen; +unsigned int flags; +{ + size_t ingroup; /* number of input bytes converted at once */ + char buf[4]; /* output from conversion */ + int nbytes; /* size of output */ + int (*decode)(const char *, char *, size_t); + char *stop; + int ndone; + int i; + int underscoreok; + int skipSpace = 0; + + if (srclen == 0) + srclen = strlen(src); + if (dstlen == 0) + dst = buf; /* point it somewhere valid */ + stop = dst + dstlen; + + if (base == 0) { + if (srclen < 2) + return "input too short to be valid"; + if (*src++ != '0') + return "input does not begin with format prefix"; + switch (*src++) { + case 'x': + case 'X': + base = 16; + break; + case 's': + case 'S': + base = 64; + break; + case 't': + case 'T': + base = 256; + break; + default: + return "unknown format prefix"; + } + srclen -= 2; + } + switch (base) { + case 16: + decode = unhex; + underscoreok = 1; + ingroup = 2; + break; + case 64: + decode = unb64; + underscoreok = 0; + ingroup = 4; + if(flags & TTODATAV_IGNORESPACE) { + skipSpace = 1; + } + break; + + case 256: + decode = untext; + ingroup = 1; + underscoreok = 0; + break; + default: + return "unknown base"; + } + + /* proceed */ + ndone = 0; + while (srclen > 0) { + char stage[4]; /* staging area for group */ + size_t sl = 0; + + /* Grab ingroup characters into stage, + * squeezing out blanks if we are supposed to ignore them. + */ + for (sl = 0; sl < ingroup; src++, srclen--) { + if (srclen == 0) + return "input ends in mid-byte, perhaps truncated"; + else if (!(skipSpace && (*src == ' ' || *src == '\t'))) + stage[sl++] = *src; + } + + nbytes = (*decode)(stage, buf, sizeof(buf)); + switch (nbytes) { + case BADCH0: + case BADCH1: + case BADCH2: + case BADCH3: + return badch(stage, nbytes, errp, errlen); + case SHORT: + return "internal buffer too short (\"can't happen\")"; + case BADPAD: + return "bad (non-zero) padding at end of base64 input"; + } + if (nbytes <= 0) + return "unknown internal error"; + for (i = 0; i < nbytes; i++) { + if (dst < stop) + *dst++ = buf[i]; + ndone++; + } + while (srclen >= 1 && skipSpace && (*src == ' ' || *src == '\t')){ + src++; + srclen--; + } + if (underscoreok && srclen > 1 && *src == '_') { + /* srclen > 1 means not last character */ + src++; + srclen--; + } + } + + if (ndone == 0) + return "no data bytes specified by input"; + if (lenp != NULL) + *lenp = ndone; + return NULL; +} + +/* + - ttodata - convert text to data + */ +const char * /* NULL on success, else literal */ +ttodata(src, srclen, base, dst, dstlen, lenp) +const char *src; +size_t srclen; /* 0 means apply strlen() */ +int base; /* 0 means figure it out */ +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +size_t *lenp; /* where to record length (NULL is nowhere) */ +{ + return ttodatav(src, srclen, base, dst, dstlen, lenp, (char *)NULL, + (size_t)0, TTODATAV_SPACECOUNTS); +} + +/* + - atodata - convert ASCII to data + * backward-compatibility interface + */ +size_t /* 0 for failure, true length for success */ +atodata(src, srclen, dst, dstlen) +const char *src; +size_t srclen; +char *dst; +size_t dstlen; +{ + size_t len; + const char *err; + + err = ttodata(src, srclen, 0, dst, dstlen, &len); + if (err != NULL) + return 0; + return len; +} + +/* + - atobytes - convert ASCII to data bytes + * another backward-compatibility interface + */ +const char * +atobytes(src, srclen, dst, dstlen, lenp) +const char *src; +size_t srclen; +char *dst; +size_t dstlen; +size_t *lenp; +{ + return ttodata(src, srclen, 0, dst, dstlen, lenp); +} + +/* + - unhex - convert two ASCII hex digits to byte + */ +static int /* number of result bytes, or error code */ +unhex(src, dst, dstlen) +const char *src; /* known to be full length */ +char *dst; +size_t dstlen; /* not large enough is a failure */ +{ + char *p; + unsigned byte; + static char hex[] = "0123456789abcdef"; + + if (dstlen < 1) + return SHORT; + + p = strchr(hex, *src); + if (p == NULL) + p = strchr(hex, tolower(*src)); + if (p == NULL) + return BADCH0; + byte = (p - hex) << 4; + src++; + + p = strchr(hex, *src); + if (p == NULL) + p = strchr(hex, tolower(*src)); + if (p == NULL) + return BADCH1; + byte |= (p - hex); + + *dst = byte; + return 1; +} + +/* + - unb64 - convert four ASCII base64 digits to three bytes + * Note that a base64 digit group is padded out with '=' if it represents + * less than three bytes: one byte is dd==, two is ddd=, three is dddd. + */ +static int /* number of result bytes, or error code */ +unb64(src, dst, dstlen) +const char *src; /* known to be full length */ +char *dst; +size_t dstlen; +{ + char *p; + unsigned byte1; + unsigned byte2; + static char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + if (dstlen < 3) + return SHORT; + + p = strchr(base64, *src++); + + if (p == NULL) + return BADCH0; + byte1 = (p - base64) << 2; /* first six bits */ + + p = strchr(base64, *src++); + if (p == NULL) { + return BADCH1; + } + + byte2 = p - base64; /* next six: two plus four */ + *dst++ = byte1 | (byte2 >> 4); + byte1 = (byte2 & 0xf) << 4; + + p = strchr(base64, *src++); + if (p == NULL) { + if (*(src-1) == '=' && *src == '=') { + if (byte1 != 0) /* bad padding */ + return BADPAD; + return 1; + } + return BADCH2; + } + + byte2 = p - base64; /* next six: four plus two */ + *dst++ = byte1 | (byte2 >> 2); + byte1 = (byte2 & 0x3) << 6; + + p = strchr(base64, *src++); + if (p == NULL) { + if (*(src-1) == '=') { + if (byte1 != 0) /* bad padding */ + return BADPAD; + return 2; + } + return BADCH3; + } + byte2 = p - base64; /* last six */ + *dst++ = byte1 | byte2; + + return 3; +} + +/* + - untext - convert one ASCII character to byte + */ +static int /* number of result bytes, or error code */ +untext(src, dst, dstlen) +const char *src; /* known to be full length */ +char *dst; +size_t dstlen; /* not large enough is a failure */ +{ + if (dstlen < 1) + return SHORT; + + *dst = *src; + return 1; +} + +/* + - badch - produce a nice complaint about an unknown character + * + * If the compiler complains that the array bigenough[] has a negative + * size, that means the TTODATAV_BUF constant has been set too small. + */ +static const char * /* literal or errp */ +badch(src, errcode, errp, errlen) +const char *src; +int errcode; +char *errp; /* might be NULL */ +size_t errlen; +{ + static const char pre[] = "unknown character (`"; + static const char suf[] = "') in input"; + char buf[5]; +# define REQD (sizeof(pre) - 1 + sizeof(buf) - 1 + sizeof(suf)) + struct sizecheck { + char bigenough[TTODATAV_BUF - REQD]; /* see above */ + }; + char ch; + + if (errp == NULL || errlen < REQD) + return "unknown character in input"; + strcpy(errp, pre); + ch = *(src + BADOFF(errcode)); + if (isprint(ch)) { + buf[0] = ch; + buf[1] = '\0'; + } else { + buf[0] = '\\'; + buf[1] = ((ch & 0700) >> 6) + '0'; + buf[2] = ((ch & 0070) >> 3) + '0'; + buf[3] = ((ch & 0007) >> 0) + '0'; + buf[4] = '\0'; + } + strcat(errp, buf); + strcat(errp, suf); + return (const char *)errp; +} + + + +#ifdef TTODATA_MAIN + +#include + +struct artab; +static void check(struct artab *r, char *buf, size_t n, err_t oops, int *status); +static void regress(char *pgm); +static void hexout(const char *s, size_t len, FILE *f); + +/* + - main - convert first argument to hex, or run regression + */ +int +main(int argc, char *argv[]) +{ + char buf[1024]; + char buf2[1024]; + char err[512]; + size_t n; + size_t i; + char *p = buf; + char *p2 = buf2; + char *pgm = argv[0]; + const char *oops; + + if (argc < 2) { + fprintf(stderr, "Usage: %s {0x|0s|-r}\n", pgm); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(pgm); /* should not return */ + fprintf(stderr, "%s: regress() returned?!?\n", pgm); + exit(1); + } + + oops = ttodatav(argv[1], 0, 0, buf, sizeof(buf), &n, + err, sizeof(err), TTODATAV_IGNORESPACE); + if (oops != NULL) { + fprintf(stderr, "%s: ttodata error `%s' in `%s'\n", pgm, + oops, argv[1]); + exit(1); + } + + if (n > sizeof(buf)) { + p = (char *)malloc((size_t)n); + if (p == NULL) { + fprintf(stderr, + "%s: unable to malloc %d bytes for result\n", + pgm, n); + exit(1); + } + oops = ttodata(argv[1], 0, 0, p, n, &n); + if (oops != NULL) { + fprintf(stderr, "%s: error `%s' in ttodata retry?!?\n", + pgm, oops); + exit(1); + } + } + + hexout(p, n, stdout); + printf("\n"); + + i = datatot(buf, n, 'h', buf2, sizeof(buf2)); + if (i == 0) { + fprintf(stderr, "%s: datatot reports error in `%s'\n", pgm, + argv[1]); + exit(1); + } + + if (i > sizeof(buf2)) { + p2 = (char *)malloc((size_t)i); + if (p == NULL) { + fprintf(stderr, + "%s: unable to malloc %d bytes for result\n", + pgm, i); + exit(1); + } + i = datatot(buf, n, 'h', p2, i); + if (i == 0) { + fprintf(stderr, "%s: error in datatoa retry?!?\n", pgm); + exit(1); + } + } + + printf("%s\n", p2); + + exit(0); +} + +/* + - hexout - output an arbitrary-length string in hex + */ +static void +hexout(s, len, f) +const char *s; +size_t len; +FILE *f; +{ + size_t i; + + fprintf(f, "0x"); + for (i = 0; i < len; i++) + fprintf(f, "%02x", (unsigned char)s[i]); +} + +struct artab { + int base; +# define IGNORESPACE_BIAS 1000 + char *ascii; /* NULL for end */ + char *data; /* NULL for error expected */ +} atodatatab[] = { + { 0, "", NULL, }, + { 0, "0", NULL, }, + { 0, "0x", NULL, }, + { 0, "0xa", NULL, }, + { 0, "0xab", "\xab", }, + { 0, "0xabc", NULL, }, + { 0, "0xabcd", "\xab\xcd", }, + { 0, "0x0123456789", "\x01\x23\x45\x67\x89", }, + { 0, "0x01x", NULL, }, + { 0, "0xabcdef", "\xab\xcd\xef", }, + { 0, "0xABCDEF", "\xab\xcd\xef", }, + { 0, "0XaBc0eEd81f", "\xab\xc0\xee\xd8\x1f", }, + { 0, "0XaBc0_eEd8", "\xab\xc0\xee\xd8", }, + { 0, "0XaBc0_", NULL, }, + { 0, "0X_aBc0", NULL, }, + { 0, "0Xa_Bc0", NULL, }, + { 16, "aBc0eEd8", "\xab\xc0\xee\xd8", }, + { 0, "0s", NULL, }, + { 0, "0sA", NULL, }, + { 0, "0sBA", NULL, }, + { 0, "0sCBA", NULL, }, + { 0, "0sDCBA", "\x0c\x20\x40", }, + { 0, "0SDCBA", "\x0c\x20\x40", }, + { 0, "0sDA==", "\x0c", }, + { 0, "0sDC==", NULL, }, + { 0, "0sDCA=", "\x0c\x20", }, + { 0, "0sDCB=", NULL, }, + { 0, "0sDCAZ", "\x0c\x20\x19", }, + { 0, "0sDCAa", "\x0c\x20\x1a", }, + { 0, "0sDCAz", "\x0c\x20\x33", }, + { 0, "0sDCA0", "\x0c\x20\x34", }, + { 0, "0sDCA9", "\x0c\x20\x3d", }, + { 0, "0sDCA+", "\x0c\x20\x3e", }, + { 0, "0sDCA/", "\x0c\x20\x3f", }, + { 0, "0sAbraCadabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0s AbraCadabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sA braCadabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAb raCadabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbr aCadabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbra Cadabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraC adabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCa dabra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCad abra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCada bra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCadab ra+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCadabr a+", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCadabra +", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { IGNORESPACE_BIAS + 0, "0sAbraCadabra+ ", "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", }, + { 0, "0t", NULL, }, + { 0, "0tabc_xyz", "abc_xyz", }, + { 256, "abc_xyz", "abc_xyz", }, + { 0, NULL, NULL, }, +}; + +struct drtab { + char *data; /* input; NULL for end */ + char format; + int buflen; /* -1 means big buffer */ + int outlen; /* -1 means strlen(ascii)+1 */ + char *ascii; /* NULL for error expected */ +} datatoatab[] = { + { "", 'x', -1, -1, NULL, }, + { "", 'X', -1, -1, NULL, }, + { "", 'n', -1, -1, NULL, }, + { "0", 'x', -1, -1, "0x30", }, + { "0", 'x', 0, 5, "---", }, + { "0", 'x', 1, 5, "", }, + { "0", 'x', 2, 5, "0", }, + { "0", 'x', 3, 5, "0x", }, + { "0", 'x', 4, 5, "0x3", }, + { "0", 'x', 5, 5, "0x30", }, + { "0", 'x', 6, 5, "0x30", }, + { "\xab\xcd", 'x', -1, -1, "0xabcd", }, + { "\x01\x23\x45\x67\x89", 'x', -1, -1, "0x0123456789", }, + { "\xab\xcd\xef", 'x', -1, -1, "0xabcdef", }, + { "\xab\xc0\xee\xd8\x1f", 'x', -1, -1, "0xabc0eed81f", }, + { "\x01\x02", 'h', -1, -1, "0x0102", }, + { "\x01\x02\x03\x04\x05\x06", 'h', -1, -1, "0x01020304_0506", }, + { "\xab\xc0\xee\xd8\x1f", 16, -1, -1, "abc0eed81f", }, + { "\x0c\x20\x40", 's', -1, -1, "0sDCBA", }, + { "\x0c\x20\x40", 's', 0, 7, "---", }, + { "\x0c\x20\x40", 's', 1, 7, "", }, + { "\x0c\x20\x40", 's', 2, 7, "0", }, + { "\x0c\x20\x40", 's', 3, 7, "0s", }, + { "\x0c\x20\x40", 's', 4, 7, "0sD", }, + { "\x0c\x20\x40", 's', 5, 7, "0sDC", }, + { "\x0c\x20\x40", 's', 6, 7, "0sDCB", }, + { "\x0c\x20\x40", 's', 7, 7, "0sDCBA", }, + { "\x0c\x20\x40", 's', 8, 7, "0sDCBA", }, + { "\x0c", 's', -1, -1, "0sDA==", }, + { "\x0c\x20", 's', -1, -1, "0sDCA=", }, + { "\x0c\x20\x19", 's', -1, -1, "0sDCAZ", }, + { "\x0c\x20\x1a", 's', -1, -1, "0sDCAa", }, + { "\x0c\x20\x33", 's', -1, -1, "0sDCAz", }, + { "\x0c\x20\x34", 's', -1, -1, "0sDCA0", }, + { "\x0c\x20\x3d", 's', -1, -1, "0sDCA9", }, + { "\x0c\x20\x3e", 's', -1, -1, "0sDCA+", }, + { "\x0c\x20\x3f", 's', -1, -1, "0sDCA/", }, + { "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", 's', -1, -1, "0sAbraCadabra+", }, + { "\x01\xba\xda\x09\xa7\x5a\x6e\xb6\xbe", 64, -1, -1, "AbraCadabra+", }, + { NULL, 'x', -1, -1, NULL, }, +}; + +/* + - regress - regression-test ttodata() and datatot() + */ +static void +check(r, buf, n, oops, status) +struct artab *r; +char *buf; +size_t n; +err_t oops; +int *status; +{ + if (oops != NULL && r->data == NULL) + {} /* error expected */ + else if (oops != NULL) { + printf("`%s' gave error `%s', expecting %d `", r->ascii, + oops, strlen(r->data)); + hexout(r->data, strlen(r->data), stdout); + printf("'\n"); + *status = 1; + } else if (r->data == NULL) { + printf("`%s' gave %d `", r->ascii, n); + hexout(buf, n, stdout); + printf("', expecting error\n"); + *status = 1; + } else if (n != strlen(r->data)) { + printf("length wrong in `%s': got %d `", r->ascii, n); + hexout(buf, n, stdout); + printf("', expecting %d `", strlen(r->data)); + hexout(r->data, strlen(r->data), stdout); + printf("'\n"); + *status = 1; + } else if (memcmp(buf, r->data, n) != 0) { + printf("`%s' gave %d `", r->ascii, n); + hexout(buf, n, stdout); + printf("', expecting %d `", strlen(r->data)); + hexout(r->data, strlen(r->data), stdout); + printf("'\n"); + *status = 1; + } + fflush(stdout); +} + +static void /* should not return at all, in fact */ +regress(pgm) +char *pgm; +{ + struct artab *r; + struct drtab *dr; + char buf[100]; + size_t n; + int status = 0; + + for (r = atodatatab; r->ascii != NULL; r++) { + int base = r->base; + int xbase = 0; + + if ((base == 0 || base == IGNORESPACE_BIAS + 0) && r->ascii[0] == '0') { + switch (r->ascii[1]) { + case 'x': + case 'X': + xbase = 16; + break; + case 's': + case 'S': + xbase = 64; + break; + case 't': + case 'T': + xbase = 256; + break; + } + } + + if (base >= IGNORESPACE_BIAS) { + base = base - IGNORESPACE_BIAS; + check(r, buf, n, ttodatav(r->ascii, 0, base, buf, sizeof(buf), &n, NULL, 0, TTODATAV_IGNORESPACE), &status); + if (xbase != 0) + check(r, buf, n, ttodatav(r->ascii+2, 0, xbase, buf, sizeof(buf), &n, NULL, 0, TTODATAV_IGNORESPACE), &status); + } else { + check(r, buf, n, ttodata(r->ascii, 0, base, buf, sizeof(buf), &n), &status); + if (base == 64 || xbase == 64) + check(r, buf, n, ttodatav(r->ascii, 0, base, buf, sizeof(buf), &n, NULL, 0, TTODATAV_IGNORESPACE), &status); + if (xbase != 0) { + check(r, buf, n, ttodata(r->ascii+2, 0, xbase, buf, sizeof(buf), &n), &status); + if (base == 64 || xbase == 64) + check(r, buf, n, ttodatav(r->ascii+2, 0, xbase, buf, sizeof(buf), &n, NULL, 0, TTODATAV_IGNORESPACE), &status); + } + } + } + for (dr = datatoatab; dr->data != NULL; dr++) { + size_t should; + + strcpy(buf, "---"); + n = datatot(dr->data, strlen(dr->data), dr->format, buf, + (dr->buflen == -1) ? sizeof(buf) : dr->buflen); + should = (dr->ascii == NULL) ? 0 : strlen(dr->ascii) + 1; + if (dr->outlen != -1) + should = dr->outlen; + if (n == 0 && dr->ascii == NULL) + {} /* error expected */ + else if (n == 0) { + printf("`"); + hexout(dr->data, strlen(dr->data), stdout); + printf("' %c gave error, expecting %d `%s'\n", + dr->format, should, dr->ascii); + status = 1; + } else if (dr->ascii == NULL) { + printf("`"); + hexout(dr->data, strlen(dr->data), stdout); + printf("' %c gave %d `%.*s', expecting error\n", + dr->format, n, (int)n, buf); + status = 1; + } else if (n != should) { + printf("length wrong in `"); + hexout(dr->data, strlen(dr->data), stdout); + printf("': got %d `%s'", n, buf); + printf(", expecting %d `%s'\n", should, dr->ascii); + status = 1; + } else if (strcmp(buf, dr->ascii) != 0) { + printf("`"); + hexout(dr->data, strlen(dr->data), stdout); + printf("' gave %d `%s'", n, buf); + printf(", expecting %d `%s'\n", should, dr->ascii); + status = 1; + } + fflush(stdout); + } + exit(status); +} + +#endif /* TTODATA_MAIN */ diff --git a/src/libfreeswan/ttoprotoport.c b/src/libfreeswan/ttoprotoport.c new file mode 100644 index 000000000..46321838c --- /dev/null +++ b/src/libfreeswan/ttoprotoport.c @@ -0,0 +1,103 @@ +/* + * conversion from protocol/port string to protocol and port + * Copyright (C) 2002 Mario Strasser , + * Zuercher Hochschule Winterthur, + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ttoprotoport.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ + +#include "internal.h" +#include "freeswan.h" + +/* + * ttoprotoport - converts from protocol/port string to protocol and port + */ +err_t +ttoprotoport(src, src_len, proto, port, has_port_wildcard) +char *src; /* input string */ +size_t src_len; /* length of input string, use strlen() if 0 */ +u_int8_t *proto; /* extracted protocol number */ +u_int16_t *port; /* extracted port number if it exists */ +int *has_port_wildcard; /* set if port is %any */ +{ + char *end, *service_name; + char proto_name[16]; + int proto_len; + long int l; + struct protoent *protocol; + struct servent *service; + + /* get the length of the string */ + if (!src_len) src_len = strlen(src); + + /* locate delimiter '/' between protocol and port */ + end = strchr(src, '/'); + if (end != NULL) { + proto_len = end - src; + service_name = end + 1; + } else { + proto_len = src_len; + service_name = src + src_len; + } + + /* copy protocol name*/ + memset(proto_name, '\0', sizeof(proto_name)); + memcpy(proto_name, src, proto_len); + + /* extract protocol by trying to resolve it by name */ + protocol = getprotobyname(proto_name); + if (protocol != NULL) { + *proto = protocol->p_proto; + } + else /* failed, now try it by number */ + { + l = strtol(proto_name, &end, 0); + + if (*proto_name && *end) + return " is neither a number nor a valid name"; + + if (l < 0 || l > 0xff) + return " must be between 0 and 255"; + + *proto = (u_int8_t)l; + } + + /* is there a port wildcard? */ + *has_port_wildcard = (strcmp(service_name, "%any") == 0); + + if (*has_port_wildcard) + { + *port = 0; + return NULL; + } + + /* extract port by trying to resolve it by name */ + service = getservbyname(service_name, NULL); + if (service != NULL) { + *port = ntohs(service->s_port); + } + else /* failed, now try it by number */ + { + l = strtol(service_name, &end, 0); + + if (*service_name && *end) + return " is neither a number nor a valid name"; + + if (l < 0 || l > 0xffff) + return " must be between 0 and 65535"; + + *port = (u_int16_t)l; + } + return NULL; +} + diff --git a/src/libfreeswan/ttosa.3 b/src/libfreeswan/ttosa.3 new file mode 100644 index 000000000..bf918e108 --- /dev/null +++ b/src/libfreeswan/ttosa.3 @@ -0,0 +1,288 @@ +.TH IPSEC_TTOSA 3 "26 Nov 2001" +.\" RCSID $Id: ttosa.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec ttosa, satot \- convert IPsec Security Association IDs to and from text +.br +ipsec initsaid \- initialize an SA ID +.SH SYNOPSIS +.B "#include +.sp +.B "typedef struct {" +.ti +1c +.B "ip_address dst;" +.ti +1c +.B "ipsec_spi_t spi;" +.ti +1c +.B "int proto;" +.br +.B "} ip_said;" +.sp +.B "const char *ttosa(const char *src, size_t srclen," +.ti +1c +.B "ip_said *sa); +.br +.B "size_t satot(const ip_said *sa, int format," +.ti +1c +.B "char *dst, size_t dstlen);" +.br +.B "void initsaid(const ip_address *addr, ipsec_spi_t spi," +.ti +1c +.B "int proto, ip_said *dst);" +.SH DESCRIPTION +.I Ttosa +converts an ASCII Security Association (SA) specifier into an +.B ip_said +structure (containing +a destination-host address +in network byte order, +an SPI number in network byte order, and +a protocol code). +.I Satot +does the reverse conversion, back to a text SA specifier. +.I Initsaid +initializes an +.B ip_said +from separate items of information. +.PP +An SA is specified in text with a mail-like syntax, e.g. +.BR esp.5a7@1.2.3.4 . +An SA specifier contains +a protocol prefix (currently +.BR ah , +.BR esp , +.BR tun , +.BR comp , +or +.BR int ), +a single character indicating the address family +.RB ( . +for IPv4, +.B : +for IPv6), +an unsigned integer SPI number in hexadecimal (with no +.B 0x +prefix), +and an IP address. +The IP address can be any form accepted by +.IR ipsec_ttoaddr (3), +e.g. dotted-decimal IPv4 address, +colon-hex IPv6 address, +or DNS name. +.PP +As a special case, the SA specifier +.B %passthrough4 +or +.B %passthrough6 +signifies the special SA used to indicate that packets should be +passed through unaltered. +(At present, these are synonyms for +.B tun.0@0.0.0.0 +and +.B tun:0@:: +respectively, +but that is subject to change without notice.) +.B %passthrough +is a historical synonym for +.BR %passthrough4 . +These forms are known to both +.I ttosa +and +.IR satot , +so the internal representation is never visible. +.PP +Similarly, the SA specifiers +.BR %pass , +.BR %drop , +.BR %reject , +.BR %hold , +.BR %trap , +and +.BR %trapsubnet +signify special ``magic'' SAs used to indicate that packets should be +passed, dropped, rejected (dropped with ICMP notification), +held, +and trapped (sent up to +.IR ipsec_pluto (8), +with either of two forms of +.B %hold +automatically installed) +respectively. +These forms too are known to both routines, +so the internal representation of the magic SAs should never be visible. +.PP +The +.B +header file supplies the +.B ip_said +structure, as well as a data type +.B ipsec_spi_t +which is an unsigned 32-bit integer. +(There is no consistency between kernel and user on what such a type +is called, hence the header hides the differences.) +.PP +The protocol code uses the same numbers that IP does. +For user convenience, given the difficulty in acquiring the exact set of +protocol names used by the kernel, +.B +defines the names +.BR SA_ESP , +.BR SA_AH , +.BR SA_IPIP , +and +.BR SA_COMP +to have the same values as the kernel names +.BR IPPROTO_ESP , +.BR IPPROTO_AH , +.BR IPPROTO_IPIP , +and +.BR IPPROTO_COMP . +.PP +.B +also defines +.BR SA_INT +to have the value +.BR 61 +(reserved by IANA for ``any host internal protocol'') +and +.BR SPI_PASS , +.BR SPI_DROP , +.BR SPI_REJECT , +.BR SPI_HOLD , +and +.B SPI_TRAP +to have the values 256-260 (in \fIhost\fR byte order) respectively. +These are used in constructing the magic SAs +(which always have address +.BR 0.0.0.0 ). +.PP +If +.I satot +encounters an unknown protocol code, e.g. 77, +it yields output using a prefix +showing the code numerically, e.g. ``unk77''. +This form is +.I not +recognized by +.IR ttosa . +.PP +The +.I srclen +parameter of +.I ttosa +specifies the length of the string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I dstlen +parameter of +.I satot +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.B +header file defines a constant, +.BR SATOT_BUF , +which is the size of a buffer just large enough for worst-case results. +.PP +The +.I format +parameter of +.I satot +specifies what format is to be used for the conversion. +The value +.B 0 +(not the ASCII character +.BR '0' , +but a zero value) +specifies a reasonable default +(currently +lowercase protocol prefix, lowercase hexadecimal SPI, +dotted-decimal or colon-hex address). +The value +.B 'f' +is similar except that the SPI is padded with +.BR 0 s +to a fixed 32-bit width, to ease aligning displayed tables. +.PP +.I Ttosa +returns +.B NULL +for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.I Satot +returns +.B 0 +for a failure, and otherwise +always returns the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL; +it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred. +.PP +There is also, temporarily, support for some obsolete +forms of SA specifier which lack the address-family indicator. +.SH SEE ALSO +ipsec_ttoul(3), ipsec_ttoaddr(3), ipsec_samesaid(3), inet(3) +.SH DIAGNOSTICS +Fatal errors in +.I ttosa +are: +empty input; +input too small to be a legal SA specifier; +no +.B @ +in input; +unknown protocol prefix; +conversion error in +.I ttoul +or +.IR ttoaddr . +.PP +Fatal errors in +.I satot +are: +unknown format. +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +The restriction of text-to-binary error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The text-to-binary error-reporting convention lends itself +to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = ttosa( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/ttosa.c b/src/libfreeswan/ttosa.c new file mode 100644 index 000000000..aa2283694 --- /dev/null +++ b/src/libfreeswan/ttosa.c @@ -0,0 +1,280 @@ +/* + * convert from text form of SA ID to binary + * Copyright (C) 2000, 2001 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ttosa.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +static struct satype { + char *prefix; + size_t prelen; /* strlen(prefix) */ + int proto; +} satypes[] = { + { "ah", 2, SA_AH }, + { "esp", 3, SA_ESP }, + { "tun", 3, SA_IPIP }, + { "comp", 4, SA_COMP }, + { "int", 3, SA_INT }, + { NULL, 0, 0, } +}; + +static struct magic { + char *name; + char *really; +} magic[] = { + { PASSTHROUGHNAME, PASSTHROUGH4IS }, + { PASSTHROUGH4NAME, PASSTHROUGH4IS }, + { PASSTHROUGH6NAME, PASSTHROUGH6IS }, + { "%pass", "int256@0.0.0.0" }, + { "%drop", "int257@0.0.0.0" }, + { "%reject", "int258@0.0.0.0" }, + { "%hold", "int259@0.0.0.0" }, + { "%trap", "int260@0.0.0.0" }, + { "%trapsubnet", "int261@0.0.0.0" }, + { NULL, NULL } +}; + +/* + - ttosa - convert text "ah507@10.0.0.1" to SA identifier + */ +err_t /* NULL for success, else string literal */ +ttosa(src, srclen, sa) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +ip_said *sa; +{ + const char *at; + const char *addr; + size_t alen; + const char *spi = NULL; + struct satype *sat; + unsigned long ul; + const char *oops; + struct magic *mp; + size_t nlen; +# define MINLEN 5 /* ah0@0 is as short as it can get */ + int af; + int base; + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + if (srclen < MINLEN) + return "string too short to be SA identifier"; + if (*src == '%') { + for (mp = magic; mp->name != NULL; mp++) { + nlen = strlen(mp->name); + if (srclen == nlen && memcmp(src, mp->name, nlen) == 0) + break; + } + if (mp->name == NULL) + return "unknown % keyword"; + src = mp->really; + srclen = strlen(src); + } + + at = memchr(src, '@', srclen); + if (at == NULL) + return "no @ in SA specifier"; + + for (sat = satypes; sat->prefix != NULL; sat++) + if (sat->prelen < srclen && + strncmp(src, sat->prefix, sat->prelen) == 0) { + sa->proto = sat->proto; + spi = src + sat->prelen; + break; /* NOTE BREAK OUT */ + } + if (sat->prefix == NULL) + return "SA specifier lacks valid protocol prefix"; + + if (spi >= at) + return "no SPI in SA specifier"; + switch (*spi) { + case '.': + af = AF_INET; + spi++; + base = 16; + break; + case ':': + af = AF_INET6; + spi++; + base = 16; + break; + default: + af = AF_UNSPEC; /* not known yet */ + base = 0; + break; + } + if (spi >= at) + return "no SPI found in SA specifier"; + oops = ttoul(spi, at - spi, base, &ul); + if (oops != NULL) + return oops; + sa->spi = htonl(ul); + + addr = at + 1; + alen = srclen - (addr - src); + if (af == AF_UNSPEC) + af = (memchr(addr, ':', alen) != NULL) ? AF_INET6 : AF_INET; + oops = ttoaddr(addr, alen, af, &sa->dst); + if (oops != NULL) + return oops; + + return NULL; +} + + + +#ifdef TTOSA_MAIN + +#include + +void regress(void); + +int +main(int argc, char *argv[]) +{ + ip_said sa; + char buf[100]; + char buf2[100]; + const char *oops; + size_t n; + + if (argc < 2) { + fprintf(stderr, "Usage: %s {ahnnn@aaa|-r}\n", argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + oops = ttosa(argv[1], 0, &sa); + if (oops != NULL) { + fprintf(stderr, "%s: conversion failed: %s\n", argv[0], oops); + exit(1); + } + n = satot(&sa, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + fprintf(stderr, "%s: reverse conv of `%d'", argv[0], sa.proto); + fprintf(stderr, "%lx@", (long unsigned int)sa.spi); + (void) addrtot(&sa.dst, 0, buf2, sizeof(buf2)); + fprintf(stderr, "%s", buf2); + fprintf(stderr, " failed: need %ld bytes, have only %ld\n", + (long)n, (long)sizeof(buf)); + exit(1); + } + printf("%s\n", buf); + + exit(0); +} + +struct rtab { + int format; +# define FUDGE 0x1000 + char *input; + char *output; /* NULL means error expected */ +} rtab[] = { + {0, "esp257@1.2.3.0", "esp.101@1.2.3.0"}, + {0, "ah0x20@1.2.3.4", "ah.20@1.2.3.4"}, + {0, "tun20@1.2.3.4", "tun.14@1.2.3.4"}, + {0, "comp20@1.2.3.4", "comp.14@1.2.3.4"}, + {0, "esp257@::1", "esp:101@::1"}, + {0, "esp257@0bc:12de::1", "esp:101@bc:12de::1"}, + {0, "esp78@1049:1::8007:2040", "esp:4e@1049:1::8007:2040"}, + {0, "esp0x78@1049:1::8007:2040", "esp:78@1049:1::8007:2040"}, + {0, "ah78@1049:1::8007:2040", "ah:4e@1049:1::8007:2040"}, + {0, "ah0x78@1049:1::8007:2040", "ah:78@1049:1::8007:2040"}, + {0, "tun78@1049:1::8007:2040", "tun:4e@1049:1::8007:2040"}, + {0, "tun0x78@1049:1::8007:2040", "tun:78@1049:1::8007:2040"}, + {0, "duk99@3ffe:370:400:ff::9001:3001", NULL}, + {0, "esp78x@1049:1::8007:2040", NULL}, + {0, "esp0x78@1049:1:0xfff::8007:2040", NULL}, + {0, "es78@1049:1::8007:2040", NULL}, + {0, "", NULL}, + {0, "_", NULL}, + {0, "ah2.2", NULL}, + {0, "goo2@1.2.3.4", NULL}, + {0, "esp9@1.2.3.4", "esp.9@1.2.3.4"}, + {'f', "esp0xa9@1.2.3.4", "esp.000000a9@1.2.3.4"}, + {0, "espp9@1.2.3.4", NULL}, + {0, "es9@1.2.3.4", NULL}, + {0, "ah@1.2.3.4", NULL}, + {0, "esp7x7@1.2.3.4", NULL}, + {0, "esp77@1.0x2.3.4", NULL}, + {0, PASSTHROUGHNAME, PASSTHROUGH4NAME}, + {0, PASSTHROUGH6NAME, PASSTHROUGH6NAME}, + {0, "%pass", "%pass"}, + {0, "int256@0.0.0.0", "%pass"}, + {0, "%drop", "%drop"}, + {0, "int257@0.0.0.0", "%drop"}, + {0, "%reject", "%reject"}, + {0, "int258@0.0.0.0", "%reject"}, + {0, "%hold", "%hold"}, + {0, "int259@0.0.0.0", "%hold"}, + {0, "%trap", "%trap"}, + {0, "int260@0.0.0.0", "%trap"}, + {0, "%trapsubnet", "%trapsubnet"}, + {0, "int261@0.0.0.0", "%trapsubnet"}, + {0, "int262@0.0.0.0", "int.106@0.0.0.0"}, + {FUDGE, "esp9@1.2.3.4", "unk77.9@1.2.3.4"}, + {0, NULL, NULL} +}; + +void +regress(void) +{ + struct rtab *r; + int status = 0; + ip_said sa; + char in[100]; + char buf[100]; + const char *oops; + size_t n; + + for (r = rtab; r->input != NULL; r++) { + strcpy(in, r->input); + oops = ttosa(in, 0, &sa); + if (oops != NULL && r->output == NULL) + {} /* okay, error expected */ + else if (oops != NULL) { + printf("`%s' ttosa failed: %s\n", r->input, oops); + status = 1; + } else if (r->output == NULL) { + printf("`%s' ttosa succeeded unexpectedly\n", + r->input); + status = 1; + } else { + if (r->format&FUDGE) + sa.proto = 77; + n = satot(&sa, (char)r->format, buf, sizeof(buf)); + if (n > sizeof(buf)) { + printf("`%s' satot failed: need %ld\n", + r->input, (long)n); + status = 1; + } else if (strcmp(r->output, buf) != 0) { + printf("`%s' gave `%s', expected `%s'\n", + r->input, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* TTOSA_MAIN */ diff --git a/src/libfreeswan/ttosubnet.c b/src/libfreeswan/ttosubnet.c new file mode 100644 index 000000000..7f5cddb82 --- /dev/null +++ b/src/libfreeswan/ttosubnet.c @@ -0,0 +1,296 @@ +/* + * convert from text form of subnet specification to binary + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ttosubnet.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +#ifndef DEFAULTSUBNET +#define DEFAULTSUBNET "%default" +#endif + +/* + - ttosubnet - convert text "addr/mask" to address and mask + * Mask can be integer bit count. + */ +err_t +ttosubnet(src, srclen, af, dst) +const char *src; +size_t srclen; /* 0 means "apply strlen" */ +int af; /* AF_INET or AF_INET6 */ +ip_subnet *dst; +{ + const char *slash; + const char *colon; + const char *mask; + size_t mlen; + const char *oops; + unsigned long bc; + static char def[] = DEFAULTSUBNET; +# define DEFLEN (sizeof(def) - 1) /* -1 for NUL */ + static char defis4[] = "0/0"; +# define DEFIS4LEN (sizeof(defis4) - 1) + static char defis6[] = "::/0"; +# define DEFIS6LEN (sizeof(defis6) - 1) + ip_address addrtmp; + ip_address masktmp; + int nbits; + int i; + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + + switch (af) { + case AF_INET: + nbits = 32; + break; + case AF_INET6: + nbits = 128; + break; + default: + return "unknown address family in ttosubnet"; + break; + } + + if (srclen == DEFLEN && strncmp(src, def, srclen) == 0) { + src = (af == AF_INET) ? defis4 : defis6; + srclen = (af == AF_INET) ? DEFIS4LEN : DEFIS6LEN; + } + + slash = memchr(src, '/', srclen); + if (slash == NULL) + return "no / in subnet specification"; + mask = slash + 1; + mlen = srclen - (mask - src); + + oops = ttoaddr(src, slash-src, af, &addrtmp); + if (oops != NULL) + return oops; + + /* extract port */ + colon = memchr(mask, ':', mlen); + if (colon == 0) + { + setportof(0, &addrtmp); + } + else + { + long port; + + oops = ttoul(colon+1, mlen-(colon-mask+1), 10, &port); + if (oops != NULL) + return oops; + setportof(htons(port), &addrtmp); + mlen = colon - mask; + } + + /*extract mask */ + oops = ttoul(mask, mlen, 10, &bc); + if (oops == NULL) { + /* ttoul succeeded, it's a bit-count mask */ + if (bc > nbits) + return "subnet mask bit count too large"; + i = bc; + } else { + oops = ttoaddr(mask, mlen, af, &masktmp); + if (oops != NULL) + return oops; + i = masktocount(&masktmp); + if (i < 0) + return "non-contiguous or otherwise erroneous mask"; + } + + return initsubnet(&addrtmp, i, '0', dst); +} + + + +#ifdef TTOSUBNET_MAIN + +#include + +void regress(void); + +int main(int argc, char *argv[]) +{ + ip_subnet s; + char buf[100]; + char buf2[100]; + const char *oops; + size_t n; + int af; + char *p; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [-6] addr/mask\n", argv[0]); + fprintf(stderr, " or: %s -r\n", argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-r") == 0) { + regress(); + fprintf(stderr, "regress() returned?!?\n"); + exit(1); + } + + af = AF_INET; + p = argv[1]; + if (strcmp(argv[1], "-6") == 0) { + af = AF_INET6; + p = argv[2]; + } else if (strchr(argv[1], ':') != NULL) + af = AF_INET6; + + oops = ttosubnet(p, 0, af, &s); + if (oops != NULL) { + fprintf(stderr, "%s: conversion failed: %s\n", argv[0], oops); + exit(1); + } + n = subnettot(&s, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + fprintf(stderr, "%s: reverse conversion of ", argv[0]); + (void) addrtot(&s.addr, 0, buf2, sizeof(buf2)); + fprintf(stderr, "%s/", buf2); + fprintf(stderr, "%d", s.maskbits); + fprintf(stderr, " failed: need %ld bytes, have only %ld\n", + (long)n, (long)sizeof(buf)); + exit(1); + } + printf("%s\n", buf); + + exit(0); +} + +struct rtab { + int family; + char *input; + char *output; /* NULL means error expected */ +} rtab[] = { + {4, "1.2.3.0/255.255.255.0", "1.2.3.0/24"}, + {4, "1.2.3.0/24", "1.2.3.0/24"}, + {4, "1.2.3.0/24:10", "1.2.3.0/24:10"}, + {4, "1.2.3.0/24:-1", NULL}, + {4, "1.2.3.0/24:none", NULL}, + {4, "1.2.3.0/24:", NULL}, + {4, "1.2.3.0/24:0x10", "1.2.3.0/24:16"}, + {4, "1.2.3.0/24:0X10", "1.2.3.0/24:16"}, + {4, "1.2.3.0/24:010", "1.2.3.0/24:8"}, + {4, "1.2.3.1/255.255.255.240", "1.2.3.0/28"}, + {4, "1.2.3.1/32", "1.2.3.1/32"}, + {4, "1.2.3.1/0", "0.0.0.0/0"}, +/* {4, "1.2.3.1/255.255.127.0", "1.2.3.0/255.255.127.0"}, */ + {4, "1.2.3.1/255.255.127.0", NULL}, + {4, "128.009.000.032/32", "128.9.0.32/32"}, + {4, "128.0x9.0.32/32", NULL}, + {4, "0x80090020/32", "128.9.0.32/32"}, + {4, "0x800x0020/32", NULL}, + {4, "128.9.0.32/0xffFF0000", "128.9.0.0/16"}, + {4, "128.9.0.32/0xff0000FF", NULL}, + {4, "128.9.0.32/0x0000ffFF", NULL}, + {4, "128.9.0.32/0x00ffFF0000", NULL}, + {4, "128.9.0.32/0xffFF", NULL}, + {4, "128.9.0.32.27/32", NULL}, + {4, "128.9.0k32/32", NULL}, + {4, "328.9.0.32/32", NULL}, + {4, "128.9..32/32", NULL}, + {4, "10/8", "10.0.0.0/8"}, + {4, "10.0/8", "10.0.0.0/8"}, + {4, "10.0.0/8", "10.0.0.0/8"}, + {4, "10.0.1/24", "10.0.1.0/24"}, + {4, "_", NULL}, + {4, "_/_", NULL}, + {4, "1.2.3.1", NULL}, + {4, "1.2.3.1/_", NULL}, + {4, "1.2.3.1/24._", NULL}, + {4, "1.2.3.1/99", NULL}, + {4, "localhost/32", "127.0.0.1/32"}, + {4, "%default", "0.0.0.0/0"}, + {6, "3049:1::8007:2040/0", "::/0"}, + {6, "3049:1::8007:2040/128", "3049:1::8007:2040/128"}, + {6, "3049:1::192.168.0.1/128", NULL}, /*"3049:1::c0a8:1/128",*/ + {6, "3049:1::8007::2040/128", NULL}, + {6, "3049:1::8007:2040/ffff::0", "3049::/16"}, + {6, "3049:1::8007:2040/64", "3049:1::/64"}, + {6, "3049:1::8007:2040/ffff::", "3049::/16"}, + {6, "3049:1::8007:2040/0000:ffff::0", NULL}, + {6, "3049:1::8007:2040/ff1f::0", NULL}, + {6, "3049:1::8007:x:2040/128", NULL}, + {6, "3049:1t::8007:2040/128", NULL}, + {6, "3049:1::80071:2040/128", NULL}, + {6, "::/21", "::/21"}, + {6, "::1/128", "::1/128"}, + {6, "1::/21", "1::/21"}, + {6, "1::2/128", "1::2/128"}, + {6, "1:0:0:0:0:0:0:2/128", "1::2/128"}, + {6, "1:0:0:0:3:0:0:2/128", "1::3:0:0:2/128"}, + {6, "1:0:0:3:0:0:0:2/128", "1::3:0:0:0:2/128"}, + {6, "1:0:3:0:0:0:0:2/128", "1:0:3::2/128"}, + {6, "abcd:ef01:2345:6789:0:00a:000:20/128", "abcd:ef01:2345:6789:0:a:0:20/128"}, + {6, "3049:1::8007:2040/ffff:ffff:", NULL}, + {6, "3049:1::8007:2040/ffff:88::", NULL}, + {6, "3049:12::9000:3200/ffff:fff0::", "3049:10::/28"}, + {6, "3049:12::9000:3200/28", "3049:10::/28"}, + {6, "3049:12::9000:3200/ff00:::", NULL}, + {6, "3049:12::9000:3200/ffff:::", NULL}, + {6, "3049:12::9000:3200/128_", NULL}, + {6, "3049:12::9000:3200/", NULL}, + {6, "%default", "::/0"}, + {4, NULL, NULL} +}; + +void +regress(void) +{ + struct rtab *r; + int status = 0; + ip_subnet s; + char in[100]; + char buf[100]; + const char *oops; + size_t n; + int af; + + for (r = rtab; r->input != NULL; r++) { + af = (r->family == 4) ? AF_INET : AF_INET6; + strcpy(in, r->input); + oops = ttosubnet(in, 0, af, &s); + if (oops != NULL && r->output == NULL) + {} /* okay, error expected */ + else if (oops != NULL) { + printf("`%s' ttosubnet failed: %s\n", r->input, oops); + status = 1; + } else if (r->output == NULL) { + printf("`%s' ttosubnet succeeded unexpectedly\n", + r->input); + status = 1; + } else { + n = subnettot(&s, 0, buf, sizeof(buf)); + if (n > sizeof(buf)) { + printf("`%s' subnettot failed: need %ld\n", + r->input, (long)n); + status = 1; + } else if (strcmp(r->output, buf) != 0) { + printf("`%s' gave `%s', expected `%s'\n", + r->input, buf, r->output); + status = 1; + } + } + } + exit(status); +} + +#endif /* TTOSUBNET_MAIN */ diff --git a/src/libfreeswan/ttoul.3 b/src/libfreeswan/ttoul.3 new file mode 100644 index 000000000..67d4bd34f --- /dev/null +++ b/src/libfreeswan/ttoul.3 @@ -0,0 +1,192 @@ +.TH IPSEC_TTOUL 3 "16 Aug 2000" +.\" RCSID $Id: ttoul.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec ttoul, ultot \- convert unsigned-long numbers to and from text +.SH SYNOPSIS +.B "#include +.sp +.B "const char *ttoul(const char *src, size_t srclen," +.ti +1c +.B "int base, unsigned long *n);" +.br +.B "size_t ultot(unsigned long n, int format, char *dst," +.ti +1c +.B "size_t dstlen);" +.SH DESCRIPTION +.I Ttoul +converts a text-string number into a binary +.B "unsigned long" +value. +.I Ultot +does the reverse conversion, back to a text version. +.PP +Numbers are specified in text as +decimal (e.g. +.BR 123 ), +octal with a leading zero (e.g. +.BR 012 , +which has value 10), +or hexadecimal with a leading +.B 0x +(e.g. +.BR 0x1f , +which has value 31) +in either upper or lower case. +.PP +The +.I srclen +parameter of +.I ttoul +specifies the length of the string pointed to by +.IR src ; +it is an error for there to be anything else +(e.g., a terminating NUL) within that length. +As a convenience for cases where an entire NUL-terminated string is +to be converted, +a +.I srclen +value of +.B 0 +is taken to mean +.BR strlen(src) . +.PP +The +.I base +parameter of +.I ttoul +can be +.BR 8 , +.BR 10 , +or +.BR 16 , +in which case the number supplied is assumed to be of that form +(and in the case of +.BR 16 , +to lack any +.B 0x +prefix). +It can also be +.BR 0 , +in which case the number is examined for a leading zero +or a leading +.B 0x +to determine its base. +.PP +The +.I dstlen +parameter of +.I ultot +specifies the size of the +.I dst +parameter; +under no circumstances are more than +.I dstlen +bytes written to +.IR dst . +A result which will not fit is truncated. +.I Dstlen +can be zero, in which case +.I dst +need not be valid and no result is written, +but the return value is unaffected; +in all other cases, the (possibly truncated) result is NUL-terminated. +The +.I freeswan.h +header file defines a constant, +.BR ULTOT_BUF , +which is the size of a buffer just large enough for worst-case results. +.PP +The +.I format +parameter of +.I ultot +must be one of: +.RS +.IP \fB'o'\fR 4 +octal conversion with leading +.B 0 +.IP \fB\ 8\fR +octal conversion with no leading +.B 0 +.IP \fB'd'\fR +decimal conversion +.IP \fB10\fR +same as +.B d +.IP \fB'x'\fR +hexadecimal conversion, including leading +.B 0x +.IP \fB16\fR +hexadecimal conversion with no leading +.B 0x +.IP \fB17\fR +like +.B 16 +except padded on left with +.BR 0 s +to eight digits (full width of a 32-bit number) +.RE +.PP +.I Ttoul +returns NULL for success and +a pointer to a string-literal error message for failure; +see DIAGNOSTICS. +.I Ultot +returns +.B 0 +for a failure, and otherwise +returns the size of buffer which would +be needed to +accommodate the full conversion result, including terminating NUL +(it is the caller's responsibility to check this against the size of +the provided buffer to determine whether truncation has occurred). +.SH SEE ALSO +atol(3), strtoul(3) +.SH DIAGNOSTICS +Fatal errors in +.I ttoul +are: +empty input; +unknown +.IR base ; +non-digit character found; +number too large for an +.BR "unsigned long" . +.PP +Fatal errors in +.I ultot +are: +unknown +.IR format . +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. +.SH BUGS +Conversion of +.B 0 +with format +.B o +yields +.BR 00 . +.PP +.I Ultot +format +.B 17 +is a bit of a kludge. +.PP +The restriction of error reports to literal strings +(so that callers don't need to worry about freeing them or copying them) +does limit the precision of error reporting. +.PP +The error-reporting convention lends itself to slightly obscure code, +because many readers will not think of NULL as signifying success. +A good way to make it clearer is to write something like: +.PP +.RS +.nf +.B "const char *error;" +.sp +.B "error = ttoul( /* ... */ );" +.B "if (error != NULL) {" +.B " /* something went wrong */" +.fi +.RE diff --git a/src/libfreeswan/ttoul.c b/src/libfreeswan/ttoul.c new file mode 100644 index 000000000..9c6193c68 --- /dev/null +++ b/src/libfreeswan/ttoul.c @@ -0,0 +1,91 @@ +/* + * convert from text form of unsigned long to binary + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ttoul.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - ttoul - convert text substring to unsigned long number + */ +const char * /* NULL for success, else string literal */ +ttoul(src, srclen, base, resultp) +const char *src; +size_t srclen; /* 0 means strlen(src) */ +int base; /* 0 means figure it out */ +unsigned long *resultp; +{ + const char *stop; + static char hex[] = "0123456789abcdef"; + static char uchex[] = "0123456789ABCDEF"; + int d; + char c; + char *p; + unsigned long r; + unsigned long rlimit; + int dlimit; + + if (srclen == 0) + srclen = strlen(src); + if (srclen == 0) + return "empty string"; + + if (base == 0) { + if (srclen > 2 && *src == '0' && + (*(src+1) == 'x' || *(src+1) == 'X')) + return ttoul(src+2, srclen-2, 16, resultp); + if (srclen > 1 && *src == '0') + return ttoul(src+1, srclen-1, 8, resultp); + return ttoul(src, srclen, 10, resultp); + } + if (base != 8 && base != 10 && base != 16) + return "unsupported number base"; + + r = 0; + stop = src + srclen; + if (base == 16) { + while (src < stop) { + c = *src++; + p = strchr(hex, c); + if (p != NULL) + d = p - hex; + else { + p = strchr(uchex, c); + if (p == NULL) + return "non-hex digit in hex number"; + d = p - uchex; + } + r = (r << 4) | d; + } + /* defer length check to catch invalid digits first */ + if (srclen > sizeof(unsigned long) * 2) + return "hex number too long"; + } else { + rlimit = ULONG_MAX / base; + dlimit = (int)(ULONG_MAX - rlimit*base); + while (src < stop) { + c = *src++; + d = c - '0'; + if (d < 0 || d >= base) + return "non-digit in number"; + if (r > rlimit || (r == rlimit && d > dlimit)) + return "unsigned-long overflow"; + r = r*base + d; + } + } + + *resultp = r; + return NULL; +} diff --git a/src/libfreeswan/ultoa.c b/src/libfreeswan/ultoa.c new file mode 100644 index 000000000..2c2644826 --- /dev/null +++ b/src/libfreeswan/ultoa.c @@ -0,0 +1,67 @@ +/* + * convert unsigned long to ASCII + * Copyright (C) 1998, 1999 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ultoa.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - ultoa - convert unsigned long to decimal ASCII + */ +size_t /* length required for full conversion */ +ultoa(n, base, dst, dstlen) +unsigned long n; +int base; +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + char buf[3*sizeof(unsigned long) + 1]; + char *bufend = buf + sizeof(buf); + size_t len; + char *p; + static char hex[] = "0123456789abcdef"; + + p = bufend; + *--p = '\0'; + if (base == 10) { + do { + *--p = n%10 + '0'; + n /= 10; + } while (n != 0); + } else if (base == 16) { + do { + *--p = hex[n&0xf]; + n >>= 4; + } while (n != 0); + *--p = 'x'; + *--p = '0'; + } else if (base == 8) { + do { + *--p = (n&07) + '0'; + n >>= 3; + } while (n != 0); + *--p = '0'; + } else + *--p = '?'; + + len = bufend - p; + + if (dstlen > 0) { + if (len > dstlen) + *(p + dstlen - 1) = '\0'; + strcpy(dst, p); + } + return len; +} diff --git a/src/libfreeswan/ultot.c b/src/libfreeswan/ultot.c new file mode 100644 index 000000000..edffa4a2d --- /dev/null +++ b/src/libfreeswan/ultot.c @@ -0,0 +1,83 @@ +/* + * convert unsigned long to text + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: ultot.c,v 1.1 2004/03/15 20:35:26 as Exp $ + */ +#include "internal.h" +#include "freeswan.h" + +/* + - ultot - convert unsigned long to text + */ +size_t /* length required for full conversion */ +ultot(n, base, dst, dstlen) +unsigned long n; +int base; +char *dst; /* need not be valid if dstlen is 0 */ +size_t dstlen; +{ + char buf[3*sizeof(unsigned long) + 1]; + char *bufend = buf + sizeof(buf); + size_t len; + char *p; + static char hex[] = "0123456789abcdef"; +# define HEX32 (32/4) + + p = bufend; + *--p = '\0'; + switch (base) { + case 10: + case 'd': + do { + *--p = n%10 + '0'; + n /= 10; + } while (n != 0); + break; + case 16: + case 17: + case 'x': + do { + *--p = hex[n&0xf]; + n >>= 4; + } while (n != 0); + if (base == 17) + while (bufend - p < HEX32 + 1) + *--p = '0'; + if (base == 'x') { + *--p = 'x'; + *--p = '0'; + } + break; + case 8: + case 'o': + do { + *--p = (n&07) + '0'; + n >>= 3; + } while (n != 0); + if (base == 'o') + *--p = '0'; + break; + default: + return 0; + break; + } + + len = bufend - p; + if (dstlen > 0) { + if (len > dstlen) + *(p + dstlen - 1) = '\0'; + strcpy(dst, p); + } + return len; +} diff --git a/src/libfreeswan/version.3 b/src/libfreeswan/version.3 new file mode 100644 index 000000000..06c5f01e3 --- /dev/null +++ b/src/libfreeswan/version.3 @@ -0,0 +1,44 @@ +.TH IPSEC_VERSION 3 "21 Nov 2001" +.\" RCSID $Id: version.3,v 1.1 2004/03/15 20:35:26 as Exp $ +.SH NAME +ipsec ipsec_version_code \- get IPsec version code +.br +ipsec ipsec_version_string \- get full IPsec version string +.br +ipsec ipsec_copyright_notice \- get IPsec copyright notice +.SH SYNOPSIS +.B "#include +.sp +.B "const char *ipsec_version_code(void);" +.br +.B "const char *ipsec_version_string(void);" +.br +.B "const char **ipsec_copyright_notice(void);" +.SH DESCRIPTION +These functions provide information on version numbering and copyright +of the Linux FreeS/WAN IPsec implementation. +.PP +.I Ipsec_version_code +returns a pointer to a string constant +containing the current IPsec version code, +such as ``1.92'' or ``snap2001Nov19b''. +.PP +.I Ipsec_version_string +returns a pointer to a string constant giving a full version identification, +consisting of the version code preceded by a prefix identifying the software, +e.g. ``Linux FreeS/WAN 1.92''. +.PP +.I Ipsec_copyright_notice +returns a pointer to a vector of pointers, +terminated by a +.BR NULL , +which is the text of a suitable copyright notice. +Each pointer points to a string constant (possibly empty) which is one line +of the somewhat-verbose copyright notice. +The strings are NUL-terminated and do not contain a newline; +supplying suitable line termination for the output device is +the caller's responsibility. +.SH SEE ALSO +ipsec(8) +.SH HISTORY +Written for the FreeS/WAN project by Henry Spencer. diff --git a/src/libfreeswan/version.c b/src/libfreeswan/version.c new file mode 100644 index 000000000..3a947b1b9 --- /dev/null +++ b/src/libfreeswan/version.c @@ -0,0 +1,43 @@ +/* + * return IPsec version information + * Copyright (C) 2001 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + * + * RCSID $Id: version.in.c,v 1.2 2004/03/16 12:26:32 as Exp $ + */ + +#ifdef __KERNEL__ +#include +#endif + +#include "freeswan.h" + +static const char strongswan_number[] = VERSION; +static const char strongswan_string[] = "Linux strongSwan " VERSION; + +/* + - ipsec_version_code - return IPsec version number/code, as string + */ +const char * +ipsec_version_code() +{ + return strongswan_number; +} + +/* + - ipsec_version_string - return full version string + */ +const char * +ipsec_version_string() +{ + return strongswan_string; +} diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am new file mode 100644 index 000000000..b103be193 --- /dev/null +++ b/src/libstrongswan/Makefile.am @@ -0,0 +1,69 @@ +lib_LTLIBRARIES = libstrongswan.la + +libstrongswan_la_SOURCES = \ +credential_store.h \ +library.c library.h \ +chunk.c chunk.h \ +debug.c debug.h \ +enum.c enum.h \ +printf_hook.c printf_hook.h \ +asn1/asn1.c asn1/asn1.h \ +asn1/oid.c asn1/oid.h \ +asn1/pem.c asn1/pem.h \ +asn1/ttodata.c asn1/ttodata.h \ +crypto/ca.c crypto/ca.h \ +crypto/certinfo.c crypto/certinfo.h \ +crypto/crl.c crypto/crl.h \ +crypto/crypters/crypter.c crypto/crypters/crypter.h \ +crypto/crypters/aes_cbc_crypter.c crypto/crypters/aes_cbc_crypter.h\ +crypto/crypters/des_crypter.c crypto/crypters/des_crypter.h\ +crypto/diffie_hellman.c crypto/diffie_hellman.h \ +crypto/hashers/hasher.h crypto/hashers/hasher.c \ +crypto/hashers/sha1_hasher.c crypto/hashers/sha1_hasher.h \ +crypto/hashers/sha2_hasher.c crypto/hashers/sha2_hasher.h \ +crypto/hashers/md5_hasher.c crypto/hashers/md5_hasher.h \ +crypto/hmac.c crypto/hmac.h \ +crypto/ocsp.c crypto/ocsp.h \ +crypto/prfs/fips_prf.c crypto/prfs/fips_prf.h \ +crypto/prfs/hmac_prf.c crypto/prfs/hmac_prf.h \ +crypto/prfs/prf.c crypto/prfs/prf.h \ +crypto/prf_plus.h crypto/prf_plus.c \ +crypto/rsa/rsa_private_key.c crypto/rsa/rsa_private_key.h \ +crypto/rsa/rsa_public_key.h crypto/rsa/rsa_public_key.c \ +crypto/signers/hmac_signer.c crypto/signers/hmac_signer.h \ +crypto/signers/signer.c crypto/signers/signer.h \ +crypto/x509.c crypto/x509.h \ +utils/fetcher.c utils/fetcher.h \ +utils/host.c utils/host.h \ +utils/identification.c utils/identification.h \ +utils/iterator.h \ +utils/leak_detective.c utils/leak_detective.h \ +utils/lexparser.c utils/lexparser.h \ +utils/linked_list.c utils/linked_list.h \ +utils/randomizer.c utils/randomizer.h + +libstrongswan_la_LIBADD = -lgmp -lpthread + +INCLUDES = -I$(top_srcdir)/src/libstrongswan +EXTRA_DIST = asn1/oid.txt asn1/oid.pl +BUILT_SOURCES = asn1/oid.c asn1/oid.h +MAINTAINERCLEANFILES = asn1/oid.c asn1/oid.h + +if USE_LEAK_DETECTIVE + libstrongswan_la_LIBADD += -ldl + AM_CFLAGS = -DLEAK_DETECTIVE +endif + +if USE_LIBCURL + libstrongswan_la_LIBADD += -lcurl +endif + +if USE_LIBLDAP + libstrongswan_la_LIBADD += -lldap -llber +endif + +asn1/oid.c : asn1/oid.txt asn1/oid.pl + cd asn1 && $(PERL) oid.pl + +asn1/oid.h : asn1/oid.txt asn1/oid.pl + cd asn1 && $(PERL) oid.pl diff --git a/src/libstrongswan/Makefile.in b/src/libstrongswan/Makefile.in new file mode 100644 index 000000000..e5c5c758e --- /dev/null +++ b/src/libstrongswan/Makefile.in @@ -0,0 +1,820 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@USE_LEAK_DETECTIVE_TRUE@am__append_1 = -ldl +@USE_LIBCURL_TRUE@am__append_2 = -lcurl +@USE_LIBLDAP_TRUE@am__append_3 = -lldap -llber +subdir = src/libstrongswan +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libstrongswan_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libstrongswan_la_OBJECTS = library.lo chunk.lo debug.lo enum.lo \ + printf_hook.lo asn1.lo oid.lo pem.lo ttodata.lo ca.lo \ + certinfo.lo crl.lo crypter.lo aes_cbc_crypter.lo \ + des_crypter.lo diffie_hellman.lo hasher.lo sha1_hasher.lo \ + sha2_hasher.lo md5_hasher.lo hmac.lo ocsp.lo fips_prf.lo \ + hmac_prf.lo prf.lo prf_plus.lo rsa_private_key.lo \ + rsa_public_key.lo hmac_signer.lo signer.lo x509.lo fetcher.lo \ + host.lo identification.lo leak_detective.lo lexparser.lo \ + linked_list.lo randomizer.lo +libstrongswan_la_OBJECTS = $(am_libstrongswan_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libstrongswan_la_SOURCES) +DIST_SOURCES = $(libstrongswan_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +lib_LTLIBRARIES = libstrongswan.la +libstrongswan_la_SOURCES = \ +credential_store.h \ +library.c library.h \ +chunk.c chunk.h \ +debug.c debug.h \ +enum.c enum.h \ +printf_hook.c printf_hook.h \ +asn1/asn1.c asn1/asn1.h \ +asn1/oid.c asn1/oid.h \ +asn1/pem.c asn1/pem.h \ +asn1/ttodata.c asn1/ttodata.h \ +crypto/ca.c crypto/ca.h \ +crypto/certinfo.c crypto/certinfo.h \ +crypto/crl.c crypto/crl.h \ +crypto/crypters/crypter.c crypto/crypters/crypter.h \ +crypto/crypters/aes_cbc_crypter.c crypto/crypters/aes_cbc_crypter.h\ +crypto/crypters/des_crypter.c crypto/crypters/des_crypter.h\ +crypto/diffie_hellman.c crypto/diffie_hellman.h \ +crypto/hashers/hasher.h crypto/hashers/hasher.c \ +crypto/hashers/sha1_hasher.c crypto/hashers/sha1_hasher.h \ +crypto/hashers/sha2_hasher.c crypto/hashers/sha2_hasher.h \ +crypto/hashers/md5_hasher.c crypto/hashers/md5_hasher.h \ +crypto/hmac.c crypto/hmac.h \ +crypto/ocsp.c crypto/ocsp.h \ +crypto/prfs/fips_prf.c crypto/prfs/fips_prf.h \ +crypto/prfs/hmac_prf.c crypto/prfs/hmac_prf.h \ +crypto/prfs/prf.c crypto/prfs/prf.h \ +crypto/prf_plus.h crypto/prf_plus.c \ +crypto/rsa/rsa_private_key.c crypto/rsa/rsa_private_key.h \ +crypto/rsa/rsa_public_key.h crypto/rsa/rsa_public_key.c \ +crypto/signers/hmac_signer.c crypto/signers/hmac_signer.h \ +crypto/signers/signer.c crypto/signers/signer.h \ +crypto/x509.c crypto/x509.h \ +utils/fetcher.c utils/fetcher.h \ +utils/host.c utils/host.h \ +utils/identification.c utils/identification.h \ +utils/iterator.h \ +utils/leak_detective.c utils/leak_detective.h \ +utils/lexparser.c utils/lexparser.h \ +utils/linked_list.c utils/linked_list.h \ +utils/randomizer.c utils/randomizer.h + +libstrongswan_la_LIBADD = -lgmp -lpthread $(am__append_1) \ + $(am__append_2) $(am__append_3) +INCLUDES = -I$(top_srcdir)/src/libstrongswan +EXTRA_DIST = asn1/oid.txt asn1/oid.pl +BUILT_SOURCES = asn1/oid.c asn1/oid.h +MAINTAINERCLEANFILES = asn1/oid.c asn1/oid.h +@USE_LEAK_DETECTIVE_TRUE@AM_CFLAGS = -DLEAK_DETECTIVE +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libstrongswan/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libstrongswan/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libstrongswan.la: $(libstrongswan_la_OBJECTS) $(libstrongswan_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libstrongswan_la_LDFLAGS) $(libstrongswan_la_OBJECTS) $(libstrongswan_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aes_cbc_crypter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certinfo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chunk.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/des_crypter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diffie_hellman.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enum.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fetcher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fips_prf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hasher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac_prf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac_signer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/host.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/identification.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/leak_detective.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lexparser.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/library.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linked_list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_hasher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oid.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prf_plus.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf_hook.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/randomizer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsa_private_key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsa_public_key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1_hasher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha2_hasher.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ttodata.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +asn1.lo: asn1/asn1.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT asn1.lo -MD -MP -MF "$(DEPDIR)/asn1.Tpo" -c -o asn1.lo `test -f 'asn1/asn1.c' || echo '$(srcdir)/'`asn1/asn1.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/asn1.Tpo" "$(DEPDIR)/asn1.Plo"; else rm -f "$(DEPDIR)/asn1.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1/asn1.c' object='asn1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o asn1.lo `test -f 'asn1/asn1.c' || echo '$(srcdir)/'`asn1/asn1.c + +oid.lo: asn1/oid.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oid.lo -MD -MP -MF "$(DEPDIR)/oid.Tpo" -c -o oid.lo `test -f 'asn1/oid.c' || echo '$(srcdir)/'`asn1/oid.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/oid.Tpo" "$(DEPDIR)/oid.Plo"; else rm -f "$(DEPDIR)/oid.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1/oid.c' object='oid.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oid.lo `test -f 'asn1/oid.c' || echo '$(srcdir)/'`asn1/oid.c + +pem.lo: asn1/pem.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pem.lo -MD -MP -MF "$(DEPDIR)/pem.Tpo" -c -o pem.lo `test -f 'asn1/pem.c' || echo '$(srcdir)/'`asn1/pem.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/pem.Tpo" "$(DEPDIR)/pem.Plo"; else rm -f "$(DEPDIR)/pem.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1/pem.c' object='pem.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pem.lo `test -f 'asn1/pem.c' || echo '$(srcdir)/'`asn1/pem.c + +ttodata.lo: asn1/ttodata.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ttodata.lo -MD -MP -MF "$(DEPDIR)/ttodata.Tpo" -c -o ttodata.lo `test -f 'asn1/ttodata.c' || echo '$(srcdir)/'`asn1/ttodata.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ttodata.Tpo" "$(DEPDIR)/ttodata.Plo"; else rm -f "$(DEPDIR)/ttodata.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1/ttodata.c' object='ttodata.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ttodata.lo `test -f 'asn1/ttodata.c' || echo '$(srcdir)/'`asn1/ttodata.c + +ca.lo: crypto/ca.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ca.lo -MD -MP -MF "$(DEPDIR)/ca.Tpo" -c -o ca.lo `test -f 'crypto/ca.c' || echo '$(srcdir)/'`crypto/ca.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ca.Tpo" "$(DEPDIR)/ca.Plo"; else rm -f "$(DEPDIR)/ca.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/ca.c' object='ca.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ca.lo `test -f 'crypto/ca.c' || echo '$(srcdir)/'`crypto/ca.c + +certinfo.lo: crypto/certinfo.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT certinfo.lo -MD -MP -MF "$(DEPDIR)/certinfo.Tpo" -c -o certinfo.lo `test -f 'crypto/certinfo.c' || echo '$(srcdir)/'`crypto/certinfo.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/certinfo.Tpo" "$(DEPDIR)/certinfo.Plo"; else rm -f "$(DEPDIR)/certinfo.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/certinfo.c' object='certinfo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o certinfo.lo `test -f 'crypto/certinfo.c' || echo '$(srcdir)/'`crypto/certinfo.c + +crl.lo: crypto/crl.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT crl.lo -MD -MP -MF "$(DEPDIR)/crl.Tpo" -c -o crl.lo `test -f 'crypto/crl.c' || echo '$(srcdir)/'`crypto/crl.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/crl.Tpo" "$(DEPDIR)/crl.Plo"; else rm -f "$(DEPDIR)/crl.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/crl.c' object='crl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o crl.lo `test -f 'crypto/crl.c' || echo '$(srcdir)/'`crypto/crl.c + +crypter.lo: crypto/crypters/crypter.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT crypter.lo -MD -MP -MF "$(DEPDIR)/crypter.Tpo" -c -o crypter.lo `test -f 'crypto/crypters/crypter.c' || echo '$(srcdir)/'`crypto/crypters/crypter.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/crypter.Tpo" "$(DEPDIR)/crypter.Plo"; else rm -f "$(DEPDIR)/crypter.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/crypters/crypter.c' object='crypter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o crypter.lo `test -f 'crypto/crypters/crypter.c' || echo '$(srcdir)/'`crypto/crypters/crypter.c + +aes_cbc_crypter.lo: crypto/crypters/aes_cbc_crypter.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT aes_cbc_crypter.lo -MD -MP -MF "$(DEPDIR)/aes_cbc_crypter.Tpo" -c -o aes_cbc_crypter.lo `test -f 'crypto/crypters/aes_cbc_crypter.c' || echo '$(srcdir)/'`crypto/crypters/aes_cbc_crypter.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/aes_cbc_crypter.Tpo" "$(DEPDIR)/aes_cbc_crypter.Plo"; else rm -f "$(DEPDIR)/aes_cbc_crypter.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/crypters/aes_cbc_crypter.c' object='aes_cbc_crypter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o aes_cbc_crypter.lo `test -f 'crypto/crypters/aes_cbc_crypter.c' || echo '$(srcdir)/'`crypto/crypters/aes_cbc_crypter.c + +des_crypter.lo: crypto/crypters/des_crypter.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT des_crypter.lo -MD -MP -MF "$(DEPDIR)/des_crypter.Tpo" -c -o des_crypter.lo `test -f 'crypto/crypters/des_crypter.c' || echo '$(srcdir)/'`crypto/crypters/des_crypter.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/des_crypter.Tpo" "$(DEPDIR)/des_crypter.Plo"; else rm -f "$(DEPDIR)/des_crypter.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/crypters/des_crypter.c' object='des_crypter.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o des_crypter.lo `test -f 'crypto/crypters/des_crypter.c' || echo '$(srcdir)/'`crypto/crypters/des_crypter.c + +diffie_hellman.lo: crypto/diffie_hellman.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT diffie_hellman.lo -MD -MP -MF "$(DEPDIR)/diffie_hellman.Tpo" -c -o diffie_hellman.lo `test -f 'crypto/diffie_hellman.c' || echo '$(srcdir)/'`crypto/diffie_hellman.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/diffie_hellman.Tpo" "$(DEPDIR)/diffie_hellman.Plo"; else rm -f "$(DEPDIR)/diffie_hellman.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/diffie_hellman.c' object='diffie_hellman.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o diffie_hellman.lo `test -f 'crypto/diffie_hellman.c' || echo '$(srcdir)/'`crypto/diffie_hellman.c + +hasher.lo: crypto/hashers/hasher.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hasher.lo -MD -MP -MF "$(DEPDIR)/hasher.Tpo" -c -o hasher.lo `test -f 'crypto/hashers/hasher.c' || echo '$(srcdir)/'`crypto/hashers/hasher.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hasher.Tpo" "$(DEPDIR)/hasher.Plo"; else rm -f "$(DEPDIR)/hasher.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/hashers/hasher.c' object='hasher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hasher.lo `test -f 'crypto/hashers/hasher.c' || echo '$(srcdir)/'`crypto/hashers/hasher.c + +sha1_hasher.lo: crypto/hashers/sha1_hasher.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha1_hasher.lo -MD -MP -MF "$(DEPDIR)/sha1_hasher.Tpo" -c -o sha1_hasher.lo `test -f 'crypto/hashers/sha1_hasher.c' || echo '$(srcdir)/'`crypto/hashers/sha1_hasher.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sha1_hasher.Tpo" "$(DEPDIR)/sha1_hasher.Plo"; else rm -f "$(DEPDIR)/sha1_hasher.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/hashers/sha1_hasher.c' object='sha1_hasher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha1_hasher.lo `test -f 'crypto/hashers/sha1_hasher.c' || echo '$(srcdir)/'`crypto/hashers/sha1_hasher.c + +sha2_hasher.lo: crypto/hashers/sha2_hasher.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha2_hasher.lo -MD -MP -MF "$(DEPDIR)/sha2_hasher.Tpo" -c -o sha2_hasher.lo `test -f 'crypto/hashers/sha2_hasher.c' || echo '$(srcdir)/'`crypto/hashers/sha2_hasher.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sha2_hasher.Tpo" "$(DEPDIR)/sha2_hasher.Plo"; else rm -f "$(DEPDIR)/sha2_hasher.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/hashers/sha2_hasher.c' object='sha2_hasher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha2_hasher.lo `test -f 'crypto/hashers/sha2_hasher.c' || echo '$(srcdir)/'`crypto/hashers/sha2_hasher.c + +md5_hasher.lo: crypto/hashers/md5_hasher.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT md5_hasher.lo -MD -MP -MF "$(DEPDIR)/md5_hasher.Tpo" -c -o md5_hasher.lo `test -f 'crypto/hashers/md5_hasher.c' || echo '$(srcdir)/'`crypto/hashers/md5_hasher.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/md5_hasher.Tpo" "$(DEPDIR)/md5_hasher.Plo"; else rm -f "$(DEPDIR)/md5_hasher.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/hashers/md5_hasher.c' object='md5_hasher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o md5_hasher.lo `test -f 'crypto/hashers/md5_hasher.c' || echo '$(srcdir)/'`crypto/hashers/md5_hasher.c + +hmac.lo: crypto/hmac.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hmac.lo -MD -MP -MF "$(DEPDIR)/hmac.Tpo" -c -o hmac.lo `test -f 'crypto/hmac.c' || echo '$(srcdir)/'`crypto/hmac.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hmac.Tpo" "$(DEPDIR)/hmac.Plo"; else rm -f "$(DEPDIR)/hmac.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/hmac.c' object='hmac.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hmac.lo `test -f 'crypto/hmac.c' || echo '$(srcdir)/'`crypto/hmac.c + +ocsp.lo: crypto/ocsp.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ocsp.lo -MD -MP -MF "$(DEPDIR)/ocsp.Tpo" -c -o ocsp.lo `test -f 'crypto/ocsp.c' || echo '$(srcdir)/'`crypto/ocsp.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ocsp.Tpo" "$(DEPDIR)/ocsp.Plo"; else rm -f "$(DEPDIR)/ocsp.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/ocsp.c' object='ocsp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ocsp.lo `test -f 'crypto/ocsp.c' || echo '$(srcdir)/'`crypto/ocsp.c + +fips_prf.lo: crypto/prfs/fips_prf.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fips_prf.lo -MD -MP -MF "$(DEPDIR)/fips_prf.Tpo" -c -o fips_prf.lo `test -f 'crypto/prfs/fips_prf.c' || echo '$(srcdir)/'`crypto/prfs/fips_prf.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/fips_prf.Tpo" "$(DEPDIR)/fips_prf.Plo"; else rm -f "$(DEPDIR)/fips_prf.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/prfs/fips_prf.c' object='fips_prf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fips_prf.lo `test -f 'crypto/prfs/fips_prf.c' || echo '$(srcdir)/'`crypto/prfs/fips_prf.c + +hmac_prf.lo: crypto/prfs/hmac_prf.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hmac_prf.lo -MD -MP -MF "$(DEPDIR)/hmac_prf.Tpo" -c -o hmac_prf.lo `test -f 'crypto/prfs/hmac_prf.c' || echo '$(srcdir)/'`crypto/prfs/hmac_prf.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hmac_prf.Tpo" "$(DEPDIR)/hmac_prf.Plo"; else rm -f "$(DEPDIR)/hmac_prf.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/prfs/hmac_prf.c' object='hmac_prf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hmac_prf.lo `test -f 'crypto/prfs/hmac_prf.c' || echo '$(srcdir)/'`crypto/prfs/hmac_prf.c + +prf.lo: crypto/prfs/prf.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT prf.lo -MD -MP -MF "$(DEPDIR)/prf.Tpo" -c -o prf.lo `test -f 'crypto/prfs/prf.c' || echo '$(srcdir)/'`crypto/prfs/prf.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/prf.Tpo" "$(DEPDIR)/prf.Plo"; else rm -f "$(DEPDIR)/prf.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/prfs/prf.c' object='prf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o prf.lo `test -f 'crypto/prfs/prf.c' || echo '$(srcdir)/'`crypto/prfs/prf.c + +prf_plus.lo: crypto/prf_plus.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT prf_plus.lo -MD -MP -MF "$(DEPDIR)/prf_plus.Tpo" -c -o prf_plus.lo `test -f 'crypto/prf_plus.c' || echo '$(srcdir)/'`crypto/prf_plus.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/prf_plus.Tpo" "$(DEPDIR)/prf_plus.Plo"; else rm -f "$(DEPDIR)/prf_plus.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/prf_plus.c' object='prf_plus.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o prf_plus.lo `test -f 'crypto/prf_plus.c' || echo '$(srcdir)/'`crypto/prf_plus.c + +rsa_private_key.lo: crypto/rsa/rsa_private_key.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsa_private_key.lo -MD -MP -MF "$(DEPDIR)/rsa_private_key.Tpo" -c -o rsa_private_key.lo `test -f 'crypto/rsa/rsa_private_key.c' || echo '$(srcdir)/'`crypto/rsa/rsa_private_key.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rsa_private_key.Tpo" "$(DEPDIR)/rsa_private_key.Plo"; else rm -f "$(DEPDIR)/rsa_private_key.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/rsa/rsa_private_key.c' object='rsa_private_key.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsa_private_key.lo `test -f 'crypto/rsa/rsa_private_key.c' || echo '$(srcdir)/'`crypto/rsa/rsa_private_key.c + +rsa_public_key.lo: crypto/rsa/rsa_public_key.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsa_public_key.lo -MD -MP -MF "$(DEPDIR)/rsa_public_key.Tpo" -c -o rsa_public_key.lo `test -f 'crypto/rsa/rsa_public_key.c' || echo '$(srcdir)/'`crypto/rsa/rsa_public_key.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/rsa_public_key.Tpo" "$(DEPDIR)/rsa_public_key.Plo"; else rm -f "$(DEPDIR)/rsa_public_key.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/rsa/rsa_public_key.c' object='rsa_public_key.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsa_public_key.lo `test -f 'crypto/rsa/rsa_public_key.c' || echo '$(srcdir)/'`crypto/rsa/rsa_public_key.c + +hmac_signer.lo: crypto/signers/hmac_signer.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hmac_signer.lo -MD -MP -MF "$(DEPDIR)/hmac_signer.Tpo" -c -o hmac_signer.lo `test -f 'crypto/signers/hmac_signer.c' || echo '$(srcdir)/'`crypto/signers/hmac_signer.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/hmac_signer.Tpo" "$(DEPDIR)/hmac_signer.Plo"; else rm -f "$(DEPDIR)/hmac_signer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/signers/hmac_signer.c' object='hmac_signer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hmac_signer.lo `test -f 'crypto/signers/hmac_signer.c' || echo '$(srcdir)/'`crypto/signers/hmac_signer.c + +signer.lo: crypto/signers/signer.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT signer.lo -MD -MP -MF "$(DEPDIR)/signer.Tpo" -c -o signer.lo `test -f 'crypto/signers/signer.c' || echo '$(srcdir)/'`crypto/signers/signer.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/signer.Tpo" "$(DEPDIR)/signer.Plo"; else rm -f "$(DEPDIR)/signer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/signers/signer.c' object='signer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o signer.lo `test -f 'crypto/signers/signer.c' || echo '$(srcdir)/'`crypto/signers/signer.c + +x509.lo: crypto/x509.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT x509.lo -MD -MP -MF "$(DEPDIR)/x509.Tpo" -c -o x509.lo `test -f 'crypto/x509.c' || echo '$(srcdir)/'`crypto/x509.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/x509.Tpo" "$(DEPDIR)/x509.Plo"; else rm -f "$(DEPDIR)/x509.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto/x509.c' object='x509.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x509.lo `test -f 'crypto/x509.c' || echo '$(srcdir)/'`crypto/x509.c + +fetcher.lo: utils/fetcher.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fetcher.lo -MD -MP -MF "$(DEPDIR)/fetcher.Tpo" -c -o fetcher.lo `test -f 'utils/fetcher.c' || echo '$(srcdir)/'`utils/fetcher.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/fetcher.Tpo" "$(DEPDIR)/fetcher.Plo"; else rm -f "$(DEPDIR)/fetcher.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/fetcher.c' object='fetcher.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fetcher.lo `test -f 'utils/fetcher.c' || echo '$(srcdir)/'`utils/fetcher.c + +host.lo: utils/host.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT host.lo -MD -MP -MF "$(DEPDIR)/host.Tpo" -c -o host.lo `test -f 'utils/host.c' || echo '$(srcdir)/'`utils/host.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/host.Tpo" "$(DEPDIR)/host.Plo"; else rm -f "$(DEPDIR)/host.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/host.c' object='host.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o host.lo `test -f 'utils/host.c' || echo '$(srcdir)/'`utils/host.c + +identification.lo: utils/identification.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT identification.lo -MD -MP -MF "$(DEPDIR)/identification.Tpo" -c -o identification.lo `test -f 'utils/identification.c' || echo '$(srcdir)/'`utils/identification.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/identification.Tpo" "$(DEPDIR)/identification.Plo"; else rm -f "$(DEPDIR)/identification.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/identification.c' object='identification.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o identification.lo `test -f 'utils/identification.c' || echo '$(srcdir)/'`utils/identification.c + +leak_detective.lo: utils/leak_detective.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT leak_detective.lo -MD -MP -MF "$(DEPDIR)/leak_detective.Tpo" -c -o leak_detective.lo `test -f 'utils/leak_detective.c' || echo '$(srcdir)/'`utils/leak_detective.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/leak_detective.Tpo" "$(DEPDIR)/leak_detective.Plo"; else rm -f "$(DEPDIR)/leak_detective.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/leak_detective.c' object='leak_detective.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o leak_detective.lo `test -f 'utils/leak_detective.c' || echo '$(srcdir)/'`utils/leak_detective.c + +lexparser.lo: utils/lexparser.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lexparser.lo -MD -MP -MF "$(DEPDIR)/lexparser.Tpo" -c -o lexparser.lo `test -f 'utils/lexparser.c' || echo '$(srcdir)/'`utils/lexparser.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/lexparser.Tpo" "$(DEPDIR)/lexparser.Plo"; else rm -f "$(DEPDIR)/lexparser.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/lexparser.c' object='lexparser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lexparser.lo `test -f 'utils/lexparser.c' || echo '$(srcdir)/'`utils/lexparser.c + +linked_list.lo: utils/linked_list.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT linked_list.lo -MD -MP -MF "$(DEPDIR)/linked_list.Tpo" -c -o linked_list.lo `test -f 'utils/linked_list.c' || echo '$(srcdir)/'`utils/linked_list.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/linked_list.Tpo" "$(DEPDIR)/linked_list.Plo"; else rm -f "$(DEPDIR)/linked_list.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/linked_list.c' object='linked_list.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o linked_list.lo `test -f 'utils/linked_list.c' || echo '$(srcdir)/'`utils/linked_list.c + +randomizer.lo: utils/randomizer.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT randomizer.lo -MD -MP -MF "$(DEPDIR)/randomizer.Tpo" -c -o randomizer.lo `test -f 'utils/randomizer.c' || echo '$(srcdir)/'`utils/randomizer.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/randomizer.Tpo" "$(DEPDIR)/randomizer.Plo"; else rm -f "$(DEPDIR)/randomizer.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils/randomizer.c' object='randomizer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o randomizer.lo `test -f 'utils/randomizer.c' || echo '$(srcdir)/'`utils/randomizer.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(mkdir_p) $(distdir)/asn1 + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-libLTLIBRARIES + + +asn1/oid.c : asn1/oid.txt asn1/oid.pl + cd asn1 && $(PERL) oid.pl + +asn1/oid.h : asn1/oid.txt asn1/oid.pl + cd asn1 && $(PERL) oid.pl +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c new file mode 100644 index 000000000..91a6621d4 --- /dev/null +++ b/src/libstrongswan/asn1/asn1.c @@ -0,0 +1,733 @@ +/* Simple ASN.1 parser + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * Copyright (C) 2006 Martin Will, Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include + +#include "asn1.h" + +#include +#include + +/* some common prefabricated ASN.1 constants */ +static u_char ASN1_INTEGER_0_str[] = { 0x02, 0x00 }; +static u_char ASN1_INTEGER_1_str[] = { 0x02, 0x01, 0x01 }; +static u_char ASN1_INTEGER_2_str[] = { 0x02, 0x01, 0x02 }; + +const chunk_t ASN1_INTEGER_0 = chunk_from_buf(ASN1_INTEGER_0_str); +const chunk_t ASN1_INTEGER_1 = chunk_from_buf(ASN1_INTEGER_1_str); +const chunk_t ASN1_INTEGER_2 = chunk_from_buf(ASN1_INTEGER_2_str); + +/* some popular algorithmIdentifiers */ + +static u_char ASN1_md5_id_str[] = { + 0x30, 0x0C, + 0x06, 0x08, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, + 0x05, 0x00 +}; + +static u_char ASN1_sha1_id_str[] = { + 0x30, 0x09, + 0x06, 0x05, + 0x2B, 0x0E,0x03, 0x02, 0x1A, + 0x05, 0x00 +}; + +static u_char ASN1_md5WithRSA_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, + 0x05, 0x00 +}; + +static u_char ASN1_sha1WithRSA_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, + 0x05, 0x00 +}; + +static u_char ASN1_rsaEncryption_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00 +}; + +const chunk_t ASN1_md5_id = chunk_from_buf(ASN1_md5_id_str); +const chunk_t ASN1_sha1_id = chunk_from_buf(ASN1_sha1_id_str); +const chunk_t ASN1_rsaEncryption_id = chunk_from_buf(ASN1_rsaEncryption_id_str); +const chunk_t ASN1_md5WithRSA_id = chunk_from_buf(ASN1_md5WithRSA_id_str); +const chunk_t ASN1_sha1WithRSA_id = chunk_from_buf(ASN1_sha1WithRSA_id_str); + +/* ASN.1 definiton of an algorithmIdentifier */ +static const asn1Object_t algorithmIdentifierObjects[] = { + { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "parameters", ASN1_EOC, ASN1_RAW } /* 2 */ +}; + +#define ALGORITHM_ID_ALG 1 +#define ALGORITHM_ID_PARAMETERS 2 +#define ALGORITHM_ID_ROOF 3 + +/** + * return the ASN.1 encoded algorithm identifier + */ +chunk_t asn1_algorithmIdentifier(int oid) +{ + switch (oid) + { + case OID_RSA_ENCRYPTION: + return ASN1_rsaEncryption_id; + case OID_MD5_WITH_RSA: + return ASN1_md5WithRSA_id; + case OID_SHA1_WITH_RSA: + return ASN1_sha1WithRSA_id; + case OID_MD5: + return ASN1_md5_id; + case OID_SHA1: + return ASN1_sha1_id; + default: + return chunk_empty; + } +} + +/** + * If the oid is listed in the oid_names table then the corresponding + * position in the oid_names table is returned otherwise -1 is returned + */ +int known_oid(chunk_t object) +{ + int oid = 0; + + while (object.len) + { + if (oid_names[oid].octet == *object.ptr) + { + if (--object.len == 0 || oid_names[oid].down == 0) + { + return oid; /* found terminal symbol */ + } + else + { + object.ptr++; oid++; /* advance to next hex octet */ + } + } + else + { + if (oid_names[oid].next) + oid = oid_names[oid].next; + else + return OID_UNKNOWN; + } + } + return -1; +} + +/** + * Decodes the length in bytes of an ASN.1 object + */ +u_int asn1_length(chunk_t *blob) +{ + u_char n; + size_t len; + + /* advance from tag field on to length field */ + blob->ptr++; + blob->len--; + + /* read first octet of length field */ + n = *blob->ptr++; + blob->len--; + + if ((n & 0x80) == 0) + {/* single length octet */ + return n; + } + + /* composite length, determine number of length octets */ + n &= 0x7f; + + if (n > blob->len) + { + DBG2("number of length octets is larger than ASN.1 object"); + return ASN1_INVALID_LENGTH; + } + + if (n > sizeof(len)) + { + DBG2("number of length octets is larger than limit of %d octets", + (int)sizeof(len)); + return ASN1_INVALID_LENGTH; + } + + len = 0; + + while (n-- > 0) + { + len = 256*len + *blob->ptr++; + blob->len--; + } + return len; +} + +/** + * determines if a character string is of type ASN.1 printableString + */ +bool is_printablestring(chunk_t str) +{ + const char printablestring_charset[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?"; + u_int i; + + for (i = 0; i < str.len; i++) + { + if (strchr(printablestring_charset, str.ptr[i]) == NULL) + return FALSE; + } + return TRUE; +} + +/** + * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time + */ +time_t asn1totime(const chunk_t *utctime, asn1_t type) +{ + struct tm t; + time_t tz_offset; + u_char *eot = NULL; + + if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL) + { + tz_offset = 0; /* Zulu time with a zero time zone offset */ + } + else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL) + { + int tz_hour, tz_min; + + sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min); + tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */ + } + else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL) + { + int tz_hour, tz_min; + + sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min); + tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */ + } + else + { + return 0; /* error in time format */ + } + + { + const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d": + "%4d%2d%2d%2d%2d"; + + sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday, + &t.tm_hour, &t.tm_min); + } + + /* is there a seconds field? */ + if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14)) + { + sscanf(eot-2, "%2d", &t.tm_sec); + } + else + { + t.tm_sec = 0; + } + + /* representation of year */ + if (t.tm_year >= 1900) + { + t.tm_year -= 1900; + } + else if (t.tm_year >= 100) + { + return 0; + } + else if (t.tm_year < 50) + { + t.tm_year += 100; + } + + /* representation of month 0..11*/ + t.tm_mon--; + + /* set daylight saving time to off */ + t.tm_isdst = 0; + + /* compensate timezone */ + + return mktime(&t) - timezone - tz_offset; +} + +/** + * Initializes the internal context of the ASN.1 parser + */ +void asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0, + bool implicit, bool private) +{ + ctx->blobs[0] = blob; + ctx->level0 = level0; + ctx->implicit = implicit; + ctx->private = private; + memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr)); +} + +/** + * print the value of an ASN.1 simple object + */ +static void debug_asn1_simple_object(chunk_t object, asn1_t type, bool private) +{ + int oid; + + switch (type) + { + case ASN1_OID: + oid = known_oid(object); + if (oid != OID_UNKNOWN) + { + DBG2(" '%s'", oid_names[oid].name); + return; + } + break; + case ASN1_UTF8STRING: + case ASN1_IA5STRING: + case ASN1_PRINTABLESTRING: + case ASN1_T61STRING: + case ASN1_VISIBLESTRING: + DBG2(" '%.*s'", (int)object.len, object.ptr); + return; + case ASN1_UTCTIME: + case ASN1_GENERALIZEDTIME: + { + time_t time = asn1totime(&object, type); + + DBG2(" '%T'", &time); + } + return; + default: + break; + } + if (private) + { + DBG4("%B", &object); + } + else + { + DBG3("%B", &object); + } +} + +/** + * Parses and extracts the next ASN.1 object + */ +bool extract_object(asn1Object_t const *objects, u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx) +{ + asn1Object_t obj = objects[*objectID]; + chunk_t *blob; + chunk_t *blob1; + u_char *start_ptr; + + *object = chunk_empty; + + if (obj.flags & ASN1_END) /* end of loop or option found */ + { + if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0) + { + *objectID = ctx->loopAddr[obj.level]; /* another iteration */ + obj = objects[*objectID]; + } + else + { + ctx->loopAddr[obj.level] = 0; /* exit loop or option*/ + return TRUE; + } + } + + *level = ctx->level0 + obj.level; + blob = ctx->blobs + obj.level; + blob1 = blob + 1; + start_ptr = blob->ptr; + + /* handle ASN.1 defaults values */ + if ((obj.flags & ASN1_DEF) && (blob->len == 0 || *start_ptr != obj.type) ) + { + /* field is missing */ + DBG2("L%d - %s:", *level, obj.name); + if (obj.type & ASN1_CONSTRUCTED) + { + (*objectID)++ ; /* skip context-specific tag */ + } + return TRUE; + } + + /* handle ASN.1 options */ + + if ((obj.flags & ASN1_OPT) + && (blob->len == 0 || *start_ptr != obj.type)) + { + /* advance to end of missing option field */ + do + (*objectID)++; + while (!((objects[*objectID].flags & ASN1_END) + && (objects[*objectID].level == obj.level))); + return TRUE; + } + + /* an ASN.1 object must possess at least a tag and length field */ + + if (blob->len < 2) + { + DBG2("L%d - %s: ASN.1 object smaller than 2 octets", + *level, obj.name); + return FALSE; + } + + blob1->len = asn1_length(blob); + + if (blob1->len == ASN1_INVALID_LENGTH || blob->len < blob1->len) + { + DBG2("L%d - %s: length of ASN.1 object invalid or too large", + *level, obj.name); + return FALSE; + } + + blob1->ptr = blob->ptr; + blob->ptr += blob1->len; + blob->len -= blob1->len; + + /* return raw ASN.1 object without prior type checking */ + + if (obj.flags & ASN1_RAW) + { + DBG2("L%d - %s:", *level, obj.name); + object->ptr = start_ptr; + object->len = (size_t)(blob->ptr - start_ptr); + return TRUE; + } + + if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0)) + { + DBG1("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + *level, obj.name, obj.type, *start_ptr); + DBG3("%b", start_ptr, (u_int)(blob->ptr - start_ptr)); + return FALSE; + } + + DBG2("L%d - %s:", ctx->level0+obj.level, obj.name); + + /* In case of "SEQUENCE OF" or "SET OF" start a loop */ + if (obj.flags & ASN1_LOOP) + { + if (blob1->len > 0) + { + /* at least one item, start the loop */ + ctx->loopAddr[obj.level] = *objectID + 1; + } + else + { + /* no items, advance directly to end of loop */ + do + (*objectID)++; + while (!((objects[*objectID].flags & ASN1_END) + && (objects[*objectID].level == obj.level))); + return TRUE; + } + } + + if (obj.flags & ASN1_OBJ) + { + object->ptr = start_ptr; + object->len = (size_t)(blob->ptr - start_ptr); + if (ctx->private) + { + DBG4("%B", object); + } + else + { + DBG3("%B", object); + } + } + else if (obj.flags & ASN1_BODY) + { + *object = *blob1; + debug_asn1_simple_object(*object, obj.type, ctx->private); + } + return TRUE; +} + +/** + * parse an ASN.1 simple type + */ +bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name) +{ + size_t len; + + /* an ASN.1 object must possess at least a tag and length field */ + if (object->len < 2) + { + DBG2("L%d - %s: ASN.1 object smaller than 2 octets", level, name); + return FALSE; + } + + if (*object->ptr != type) + { + DBG2("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + level, name, type, *object->ptr); + return FALSE; + } + + len = asn1_length(object); + + if (len == ASN1_INVALID_LENGTH || object->len < len) + { + DBG2("L%d - %s: length of ASN.1 object invalid or too large", + level, name); + return FALSE; + } + + DBG2("L%d - %s:", level, name); + debug_asn1_simple_object(*object, type, FALSE); + return TRUE; +} + +/** + * extracts an algorithmIdentifier + */ +int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int alg = OID_UNKNOWN; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < ALGORITHM_ID_ROOF) + { + if (!extract_object(algorithmIdentifierObjects, &objectID, &object, &level, &ctx)) + return OID_UNKNOWN; + + switch (objectID) + { + case ALGORITHM_ID_ALG: + alg = known_oid(object); + break; + case ALGORITHM_ID_PARAMETERS: + if (parameters != NULL) + *parameters = object; + break; + default: + break; + } + objectID++; + } + return alg; + } + +/* + * tests if a blob contains a valid ASN.1 set or sequence + */ +bool is_asn1(chunk_t blob) +{ + u_int len; + u_char tag = *blob.ptr; + + if (tag != ASN1_SEQUENCE && tag != ASN1_SET) + { + DBG2(" file content is not binary ASN.1"); + return FALSE; + } + len = asn1_length(&blob); + if (len != blob.len) + { + DBG2(" file size does not match ASN.1 coded length"); + return FALSE; + } + return TRUE; +} + +/** + * codes ASN.1 lengths up to a size of 16'777'215 bytes + */ +void code_asn1_length(size_t length, chunk_t *code) +{ + if (length < 128) + { + code->ptr[0] = length; + code->len = 1; + } + else if (length < 256) + { + code->ptr[0] = 0x81; + code->ptr[1] = (u_char) length; + code->len = 2; + } + else if (length < 65536) + { + code->ptr[0] = 0x82; + code->ptr[1] = length >> 8; + code->ptr[2] = length & 0x00ff; + code->len = 3; + } + else + { + code->ptr[0] = 0x83; + code->ptr[1] = length >> 16; + code->ptr[2] = (length >> 8) & 0x00ff; + code->ptr[3] = length & 0x0000ff; + code->len = 4; + } +} + +/** + * build an empty asn.1 object with tag and length fields already filled in + */ +u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen) +{ + u_char length_buf[4]; + chunk_t length = { length_buf, 0 }; + u_char *pos; + + /* code the asn.1 length field */ + code_asn1_length(datalen, &length); + + /* allocate memory for the asn.1 TLV object */ + object->len = 1 + length.len + datalen; + object->ptr = malloc(object->len); + + /* set position pointer at the start of the object */ + pos = object->ptr; + + /* copy the asn.1 tag field and advance the pointer */ + *pos++ = type; + + /* copy the asn.1 length field and advance the pointer */ + memcpy(pos, length.ptr, length.len); + pos += length.len; + + return pos; +} + +/** + * build a simple ASN.1 object + */ +chunk_t asn1_simple_object(asn1_t tag, chunk_t content) +{ + chunk_t object; + + u_char *pos = build_asn1_object(&object, tag, content.len); + memcpy(pos, content.ptr, content.len); + pos += content.len; + + return object; +} + +/** + * Build an ASN.1 object from a variable number of individual chunks. + * Depending on the mode, chunks either are moved ('m') or copied ('c'). + */ +chunk_t asn1_wrap(asn1_t type, const char *mode, ...) +{ + chunk_t construct; + va_list chunks; + u_char *pos; + int i; + int count = strlen(mode); + + /* sum up lengths of individual chunks */ + va_start(chunks, mode); + construct.len = 0; + for (i = 0; i < count; i++) + { + chunk_t ch = va_arg(chunks, chunk_t); + construct.len += ch.len; + } + va_end(chunks); + + /* allocate needed memory for construct */ + pos = build_asn1_object(&construct, type, construct.len); + + /* copy or move the chunks */ + va_start(chunks, mode); + for (i = 0; i < count; i++) + { + chunk_t ch = va_arg(chunks, chunk_t); + + switch (*mode++) + { + case 'm': + memcpy(pos, ch.ptr, ch.len); + pos += ch.len; + free(ch.ptr); + break; + case 'c': + default: + memcpy(pos, ch.ptr, ch.len); + pos += ch.len; + } + } + va_end(chunks); + + return construct; +} + +/** + * convert a MP integer into a DER coded ASN.1 object + */ +chunk_t asn1_integer_from_mpz(const mpz_t value) +{ + size_t bits = mpz_sizeinbase(value, 2); /* size in bits */ + chunk_t n; + n.len = 1 + bits / 8; /* size in bytes */ + n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, value); + + return asn1_wrap(ASN1_INTEGER, "m", n); +} + +/** + * convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format + */ +chunk_t timetoasn1(const time_t *time, asn1_t type) +{ + int offset; + const char *format; + char buf[32]; + chunk_t formatted_time; + struct tm *t = gmtime(time); + + if (type == ASN1_GENERALIZEDTIME) + { + format = "%04d%02d%02d%02d%02d%02dZ"; + offset = 1900; + } + else /* ASN1_UTCTIME */ + { + format = "%02d%02d%02d%02d%02d%02dZ"; + offset = (t->tm_year < 100)? 0 : -100; + } + snprintf(buf, sizeof(buf), format, t->tm_year + offset, + t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + formatted_time.ptr = buf; + formatted_time.len = strlen(buf); + return asn1_simple_object(type, formatted_time); +} diff --git a/src/libstrongswan/asn1/asn1.h b/src/libstrongswan/asn1/asn1.h new file mode 100644 index 000000000..5ab519ec8 --- /dev/null +++ b/src/libstrongswan/asn1/asn1.h @@ -0,0 +1,135 @@ +/* Simple ASN.1 parser + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * Copyright (C) 2006 Martin Will, Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef _ASN1_H +#define _ASN1_H + +#include +#include + +#include +#include + + +/* Defines some primitive ASN1 types */ +typedef enum { + ASN1_EOC = 0x00, + ASN1_BOOLEAN = 0x01, + ASN1_INTEGER = 0x02, + ASN1_BIT_STRING = 0x03, + ASN1_OCTET_STRING = 0x04, + ASN1_NULL = 0x05, + ASN1_OID = 0x06, + ASN1_ENUMERATED = 0x0A, + ASN1_UTF8STRING = 0x0C, + ASN1_NUMERICSTRING = 0x12, + ASN1_PRINTABLESTRING = 0x13, + ASN1_T61STRING = 0x14, + ASN1_VIDEOTEXSTRING = 0x15, + ASN1_IA5STRING = 0x16, + ASN1_UTCTIME = 0x17, + ASN1_GENERALIZEDTIME = 0x18, + ASN1_GRAPHICSTRING = 0x19, + ASN1_VISIBLESTRING = 0x1A, + ASN1_GENERALSTRING = 0x1B, + ASN1_UNIVERSALSTRING = 0x1C, + ASN1_BMPSTRING = 0x1E, + + ASN1_CONSTRUCTED = 0x20, + + ASN1_SEQUENCE = 0x30, + + ASN1_SET = 0x31, + + ASN1_CONTEXT_S_0 = 0x80, + ASN1_CONTEXT_S_1 = 0x81, + ASN1_CONTEXT_S_2 = 0x82, + ASN1_CONTEXT_S_3 = 0x83, + ASN1_CONTEXT_S_4 = 0x84, + ASN1_CONTEXT_S_5 = 0x85, + ASN1_CONTEXT_S_6 = 0x86, + ASN1_CONTEXT_S_7 = 0x87, + ASN1_CONTEXT_S_8 = 0x88, + + ASN1_CONTEXT_C_0 = 0xA0, + ASN1_CONTEXT_C_1 = 0xA1, + ASN1_CONTEXT_C_2 = 0xA2, + ASN1_CONTEXT_C_3 = 0xA3, + ASN1_CONTEXT_C_4 = 0xA4, + ASN1_CONTEXT_C_5 = 0xA5 +} asn1_t; + +/* Definition of ASN1 flags */ + +#define ASN1_NONE 0x00 +#define ASN1_DEF 0x01 +#define ASN1_OPT 0x02 +#define ASN1_LOOP 0x04 +#define ASN1_END 0x08 +#define ASN1_OBJ 0x10 +#define ASN1_BODY 0x20 +#define ASN1_RAW 0x40 + +#define ASN1_INVALID_LENGTH 0xffffffff + +/* definition of an ASN.1 object */ + +typedef struct { + u_int level; + const u_char *name; + asn1_t type; + u_char flags; +} asn1Object_t; + +#define ASN1_MAX_LEVEL 10 + +typedef struct { + bool implicit; + bool private; + u_int level0; + u_int loopAddr[ASN1_MAX_LEVEL+1]; + chunk_t blobs[ASN1_MAX_LEVEL+2]; +} asn1_ctx_t; + +/* some common prefabricated ASN.1 constants */ +extern const chunk_t ASN1_INTEGER_0; +extern const chunk_t ASN1_INTEGER_1; +extern const chunk_t ASN1_INTEGER_2; + +/* some popular algorithmIdentifiers */ +extern const chunk_t ASN1_md5_id; +extern const chunk_t ASN1_sha1_id; +extern const chunk_t ASN1_rsaEncryption_id; +extern const chunk_t ASN1_md5WithRSA_id; +extern const chunk_t ASN1_sha1WithRSA_id; + +extern chunk_t asn1_algorithmIdentifier(int oid); +extern int known_oid(chunk_t object); +extern u_int asn1_length(chunk_t *blob); +extern bool is_printablestring(chunk_t str); +extern time_t asn1totime(const chunk_t *utctime, asn1_t type); +extern void asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0, bool implicit, bool private); +extern bool extract_object(asn1Object_t const *objects, u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx); +extern bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level, const char* name); +extern int parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters); +extern bool is_asn1(chunk_t blob); + +extern void code_asn1_length(size_t length, chunk_t *code); +extern u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen); +extern chunk_t asn1_integer_from_mpz(const mpz_t value); +extern chunk_t asn1_simple_object(asn1_t tag, chunk_t content); +extern chunk_t asn1_wrap(asn1_t type, const char *mode, ...); + +#endif /* _ASN1_H */ diff --git a/src/libstrongswan/asn1/oid.c b/src/libstrongswan/asn1/oid.c new file mode 100644 index 000000000..4b0632de2 --- /dev/null +++ b/src/libstrongswan/asn1/oid.c @@ -0,0 +1,197 @@ +/* List of some useful object identifiers (OIDs) + * Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This file has been automatically generated by the script oid.pl + * Do not edit manually! + */ + +#include + +#include "oid.h" + +const oid_t oid_names[] = { + {0x02, 7, 1, "ITU-T Administration" }, /* 0 */ + { 0x82, 0, 1, "" }, /* 1 */ + { 0x06, 0, 1, "Germany ITU-T member" }, /* 2 */ + { 0x01, 0, 1, "Deutsche Telekom AG" }, /* 3 */ + { 0x0A, 0, 1, "" }, /* 4 */ + { 0x07, 0, 1, "" }, /* 5 */ + { 0x14, 0, 0, "ND" }, /* 6 */ + {0x09, 18, 1, "data" }, /* 7 */ + { 0x92, 0, 1, "" }, /* 8 */ + { 0x26, 0, 1, "" }, /* 9 */ + { 0x89, 0, 1, "" }, /* 10 */ + { 0x93, 0, 1, "" }, /* 11 */ + { 0xF2, 0, 1, "" }, /* 12 */ + { 0x2C, 0, 1, "" }, /* 13 */ + { 0x64, 0, 1, "pilot" }, /* 14 */ + { 0x01, 0, 1, "pilotAttributeType" }, /* 15 */ + { 0x01, 17, 0, "UID" }, /* 16 */ + { 0x19, 0, 0, "DC" }, /* 17 */ + {0x55, 51, 1, "X.500" }, /* 18 */ + { 0x04, 36, 1, "X.509" }, /* 19 */ + { 0x03, 21, 0, "CN" }, /* 20 */ + { 0x04, 22, 0, "S" }, /* 21 */ + { 0x05, 23, 0, "SN" }, /* 22 */ + { 0x06, 24, 0, "C" }, /* 23 */ + { 0x07, 25, 0, "L" }, /* 24 */ + { 0x08, 26, 0, "ST" }, /* 25 */ + { 0x0A, 27, 0, "O" }, /* 26 */ + { 0x0B, 28, 0, "OU" }, /* 27 */ + { 0x0C, 29, 0, "T" }, /* 28 */ + { 0x0D, 30, 0, "D" }, /* 29 */ + { 0x24, 31, 0, "userCertificate" }, /* 30 */ + { 0x29, 32, 0, "N" }, /* 31 */ + { 0x2A, 33, 0, "G" }, /* 32 */ + { 0x2B, 34, 0, "I" }, /* 33 */ + { 0x2D, 35, 0, "ID" }, /* 34 */ + { 0x48, 0, 0, "role" }, /* 35 */ + { 0x1D, 0, 1, "id-ce" }, /* 36 */ + { 0x09, 38, 0, "subjectDirectoryAttrs" }, /* 37 */ + { 0x0E, 39, 0, "subjectKeyIdentifier" }, /* 38 */ + { 0x0F, 40, 0, "keyUsage" }, /* 39 */ + { 0x10, 41, 0, "privateKeyUsagePeriod" }, /* 40 */ + { 0x11, 42, 0, "subjectAltName" }, /* 41 */ + { 0x12, 43, 0, "issuerAltName" }, /* 42 */ + { 0x13, 44, 0, "basicConstraints" }, /* 43 */ + { 0x15, 45, 0, "reasonCode" }, /* 44 */ + { 0x1F, 46, 0, "crlDistributionPoints" }, /* 45 */ + { 0x20, 47, 0, "certificatePolicies" }, /* 46 */ + { 0x23, 48, 0, "authorityKeyIdentifier" }, /* 47 */ + { 0x25, 49, 0, "extendedKeyUsage" }, /* 48 */ + { 0x37, 50, 0, "targetInformation" }, /* 49 */ + { 0x38, 0, 0, "noRevAvail" }, /* 50 */ + {0x2A, 88, 1, "" }, /* 51 */ + { 0x86, 0, 1, "" }, /* 52 */ + { 0x48, 0, 1, "" }, /* 53 */ + { 0x86, 0, 1, "" }, /* 54 */ + { 0xF7, 0, 1, "" }, /* 55 */ + { 0x0D, 0, 1, "RSADSI" }, /* 56 */ + { 0x01, 83, 1, "PKCS" }, /* 57 */ + { 0x01, 66, 1, "PKCS-1" }, /* 58 */ + { 0x01, 60, 0, "rsaEncryption" }, /* 59 */ + { 0x02, 61, 0, "md2WithRSAEncryption" }, /* 60 */ + { 0x04, 62, 0, "md5WithRSAEncryption" }, /* 61 */ + { 0x05, 63, 0, "sha-1WithRSAEncryption" }, /* 62 */ + { 0x0B, 64, 0, "sha256WithRSAEncryption"}, /* 63 */ + { 0x0C, 65, 0, "sha384WithRSAEncryption"}, /* 64 */ + { 0x0D, 0, 0, "sha512WithRSAEncryption"}, /* 65 */ + { 0x07, 73, 1, "PKCS-7" }, /* 66 */ + { 0x01, 68, 0, "data" }, /* 67 */ + { 0x02, 69, 0, "signedData" }, /* 68 */ + { 0x03, 70, 0, "envelopedData" }, /* 69 */ + { 0x04, 71, 0, "signedAndEnvelopedData" }, /* 70 */ + { 0x05, 72, 0, "digestedData" }, /* 71 */ + { 0x06, 0, 0, "encryptedData" }, /* 72 */ + { 0x09, 0, 1, "PKCS-9" }, /* 73 */ + { 0x01, 75, 0, "E" }, /* 74 */ + { 0x02, 76, 0, "unstructuredName" }, /* 75 */ + { 0x03, 77, 0, "contentType" }, /* 76 */ + { 0x04, 78, 0, "messageDigest" }, /* 77 */ + { 0x05, 79, 0, "signingTime" }, /* 78 */ + { 0x06, 80, 0, "counterSignature" }, /* 79 */ + { 0x07, 81, 0, "challengePassword" }, /* 80 */ + { 0x08, 82, 0, "unstructuredAddress" }, /* 81 */ + { 0x0E, 0, 0, "extensionRequest" }, /* 82 */ + { 0x02, 86, 1, "digestAlgorithm" }, /* 83 */ + { 0x02, 85, 0, "md2" }, /* 84 */ + { 0x05, 0, 0, "md5" }, /* 85 */ + { 0x03, 0, 1, "encryptionAlgorithm" }, /* 86 */ + { 0x07, 0, 0, "3des-ede-cbc" }, /* 87 */ + {0x2B, 149, 1, "" }, /* 88 */ + { 0x06, 136, 1, "dod" }, /* 89 */ + { 0x01, 0, 1, "internet" }, /* 90 */ + { 0x04, 105, 1, "private" }, /* 91 */ + { 0x01, 0, 1, "enterprise" }, /* 92 */ + { 0x82, 98, 1, "" }, /* 93 */ + { 0x37, 0, 1, "Microsoft" }, /* 94 */ + { 0x0A, 0, 1, "" }, /* 95 */ + { 0x03, 0, 1, "" }, /* 96 */ + { 0x03, 0, 0, "msSGC" }, /* 97 */ + { 0x89, 0, 1, "" }, /* 98 */ + { 0x31, 0, 1, "" }, /* 99 */ + { 0x01, 0, 1, "" }, /* 100 */ + { 0x01, 0, 1, "" }, /* 101 */ + { 0x02, 0, 1, "" }, /* 102 */ + { 0x02, 104, 0, "" }, /* 103 */ + { 0x4B, 0, 0, "TCGID" }, /* 104 */ + { 0x05, 0, 1, "security" }, /* 105 */ + { 0x05, 0, 1, "mechanisms" }, /* 106 */ + { 0x07, 0, 1, "id-pkix" }, /* 107 */ + { 0x01, 110, 1, "id-pe" }, /* 108 */ + { 0x01, 0, 0, "authorityInfoAccess" }, /* 109 */ + { 0x03, 120, 1, "id-kp" }, /* 110 */ + { 0x01, 112, 0, "serverAuth" }, /* 111 */ + { 0x02, 113, 0, "clientAuth" }, /* 112 */ + { 0x03, 114, 0, "codeSigning" }, /* 113 */ + { 0x04, 115, 0, "emailProtection" }, /* 114 */ + { 0x05, 116, 0, "ipsecEndSystem" }, /* 115 */ + { 0x06, 117, 0, "ipsecTunnel" }, /* 116 */ + { 0x07, 118, 0, "ipsecUser" }, /* 117 */ + { 0x08, 119, 0, "timeStamping" }, /* 118 */ + { 0x09, 0, 0, "ocspSigning" }, /* 119 */ + { 0x08, 122, 1, "id-otherNames" }, /* 120 */ + { 0x05, 0, 0, "xmppAddr" }, /* 121 */ + { 0x0A, 127, 1, "id-aca" }, /* 122 */ + { 0x01, 124, 0, "authenticationInfo" }, /* 123 */ + { 0x02, 125, 0, "accessIdentity" }, /* 124 */ + { 0x03, 126, 0, "chargingIdentity" }, /* 125 */ + { 0x04, 0, 0, "group" }, /* 126 */ + { 0x30, 0, 1, "id-ad" }, /* 127 */ + { 0x01, 0, 1, "ocsp" }, /* 128 */ + { 0x01, 130, 0, "basic" }, /* 129 */ + { 0x02, 131, 0, "nonce" }, /* 130 */ + { 0x03, 132, 0, "crl" }, /* 131 */ + { 0x04, 133, 0, "response" }, /* 132 */ + { 0x05, 134, 0, "noCheck" }, /* 133 */ + { 0x06, 135, 0, "archiveCutoff" }, /* 134 */ + { 0x07, 0, 0, "serviceLocator" }, /* 135 */ + { 0x0E, 142, 1, "oiw" }, /* 136 */ + { 0x03, 0, 1, "secsig" }, /* 137 */ + { 0x02, 0, 1, "algorithms" }, /* 138 */ + { 0x07, 140, 0, "des-cbc" }, /* 139 */ + { 0x1A, 141, 0, "sha-1" }, /* 140 */ + { 0x1D, 0, 0, "sha-1WithRSASignature" }, /* 141 */ + { 0x24, 0, 1, "TeleTrusT" }, /* 142 */ + { 0x03, 0, 1, "algorithm" }, /* 143 */ + { 0x03, 0, 1, "signatureAlgorithm" }, /* 144 */ + { 0x01, 0, 1, "rsaSignature" }, /* 145 */ + { 0x02, 147, 0, "rsaSigWithripemd160" }, /* 146 */ + { 0x03, 148, 0, "rsaSigWithripemd128" }, /* 147 */ + { 0x04, 0, 0, "rsaSigWithripemd256" }, /* 148 */ + {0x60, 0, 1, "" }, /* 149 */ + { 0x86, 0, 1, "" }, /* 150 */ + { 0x48, 0, 1, "" }, /* 151 */ + { 0x01, 0, 1, "organization" }, /* 152 */ + { 0x65, 160, 1, "gov" }, /* 153 */ + { 0x03, 0, 1, "csor" }, /* 154 */ + { 0x04, 0, 1, "nistalgorithm" }, /* 155 */ + { 0x02, 0, 1, "hashalgs" }, /* 156 */ + { 0x01, 158, 0, "id-SHA-256" }, /* 157 */ + { 0x02, 159, 0, "id-SHA-384" }, /* 158 */ + { 0x03, 0, 0, "id-SHA-512" }, /* 159 */ + { 0x86, 0, 1, "" }, /* 160 */ + { 0xf8, 0, 1, "" }, /* 161 */ + { 0x42, 174, 1, "netscape" }, /* 162 */ + { 0x01, 169, 1, "" }, /* 163 */ + { 0x01, 165, 0, "nsCertType" }, /* 164 */ + { 0x03, 166, 0, "nsRevocationUrl" }, /* 165 */ + { 0x04, 167, 0, "nsCaRevocationUrl" }, /* 166 */ + { 0x08, 168, 0, "nsCaPolicyUrl" }, /* 167 */ + { 0x0d, 0, 0, "nsComment" }, /* 168 */ + { 0x03, 172, 1, "directory" }, /* 169 */ + { 0x01, 0, 1, "" }, /* 170 */ + { 0x03, 0, 0, "employeeNumber" }, /* 171 */ + { 0x04, 0, 1, "policy" }, /* 172 */ + { 0x01, 0, 0, "nsSGC" }, /* 173 */ + { 0x45, 0, 1, "verisign" }, /* 174 */ + { 0x01, 0, 1, "pki" }, /* 175 */ + { 0x09, 0, 1, "attributes" }, /* 176 */ + { 0x02, 178, 0, "messageType" }, /* 177 */ + { 0x03, 179, 0, "pkiStatus" }, /* 178 */ + { 0x04, 180, 0, "failInfo" }, /* 179 */ + { 0x05, 181, 0, "senderNonce" }, /* 180 */ + { 0x06, 182, 0, "recipientNonce" }, /* 181 */ + { 0x07, 183, 0, "transID" }, /* 182 */ + { 0x08, 0, 0, "extensionReq" } /* 183 */ +}; diff --git a/src/libstrongswan/asn1/oid.h b/src/libstrongswan/asn1/oid.h new file mode 100644 index 000000000..f85997159 --- /dev/null +++ b/src/libstrongswan/asn1/oid.h @@ -0,0 +1,80 @@ +/* Object identifiers (OIDs) used by FreeS/WAN + * Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This file has been automatically generated by the script oid.pl + * Do not edit manually! + */ + +#ifndef OID_H_ +#define OID_H_ + +typedef struct { + u_char octet; + u_int next; + u_int down; + const u_char *name; +} oid_t; + +extern const oid_t oid_names[]; + +#define OID_UNKNOWN -1 +#define OID_ROLE 35 +#define OID_SUBJECT_KEY_ID 38 +#define OID_SUBJECT_ALT_NAME 41 +#define OID_BASIC_CONSTRAINTS 43 +#define OID_CRL_REASON_CODE 44 +#define OID_CRL_DISTRIBUTION_POINTS 45 +#define OID_AUTHORITY_KEY_ID 47 +#define OID_EXTENDED_KEY_USAGE 48 +#define OID_TARGET_INFORMATION 49 +#define OID_NO_REV_AVAIL 50 +#define OID_RSA_ENCRYPTION 59 +#define OID_MD2_WITH_RSA 60 +#define OID_MD5_WITH_RSA 61 +#define OID_SHA1_WITH_RSA 62 +#define OID_SHA256_WITH_RSA 63 +#define OID_SHA384_WITH_RSA 64 +#define OID_SHA512_WITH_RSA 65 +#define OID_PKCS7_DATA 67 +#define OID_PKCS7_SIGNED_DATA 68 +#define OID_PKCS7_ENVELOPED_DATA 69 +#define OID_PKCS7_SIGNED_ENVELOPED_DATA 70 +#define OID_PKCS7_DIGESTED_DATA 71 +#define OID_PKCS7_ENCRYPTED_DATA 72 +#define OID_PKCS9_EMAIL 74 +#define OID_PKCS9_CONTENT_TYPE 76 +#define OID_PKCS9_MESSAGE_DIGEST 77 +#define OID_PKCS9_SIGNING_TIME 78 +#define OID_MD2 84 +#define OID_MD5 85 +#define OID_3DES_EDE_CBC 87 +#define OID_AUTHORITY_INFO_ACCESS 109 +#define OID_OCSP_SIGNING 119 +#define OID_XMPP_ADDR 121 +#define OID_AUTHENTICATION_INFO 123 +#define OID_ACCESS_IDENTITY 124 +#define OID_CHARGING_IDENTITY 125 +#define OID_GROUP 126 +#define OID_OCSP 128 +#define OID_BASIC 129 +#define OID_NONCE 130 +#define OID_CRL 131 +#define OID_RESPONSE 132 +#define OID_NO_CHECK 133 +#define OID_ARCHIVE_CUTOFF 134 +#define OID_SERVICE_LOCATOR 135 +#define OID_DES_CBC 139 +#define OID_SHA1 140 +#define OID_SHA1_WITH_RSA_OIW 141 +#define OID_NS_REVOCATION_URL 165 +#define OID_NS_CA_REVOCATION_URL 166 +#define OID_NS_CA_POLICY_URL 167 +#define OID_NS_COMMENT 168 +#define OID_PKI_MESSAGE_TYPE 177 +#define OID_PKI_STATUS 178 +#define OID_PKI_FAIL_INFO 179 +#define OID_PKI_SENDER_NONCE 180 +#define OID_PKI_RECIPIENT_NONCE 181 +#define OID_PKI_TRANS_ID 182 + +#endif /* OID_H_ */ diff --git a/src/libstrongswan/asn1/oid.pl b/src/libstrongswan/asn1/oid.pl new file mode 100644 index 000000000..5db619755 --- /dev/null +++ b/src/libstrongswan/asn1/oid.pl @@ -0,0 +1,127 @@ +#!/usr/bin/perl +# Generates oid.h and oid.c out of oid.txt +# Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur +# +# This program 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. See . +# +# 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 General Public License +# for more details. +# + +$copyright="Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur"; +$automatic="This file has been automatically generated by the script oid.pl"; +$warning="Do not edit manually!"; + +print "oid.pl generating oid.h and oid.c\n"; + +# Generate oid.h + +open(OID_H, ">oid.h") + or die "could not open 'oid.h': $!"; + +print OID_H "/* Object identifiers (OIDs) used by FreeS/WAN\n", + " * ", $copyright, "\n", + " * \n", + " * ", $automatic, "\n", + " * ", $warning, "\n", + " */\n\n", + "#ifndef OID_H_\n", + "#define OID_H_\n\n", + "typedef struct {\n", + " u_char octet;\n", + " u_int next;\n", + " u_int down;\n", + " const u_char *name;\n", + "} oid_t;\n", + "\n", + "extern const oid_t oid_names[];\n", + "\n", + "#define OID_UNKNOWN -1\n"; + +# parse oid.txt + +open(SRC, ") +{ + $line =~ m/( *?)(0x\w{2})\s+(".*?")[ \t]*?([\w_]*?)\Z/; + + @order[$counter] = length($1); + @octet[$counter] = $2; + @name[$counter] = $3; + + if (length($1) > $max_order) + { + $max_order = length($1); + } + if (length($3) > $max_name) + { + $max_name = length($3); + } + if (length($4) > 0) + { + printf OID_H "#define %s%s%d\n", $4, "\t" x ((39-length($4))/4), $counter; + } + $counter++; +} + +print OID_H "\n#endif /* OID_H_ */\n"; + +close SRC; +close OID_H; + +# Generate oid.c + +open(OID_C, ">oid.c") + or die "could not open 'oid.c': $!"; + +print OID_C "/* List of some useful object identifiers (OIDs)\n", + " * ", $copyright, "\n", + " * \n", + " * ", $automatic, "\n", + " * ", $warning, "\n", + " */\n", + "\n", + "#include \n", + "\n", + "#include \"oid.h\"\n", + "\n", + "const oid_t oid_names[] = {\n"; + +for ($c = 0; $c < $counter; $c++) +{ + $next = 0; + + for ($d = $c+1; $d < $counter && @order[$d] >= @order[$c]; $d++) + { + if (@order[$d] == @order[$c]) + { + @next[$c] = $d; + last; + } + } + + printf OID_C " {%s%s,%s%3d, %d, %s%s}%s /* %3d */\n" + ,' ' x @order[$c] + , @octet[$c] + , ' ' x (1 + $max_order - @order[$c]) + , @next[$c] + , @order[$c+1] > @order[$c] + , @name[$c] + , ' ' x ($max_name - length(@name[$c])) + , $c != $counter-1 ? "," : " " + , $c; +} + +print OID_C "};\n" ; +close OID_C; diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt new file mode 100644 index 000000000..eed46d59d --- /dev/null +++ b/src/libstrongswan/asn1/oid.txt @@ -0,0 +1,184 @@ +0x02 "ITU-T Administration" + 0x82 "" + 0x06 "Germany ITU-T member" + 0x01 "Deutsche Telekom AG" + 0x0A "" + 0x07 "" + 0x14 "ND" +0x09 "data" + 0x92 "" + 0x26 "" + 0x89 "" + 0x93 "" + 0xF2 "" + 0x2C "" + 0x64 "pilot" + 0x01 "pilotAttributeType" + 0x01 "UID" + 0x19 "DC" +0x55 "X.500" + 0x04 "X.509" + 0x03 "CN" + 0x04 "S" + 0x05 "SN" + 0x06 "C" + 0x07 "L" + 0x08 "ST" + 0x0A "O" + 0x0B "OU" + 0x0C "T" + 0x0D "D" + 0x24 "userCertificate" + 0x29 "N" + 0x2A "G" + 0x2B "I" + 0x2D "ID" + 0x48 "role" OID_ROLE + 0x1D "id-ce" + 0x09 "subjectDirectoryAttrs" + 0x0E "subjectKeyIdentifier" OID_SUBJECT_KEY_ID + 0x0F "keyUsage" + 0x10 "privateKeyUsagePeriod" + 0x11 "subjectAltName" OID_SUBJECT_ALT_NAME + 0x12 "issuerAltName" + 0x13 "basicConstraints" OID_BASIC_CONSTRAINTS + 0x15 "reasonCode" OID_CRL_REASON_CODE + 0x1F "crlDistributionPoints" OID_CRL_DISTRIBUTION_POINTS + 0x20 "certificatePolicies" + 0x23 "authorityKeyIdentifier" OID_AUTHORITY_KEY_ID + 0x25 "extendedKeyUsage" OID_EXTENDED_KEY_USAGE + 0x37 "targetInformation" OID_TARGET_INFORMATION + 0x38 "noRevAvail" OID_NO_REV_AVAIL +0x2A "" + 0x86 "" + 0x48 "" + 0x86 "" + 0xF7 "" + 0x0D "RSADSI" + 0x01 "PKCS" + 0x01 "PKCS-1" + 0x01 "rsaEncryption" OID_RSA_ENCRYPTION + 0x02 "md2WithRSAEncryption" OID_MD2_WITH_RSA + 0x04 "md5WithRSAEncryption" OID_MD5_WITH_RSA + 0x05 "sha-1WithRSAEncryption" OID_SHA1_WITH_RSA + 0x0B "sha256WithRSAEncryption" OID_SHA256_WITH_RSA + 0x0C "sha384WithRSAEncryption" OID_SHA384_WITH_RSA + 0x0D "sha512WithRSAEncryption" OID_SHA512_WITH_RSA + 0x07 "PKCS-7" + 0x01 "data" OID_PKCS7_DATA + 0x02 "signedData" OID_PKCS7_SIGNED_DATA + 0x03 "envelopedData" OID_PKCS7_ENVELOPED_DATA + 0x04 "signedAndEnvelopedData" OID_PKCS7_SIGNED_ENVELOPED_DATA + 0x05 "digestedData" OID_PKCS7_DIGESTED_DATA + 0x06 "encryptedData" OID_PKCS7_ENCRYPTED_DATA + 0x09 "PKCS-9" + 0x01 "E" OID_PKCS9_EMAIL + 0x02 "unstructuredName" + 0x03 "contentType" OID_PKCS9_CONTENT_TYPE + 0x04 "messageDigest" OID_PKCS9_MESSAGE_DIGEST + 0x05 "signingTime" OID_PKCS9_SIGNING_TIME + 0x06 "counterSignature" + 0x07 "challengePassword" + 0x08 "unstructuredAddress" + 0x0E "extensionRequest" + 0x02 "digestAlgorithm" + 0x02 "md2" OID_MD2 + 0x05 "md5" OID_MD5 + 0x03 "encryptionAlgorithm" + 0x07 "3des-ede-cbc" OID_3DES_EDE_CBC +0x2B "" + 0x06 "dod" + 0x01 "internet" + 0x04 "private" + 0x01 "enterprise" + 0x82 "" + 0x37 "Microsoft" + 0x0A "" + 0x03 "" + 0x03 "msSGC" + 0x89 "" + 0x31 "" + 0x01 "" + 0x01 "" + 0x02 "" + 0x02 "" + 0x4B "TCGID" + 0x05 "security" + 0x05 "mechanisms" + 0x07 "id-pkix" + 0x01 "id-pe" + 0x01 "authorityInfoAccess" OID_AUTHORITY_INFO_ACCESS + 0x03 "id-kp" + 0x01 "serverAuth" + 0x02 "clientAuth" + 0x03 "codeSigning" + 0x04 "emailProtection" + 0x05 "ipsecEndSystem" + 0x06 "ipsecTunnel" + 0x07 "ipsecUser" + 0x08 "timeStamping" + 0x09 "ocspSigning" OID_OCSP_SIGNING + 0x08 "id-otherNames" + 0x05 "xmppAddr" OID_XMPP_ADDR + 0x0A "id-aca" + 0x01 "authenticationInfo" OID_AUTHENTICATION_INFO + 0x02 "accessIdentity" OID_ACCESS_IDENTITY + 0x03 "chargingIdentity" OID_CHARGING_IDENTITY + 0x04 "group" OID_GROUP + 0x30 "id-ad" + 0x01 "ocsp" OID_OCSP + 0x01 "basic" OID_BASIC + 0x02 "nonce" OID_NONCE + 0x03 "crl" OID_CRL + 0x04 "response" OID_RESPONSE + 0x05 "noCheck" OID_NO_CHECK + 0x06 "archiveCutoff" OID_ARCHIVE_CUTOFF + 0x07 "serviceLocator" OID_SERVICE_LOCATOR + 0x0E "oiw" + 0x03 "secsig" + 0x02 "algorithms" + 0x07 "des-cbc" OID_DES_CBC + 0x1A "sha-1" OID_SHA1 + 0x1D "sha-1WithRSASignature" OID_SHA1_WITH_RSA_OIW + 0x24 "TeleTrusT" + 0x03 "algorithm" + 0x03 "signatureAlgorithm" + 0x01 "rsaSignature" + 0x02 "rsaSigWithripemd160" + 0x03 "rsaSigWithripemd128" + 0x04 "rsaSigWithripemd256" +0x60 "" + 0x86 "" + 0x48 "" + 0x01 "organization" + 0x65 "gov" + 0x03 "csor" + 0x04 "nistalgorithm" + 0x02 "hashalgs" + 0x01 "id-SHA-256" + 0x02 "id-SHA-384" + 0x03 "id-SHA-512" + 0x86 "" + 0xf8 "" + 0x42 "netscape" + 0x01 "" + 0x01 "nsCertType" + 0x03 "nsRevocationUrl" OID_NS_REVOCATION_URL + 0x04 "nsCaRevocationUrl" OID_NS_CA_REVOCATION_URL + 0x08 "nsCaPolicyUrl" OID_NS_CA_POLICY_URL + 0x0d "nsComment" OID_NS_COMMENT + 0x03 "directory" + 0x01 "" + 0x03 "employeeNumber" + 0x04 "policy" + 0x01 "nsSGC" + 0x45 "verisign" + 0x01 "pki" + 0x09 "attributes" + 0x02 "messageType" OID_PKI_MESSAGE_TYPE + 0x03 "pkiStatus" OID_PKI_STATUS + 0x04 "failInfo" OID_PKI_FAIL_INFO + 0x05 "senderNonce" OID_PKI_SENDER_NONCE + 0x06 "recipientNonce" OID_PKI_RECIPIENT_NONCE + 0x07 "transID" OID_PKI_TRANS_ID + 0x08 "extensionReq" diff --git a/src/libstrongswan/asn1/pem.c b/src/libstrongswan/asn1/pem.c new file mode 100755 index 000000000..e88db249d --- /dev/null +++ b/src/libstrongswan/asn1/pem.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pem.h" + +#include +#include +#include +#include + +#include +#include +#include + +#define PKCS5_SALT_LEN 8 /* bytes */ + +/** + * check the presence of a pattern in a character string + */ +static bool present(const char* pattern, chunk_t* ch) +{ + u_int pattern_len = strlen(pattern); + + if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0) + { + ch->ptr += pattern_len; + ch->len -= pattern_len; + return TRUE; + } + return FALSE; +} + +/** + * find a boundary of the form -----tag name----- + */ +static bool find_boundary(const char* tag, chunk_t *line) +{ + chunk_t name = chunk_empty; + + if (!present("-----", line)) + return FALSE; + if (!present(tag, line)) + return FALSE; + if (*line->ptr != ' ') + return FALSE; + line->ptr++; line->len--; + + /* extract name */ + name.ptr = line->ptr; + while (line->len > 0) + { + if (present("-----", line)) + { + DBG2(" -----%s %.*s-----", tag, (int)name.len, name.ptr); + return TRUE; + } + line->ptr++; line->len--; name.len++; + } + return FALSE; +} + +/* + * decrypts a passphrase protected encrypted data block + */ +static err_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, size_t key_size, + chunk_t *iv, chunk_t *passphrase) +{ + hasher_t *hasher; + crypter_t *crypter; + chunk_t salt = { iv->ptr, PKCS5_SALT_LEN }; + chunk_t hash; + chunk_t decrypted; + chunk_t key = {alloca(key_size), key_size}; + u_int8_t padding, *last_padding_pos, *first_padding_pos; + + if (passphrase == NULL || passphrase->len == 0) + return "missing passphrase"; + + /* build key from passphrase and IV */ + hasher = hasher_create(HASH_MD5); + hash.len = hasher->get_hash_size(hasher); + hash.ptr = alloca(hash.len); + hasher->get_hash(hasher, *passphrase, NULL); + hasher->get_hash(hasher, salt, hash.ptr); + memcpy(key.ptr, hash.ptr, hash.len); + + if (key.len > hash.len) + { + hasher->get_hash(hasher, hash, NULL); + hasher->get_hash(hasher, *passphrase, NULL); + hasher->get_hash(hasher, salt, hash.ptr); + memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len); + } + hasher->destroy(hasher); + + /* decrypt blob */ + crypter = crypter_create(alg, key_size); + crypter->set_key(crypter, key); + if (crypter->decrypt(crypter, *blob, *iv, &decrypted) != SUCCESS) + { + return "data size is not multiple of block size"; + } + memcpy(blob->ptr, decrypted.ptr, blob->len); + chunk_free(&decrypted); + + /* determine amount of padding */ + last_padding_pos = blob->ptr + blob->len - 1; + padding = *last_padding_pos; + first_padding_pos = (padding > blob->len) ? blob->ptr : last_padding_pos - padding; + + /* check the padding pattern */ + while (--last_padding_pos > first_padding_pos) + { + if (*last_padding_pos != padding) + return "invalid passphrase"; + } + /* remove padding */ + blob->len -= padding; + return NULL; +} + +/* Converts a PEM encoded file into its binary form + * + * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 + * RFC 934 Message Encapsulation, January 1985 + */ +err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp) +{ + typedef enum { + PEM_PRE = 0, + PEM_MSG = 1, + PEM_HEADER = 2, + PEM_BODY = 3, + PEM_POST = 4, + PEM_ABORT = 5 + } state_t; + + encryption_algorithm_t alg = ENCR_UNDEFINED; + size_t key_size = 0; + + bool encrypted = FALSE; + + state_t state = PEM_PRE; + + chunk_t src = *blob; + chunk_t dst = *blob; + chunk_t line = chunk_empty; + chunk_t iv = chunk_empty; + + u_char iv_buf[16]; /* MD5 digest size */ + + /* zero size of converted blob */ + dst.len = 0; + + /* zero size of IV */ + iv.ptr = iv_buf; + iv.len = 0; + + while (fetchline(&src, &line)) + { + if (state == PEM_PRE) + { + if (find_boundary("BEGIN", &line)) + { + state = PEM_MSG; + } + continue; + } + else + { + if (find_boundary("END", &line)) + { + state = PEM_POST; + break; + } + if (state == PEM_MSG) + { + state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER; + } + if (state == PEM_HEADER) + { + err_t ugh = NULL; + chunk_t name = chunk_empty; + chunk_t value = chunk_empty; + + /* an empty line separates HEADER and BODY */ + if (line.len == 0) + { + state = PEM_BODY; + continue; + } + + /* we are looking for a parameter: value pair */ + DBG2(" %.*s", (int)line.len, line.ptr); + ugh = extract_parameter_value(&name, &value, &line); + if (ugh != NULL) + continue; + + if (match("Proc-Type", &name) && *value.ptr == '4') + encrypted = TRUE; + else if (match("DEK-Info", &name)) + { + size_t len = 0; + chunk_t dek; + + if (!extract_token(&dek, ',', &value)) + dek = value; + + if (match("DES-EDE3-CBC", &dek)) + { + alg = ENCR_3DES; + key_size = 24; + } + else if (match("AES-128-CBC", &dek)) + { + alg = ENCR_AES_CBC; + key_size = 16; + } + else if (match("AES-192-CBC", &dek)) + { + alg = ENCR_AES_CBC; + key_size = 24; + } + else if (match("AES-256-CBC", &dek)) + { + alg = ENCR_AES_CBC; + key_size = 32; + } + else + { + return "encryption algorithm not supported"; + } + + eat_whitespace(&value); + ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len); + if (ugh) + return "error in IV"; + + iv.len = len; + } + } + else /* state is PEM_BODY */ + { + const char *ugh = NULL; + size_t len = 0; + chunk_t data; + + /* remove any trailing whitespace */ + if (!extract_token(&data ,' ', &line)) + { + data = line; + } + + /* check for PGP armor checksum */ + if (*data.ptr == '=') + { + *pgp = TRUE; + data.ptr++; + data.len--; + DBG2(" Armor checksum: %.*s", (int)data.len, data.ptr); + continue; + } + + ugh = ttodata(data.ptr, data.len, 64, dst.ptr, blob->len - dst.len, &len); + if (ugh) + { + state = PEM_ABORT; + break; + } + else + { + dst.ptr += len; + dst.len += len; + } + } + } + } + /* set length to size of binary blob */ + blob->len = dst.len; + + if (state != PEM_POST) + return "file coded in unknown format, discarded"; + + return (encrypted)? pem_decrypt(blob, alg, key_size, &iv, passphrase) : NULL; +} + +/* load a coded key or certificate file with autodetection + * of binary DER or base64 PEM ASN.1 formats and armored PGP format + */ +bool pem_asn1_load_file(const char *filename, chunk_t *passphrase, + const char *type, chunk_t *blob, bool *pgp) +{ + err_t ugh = NULL; + + FILE *fd = fopen(filename, "r"); + + if (fd) + { + int bytes; + fseek(fd, 0, SEEK_END ); + blob->len = ftell(fd); + rewind(fd); + blob->ptr = malloc(blob->len); + bytes = fread(blob->ptr, 1, blob->len, fd); + fclose(fd); + DBG1(" loading %s file '%s' (%d bytes)", type, filename, bytes); + + *pgp = FALSE; + + /* try DER format */ + if (is_asn1(*blob)) + { + DBG2(" file coded in DER format"); + return TRUE; + } + + if (passphrase != NULL) + DBG4(" passphrase:", passphrase->ptr, passphrase->len); + + /* try PEM format */ + ugh = pem_to_bin(blob, passphrase, pgp); + + if (ugh == NULL) + { + if (*pgp) + { + DBG2(" file coded in armored PGP format"); + return TRUE; + } + if (is_asn1(*blob)) + { + DBG2(" file coded in PEM format"); + return TRUE; + } + ugh = "file coded in unknown format, discarded"; + } + + /* a conversion error has occured */ + DBG1(" %s", ugh); + chunk_free(blob); + } + else + { + DBG1(" could not open %s file '%s'", type, filename); + } + return FALSE; +} diff --git a/src/libstrongswan/asn1/pem.h b/src/libstrongswan/asn1/pem.h new file mode 100755 index 000000000..0f4b7202c --- /dev/null +++ b/src/libstrongswan/asn1/pem.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PEM_H_ +#define PEM_H_ + +#include + +#include + +err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp); + +bool pem_asn1_load_file(const char *filename, chunk_t *passphrase, + const char *type, chunk_t *blob, bool *pgp); + +#endif /*PEM_H_*/ diff --git a/src/libstrongswan/asn1/ttodata.c b/src/libstrongswan/asn1/ttodata.c new file mode 100644 index 000000000..8114b12c5 --- /dev/null +++ b/src/libstrongswan/asn1/ttodata.c @@ -0,0 +1,378 @@ +/* + * convert from text form of arbitrary data (e.g., keys) to binary + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + */ + +#include "ttodata.h" + +#include +#include + +/* converters and misc */ +static int unhex(const char *, char *, size_t); +static int unb64(const char *, char *, size_t); +static int untext(const char *, char *, size_t); +static const char *badch(const char *, int, char *, size_t); + +/* internal error codes for converters */ +#define SHORT (-2) /* internal buffer too short */ +#define BADPAD (-3) /* bad base64 padding */ +#define BADCH0 (-4) /* invalid character 0 */ +#define BADCH1 (-5) /* invalid character 1 */ +#define BADCH2 (-6) /* invalid character 2 */ +#define BADCH3 (-7) /* invalid character 3 */ +#define BADOFF(code) (BADCH0-(code)) + +/** + * @brief convert text to data, with verbose error reports + * + * If some of this looks slightly odd, it's because it has changed + * repeatedly (from the original atodata()) without a major rewrite. + * + * @param src + * @param srclen 0 means apply strlen() + * @param base 0 means figure it out + * @param dst need not be valid if dstlen is 0 + * @param dstlen + * @param lenp where to record length (NULL is nowhere) + * @param errp error buffer + * @param flags + * @return NULL on success, else literal or errp + */ +const char *ttodatav(const char *src, size_t srclen, int base, char *dst, size_t dstlen, size_t *lenp, char *errp, size_t errlen, unsigned int flags) +{ + size_t ingroup; /* number of input bytes converted at once */ + char buf[4]; /* output from conversion */ + int nbytes; /* size of output */ + int (*decode)(const char *, char *, size_t); + char *stop; + int ndone; + int i; + int underscoreok; + int skipSpace = 0; + + if (srclen == 0) + srclen = strlen(src); + if (dstlen == 0) + dst = buf; /* point it somewhere valid */ + stop = dst + dstlen; + + if (base == 0) { + if (srclen < 2) + return "input too short to be valid"; + if (*src++ != '0') + return "input does not begin with format prefix"; + switch (*src++) { + case 'x': + case 'X': + base = 16; + break; + case 's': + case 'S': + base = 64; + break; + case 't': + case 'T': + base = 256; + break; + default: + return "unknown format prefix"; + } + srclen -= 2; + } + switch (base) { + case 16: + decode = unhex; + underscoreok = 1; + ingroup = 2; + break; + case 64: + decode = unb64; + underscoreok = 0; + ingroup = 4; + if(flags & TTODATAV_IGNORESPACE) { + skipSpace = 1; + } + break; + + case 256: + decode = untext; + ingroup = 1; + underscoreok = 0; + break; + default: + return "unknown base"; + } + + /* proceed */ + ndone = 0; + while (srclen > 0) { + char stage[4]; /* staging area for group */ + size_t sl = 0; + + /* Grab ingroup characters into stage, + * squeezing out blanks if we are supposed to ignore them. + */ + for (sl = 0; sl < ingroup; src++, srclen--) { + if (srclen == 0) + return "input ends in mid-byte, perhaps truncated"; + else if (!(skipSpace && (*src == ' ' || *src == '\t'))) + stage[sl++] = *src; + } + + nbytes = (*decode)(stage, buf, sizeof(buf)); + switch (nbytes) { + case BADCH0: + case BADCH1: + case BADCH2: + case BADCH3: + return badch(stage, nbytes, errp, errlen); + case SHORT: + return "internal buffer too short (\"can't happen\")"; + case BADPAD: + return "bad (non-zero) padding at end of base64 input"; + } + if (nbytes <= 0) + return "unknown internal error"; + for (i = 0; i < nbytes; i++) { + if (dst < stop) + *dst++ = buf[i]; + ndone++; + } + while (srclen >= 1 && skipSpace && (*src == ' ' || *src == '\t')){ + src++; + srclen--; + } + if (underscoreok && srclen > 1 && *src == '_') { + /* srclen > 1 means not last character */ + src++; + srclen--; + } + } + + if (ndone == 0) + return "no data bytes specified by input"; + if (lenp != NULL) + *lenp = ndone; + return NULL; +} + +/** + * @brief ttodata - convert text to data + * + * @param src + * @param srclen 0 means apply strlen() + * @param base 0 means figure it out + * @param dst need not be valid if dstlen is 0 + * @param dstlen + * @param lenp where to record length (NULL is nowhere) + * @return NULL on success, else literal + */ +const char *ttodata(const char *src, size_t srclen, int base, char *dst, size_t dstlen, size_t *lenp) +{ + return ttodatav(src, srclen, base, dst, dstlen, lenp, (char *)NULL, + (size_t)0, TTODATAV_SPACECOUNTS); +} + +/** + * @brief atodata - convert ASCII to data + * + * backward-compatibility interface + * + * @param src + * @param srclen + * @param dst + * @param dstlen + * @return 0 for failure, true length for success + */ +size_t atodata(const char *src, size_t srclen, char *dst, size_t dstlen) +{ + size_t len; + const char *err; + + err = ttodata(src, srclen, 0, dst, dstlen, &len); + if (err != NULL) + return 0; + return len; +} + +/** + * @brief atobytes - convert ASCII to data bytes + * + * another backward-compatibility interface + */ +const char *atobytes(const char *src, size_t srclen, char *dst, size_t dstlen, size_t *lenp) +{ + return ttodata(src, srclen, 0, dst, dstlen, lenp); +} + +/** + * @brief unhex - convert two ASCII hex digits to byte + * + * @param src known to be full length + * @param dstnumber of result bytes, or error code + * @param dstlen not large enough is a failure + * @return + */ +static int unhex(const char *src, char *dst, size_t dstlen) +{ + char *p; + unsigned byte; + static char hex[] = "0123456789abcdef"; + + if (dstlen < 1) + return SHORT; + + p = strchr(hex, *src); + if (p == NULL) + p = strchr(hex, tolower(*src)); + if (p == NULL) + return BADCH0; + byte = (p - hex) << 4; + src++; + + p = strchr(hex, *src); + if (p == NULL) + p = strchr(hex, tolower(*src)); + if (p == NULL) + return BADCH1; + byte |= (p - hex); + + *dst = byte; + return 1; +} + +/** + * @brief unb64 - convert four ASCII base64 digits to three bytes + * + * Note that a base64 digit group is padded out with '=' if it represents + * less than three bytes: one byte is dd==, two is ddd=, three is dddd. + * + * @param src known to be full length + * @param dst + * @param dstlen + * @return number of result bytes, or error code + */ +static int unb64(const char *src, char *dst, size_t dstlen) +{ + char *p; + unsigned byte1; + unsigned byte2; + static char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + if (dstlen < 3) + return SHORT; + + p = strchr(base64, *src++); + + if (p == NULL) + return BADCH0; + byte1 = (p - base64) << 2; /* first six bits */ + + p = strchr(base64, *src++); + if (p == NULL) { + return BADCH1; + } + + byte2 = p - base64; /* next six: two plus four */ + *dst++ = byte1 | (byte2 >> 4); + byte1 = (byte2 & 0xf) << 4; + + p = strchr(base64, *src++); + if (p == NULL) { + if (*(src-1) == '=' && *src == '=') { + if (byte1 != 0) /* bad padding */ + return BADPAD; + return 1; + } + return BADCH2; + } + + byte2 = p - base64; /* next six: four plus two */ + *dst++ = byte1 | (byte2 >> 2); + byte1 = (byte2 & 0x3) << 6; + + p = strchr(base64, *src++); + if (p == NULL) { + if (*(src-1) == '=') { + if (byte1 != 0) /* bad padding */ + return BADPAD; + return 2; + } + return BADCH3; + } + byte2 = p - base64; /* last six */ + *dst++ = byte1 | byte2; + + return 3; +} + +/** + * @brief untext - convert one ASCII character to byte + * + * @param src known to be full length + * @param dst + * @param dstlen not large enough is a failure + * @return number of result bytes, or error code + */ +static int untext(const char *src, char *dst, size_t dstlen) +{ + if (dstlen < 1) + return SHORT; + + *dst = *src; + return 1; +} + +/** + * @brief badch - produce a nice complaint about an unknown character + * + * If the compiler complains that the array bigenough[] has a negative + * size, that means the TTODATAV_BUF constant has been set too small. + * + * @param src + * @param errcode + * @param errp might be NULL + * @param errlen + * @return literal or errp + */ +static const char *badch(const char *src, int errcode, char *errp, size_t errlen) +{ + static const char pre[] = "unknown character (`"; + static const char suf[] = "') in input"; + char buf[5]; +# define REQD (sizeof(pre) - 1 + sizeof(buf) - 1 + sizeof(suf)) + struct sizecheck { + char bigenough[TTODATAV_BUF - REQD]; /* see above */ + }; + char ch; + + if (errp == NULL || errlen < REQD) + return "unknown character in input"; + strcpy(errp, pre); + ch = *(src + BADOFF(errcode)); + if (isprint(ch)) { + buf[0] = ch; + buf[1] = '\0'; + } else { + buf[0] = '\\'; + buf[1] = ((ch & 0700) >> 6) + '0'; + buf[2] = ((ch & 0070) >> 3) + '0'; + buf[3] = ((ch & 0007) >> 0) + '0'; + buf[4] = '\0'; + } + strcat(errp, buf); + strcat(errp, suf); + return (const char *)errp; +} diff --git a/src/libstrongswan/asn1/ttodata.h b/src/libstrongswan/asn1/ttodata.h new file mode 100644 index 000000000..6125c6b82 --- /dev/null +++ b/src/libstrongswan/asn1/ttodata.h @@ -0,0 +1,28 @@ +/* + * convert from text form of arbitrary data (e.g., keys) to binary + * Copyright (C) 2000 Henry Spencer. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 Library General Public + * License for more details. + */ + +#ifndef TTODATA_H_ +#define TTODATA_H_ + +#include + +#define TTODATAV_BUF 40 /* ttodatav's largest non-literal message */ +#define TTODATAV_IGNORESPACE (1<<1) /* ignore spaces in base64 encodings*/ +#define TTODATAV_SPACECOUNTS 0 /* do not ignore spaces in base64 */ + +err_t ttodata(const char *src, size_t srclen, int base, char *buf, size_t buflen, size_t *needed); + + +#endif /* TTODATA_H_ */ diff --git a/src/libstrongswan/chunk.c b/src/libstrongswan/chunk.c new file mode 100644 index 000000000..cba823c22 --- /dev/null +++ b/src/libstrongswan/chunk.c @@ -0,0 +1,410 @@ +/** + * @file chunk.c + * + * @brief Pointer/lenght abstraction and its functions. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "chunk.h" + +#include +#include + +/** + * Empty chunk. + */ +chunk_t chunk_empty = { NULL, 0 }; + +/** + * Described in header. + */ +chunk_t chunk_create(u_char *ptr, size_t len) +{ + chunk_t chunk = {ptr, len}; + return chunk; +} + +/** + * Described in header. + */ +chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk) +{ + chunk_t clone = chunk_empty; + + if (chunk.ptr && chunk.len > 0) + { + clone.ptr = ptr; + clone.len = chunk.len; + memcpy(clone.ptr, chunk.ptr, chunk.len); + } + + return clone; +} + +/** + * Decribed in header. + */ +size_t chunk_length(const char* mode, ...) +{ + va_list chunks; + size_t length = 0; + + va_start(chunks, mode); + while (TRUE) + { + switch (*mode++) + { + case 'm': + case 'c': + { + chunk_t ch = va_arg(chunks, chunk_t); + length += ch.len; + continue; + } + default: + break; + } + break; + } + va_end(chunks); + return length; +} + +/** + * Decribed in header. + */ +chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...) +{ + va_list chunks; + chunk_t construct = chunk_create(ptr, 0); + + va_start(chunks, mode); + while (TRUE) + { + bool free_chunk = FALSE; + switch (*mode++) + { + case 'm': + { + free_chunk = TRUE; + } + case 'c': + { + chunk_t ch = va_arg(chunks, chunk_t); + memcpy(ptr, ch.ptr, ch.len); + ptr += ch.len; + construct.len += ch.len; + if (free_chunk) + { + free(ch.ptr); + } + continue; + } + default: + break; + } + break; + } + va_end(chunks); + + return construct; +} + +/** + * Decribed in header. + */ +void chunk_split(chunk_t chunk, const char *mode, ...) +{ + va_list chunks; + size_t len; + chunk_t *ch; + + va_start(chunks, mode); + while (TRUE) + { + if (*mode == '\0') + { + break; + } + len = va_arg(chunks, size_t); + ch = va_arg(chunks, chunk_t*); + /* a null chunk means skip len bytes */ + if (ch == NULL) + { + chunk = chunk_skip(chunk, len); + continue; + } + switch (*mode++) + { + case 'm': + { + ch->len = min(chunk.len, len); + if (ch->len) + { + ch->ptr = chunk.ptr; + } + else + { + ch->ptr = NULL; + } + chunk = chunk_skip(chunk, ch->len); + continue; + } + case 'a': + { + ch->len = min(chunk.len, len); + if (ch->len) + { + ch->ptr = malloc(ch->len); + memcpy(ch->ptr, chunk.ptr, ch->len); + } + else + { + ch->ptr = NULL; + } + chunk = chunk_skip(chunk, ch->len); + continue; + } + case 'c': + { + ch->len = min(ch->len, chunk.len); + ch->len = min(ch->len, len); + if (ch->len) + { + memcpy(ch->ptr, chunk.ptr, ch->len); + } + else + { + ch->ptr = NULL; + } + chunk = chunk_skip(chunk, ch->len); + continue; + } + default: + break; + } + break; + } + va_end(chunks); +} + +/** + * Described in header. + */ +bool chunk_write(chunk_t chunk, const char *path, const char *label, mode_t mask, bool force) +{ + mode_t oldmask; + FILE *fd; + + if (!force) + { + fd = fopen(path, "r"); + if (fd) + { + fclose(fd); + DBG1(" %s file '%s' already exists", label, path); + return FALSE; + } + } + + /* set umask */ + oldmask = umask(mask); + + fd = fopen(path, "w"); + + if (fd) + { + fwrite(chunk.ptr, sizeof(u_char), chunk.len, fd); + fclose(fd); + DBG1(" written %s file '%s' (%u bytes)", label, path, chunk.len); + umask(oldmask); + return TRUE; + } + else + { + DBG1(" could not open %s file '%s' for writing", label, path); + umask(oldmask); + return FALSE; + } +} + +/** + * Described in header. + */ +void chunk_free(chunk_t *chunk) +{ + free(chunk->ptr); + chunk->ptr = NULL; + chunk->len = 0; +} + +/** + * Described in header. + */ +chunk_t chunk_skip(chunk_t chunk, size_t bytes) +{ + if (chunk.len > bytes) + { + chunk.ptr += bytes; + chunk.len -= bytes; + return chunk; + } + return chunk_empty; +} + +/** + * Described in header. + */ +int chunk_compare(chunk_t a, chunk_t b) +{ + int compare_len = a.len - b.len; + int len = (compare_len < 0)? a.len : b.len; + + if (compare_len != 0 || len == 0) + { + return compare_len; + } + return memcmp(a.ptr, b.ptr, len); +}; + +/** + * Described in header. + */ +bool chunk_equals(chunk_t a, chunk_t b) +{ + return a.ptr != NULL && b.ptr != NULL && + a.len == b.len && memeq(a.ptr, b.ptr, a.len); +} + +/** + * Described in header. + */ +bool chunk_equals_or_null(chunk_t a, chunk_t b) +{ + if (a.ptr == NULL || b.ptr == NULL) + return TRUE; + return a.len == b.len && memeq(a.ptr, b.ptr, a.len); +} + +/** + * Number of bytes per line to dump raw data + */ +#define BYTES_PER_LINE 16 + +/** + * output handler in printf() for byte ranges + */ +static int print_bytes(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + char *bytes = *((void**)(args[0])); + int len = *((size_t*)(args[1])); + + char buffer[BYTES_PER_LINE * 3]; + char ascii_buffer[BYTES_PER_LINE + 1]; + char *buffer_pos = buffer; + char *bytes_pos = bytes; + char *bytes_roof = bytes + len; + int line_start = 0; + int i = 0; + int written = 0; + + written += fprintf(stream, "=> %d bytes @ %p", len, bytes); + + while (bytes_pos < bytes_roof) + { + static char hexdig[] = "0123456789ABCDEF"; + + *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF]; + *buffer_pos++ = hexdig[ *bytes_pos & 0xF]; + + ascii_buffer[i++] = + (*bytes_pos > 31 && *bytes_pos < 127) ? *bytes_pos : '.'; + + if (++bytes_pos == bytes_roof || i == BYTES_PER_LINE) + { + int padding = 3 * (BYTES_PER_LINE - i); + int written; + + while (padding--) + { + *buffer_pos++ = ' '; + } + *buffer_pos++ = '\0'; + ascii_buffer[i] = '\0'; + + written += fprintf(stream, "\n%4d: %s %s", + line_start, buffer, ascii_buffer); + + + buffer_pos = buffer; + line_start += BYTES_PER_LINE; + i = 0; + } + else + { + *buffer_pos++ = ' '; + } + } + return written; +} + +/** + * output handler in printf() for chunks + */ +static int print_chunk(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + chunk_t *chunk = *((chunk_t**)(args[0])); + bool first = TRUE; + chunk_t copy = *chunk; + int written = 0; + + if (!info->alt) + { + const void *new_args[] = {&chunk->ptr, &chunk->len}; + return print_bytes(stream, info, new_args); + } + + while (copy.len > 0) + { + if (first) + { + first = FALSE; + } + else + { + written += fprintf(stream, ":"); + } + written += fprintf(stream, "%02x", *copy.ptr++); + copy.len--; + } + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_CHUNK, print_chunk, arginfo_ptr); + register_printf_function(PRINTF_BYTES, print_bytes, arginfo_ptr_int); +} diff --git a/src/libstrongswan/chunk.h b/src/libstrongswan/chunk.h new file mode 100644 index 000000000..a13ccfc22 --- /dev/null +++ b/src/libstrongswan/chunk.h @@ -0,0 +1,154 @@ +/** + * @file chunk.h + * + * @brief Pointer/length abstraction and its functions. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CHUNK_H_ +#define CHUNK_H_ + +#include +#include + +#include + +typedef struct chunk_t chunk_t; + +/** + * General purpose pointer/length abstraction. + */ +struct chunk_t { + /** Pointer to start of data */ + u_char *ptr; + /** Length of data in bytes */ + size_t len; +}; + +/** + * A { NULL, 0 }-chunk handy for initialization. + */ +extern chunk_t chunk_empty; + +/** + * Create a new chunk pointing to "ptr" with length "len" + */ +chunk_t chunk_create(u_char *ptr, size_t len); + +/** + * Create a clone of a chunk pointing to "ptr" + */ +chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk); + +/** + * Calculate length of multiple chunks + */ +size_t chunk_length(const char *mode, ...); + +/** + * Concatenate chunks into a chunk pointing to "ptr", + * "mode" is a string of "c" (copy) and "m" (move), which says + * how to handle to chunks in "..." + */ +chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...); + +/** + * Split up a chunk into parts, "mode" is a string of "a" (alloc), + * "c" (copy) and "m" (move). Each letter say for the corresponding chunk if + * it should get allocated on heap, copied into existing chunk, or the chunk + * should point into "chunk". The length of each part is an argument before + * each target chunk. E.g.: + * chunk_split(chunk, "mcac", 3, &a, 7, &b, 5, &c, d.len, &d); + */ +void chunk_split(chunk_t chunk, const char *mode, ...); + +/** + * Write the binary contents of a chunk_t to a file + */ +bool chunk_write(chunk_t chunk, const char *path, const char *label, mode_t mask, bool force); + +/** + * Free contents of a chunk + */ +void chunk_free(chunk_t *chunk); + +/** + * Initialize a chunk to point to buffer inspectable by sizeof() + */ +#define chunk_from_buf(str) { str, sizeof(str) } + +/** + * Initialize a chunk to point to a thing + */ +#define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing)) + +/** + * Allocate a chunk on the heap + */ +#define chunk_alloc(bytes) chunk_create(malloc(bytes), bytes) + +/** + * Allocate a chunk on the stack + */ +#define chunk_alloca(bytes) chunk_create(alloca(bytes), bytes) + +/** + * Clone a chunk on heap + */ +#define chunk_clone(chunk) chunk_create_clone(malloc(chunk.len), chunk) + +/** + * Clone a chunk on stack + */ +#define chunk_clonea(chunk) chunk_create_clone(alloca(chunk.len), chunk) + +/** + * Concatenate chunks into a chunk on heap + */ +#define chunk_cat(mode, ...) chunk_create_cat(malloc(chunk_length(mode, __VA_ARGS__)), mode, __VA_ARGS__) + +/** + * Concatenate chunks into a chunk on stack + */ +#define chunk_cata(mode, ...) chunk_create_cat(alloca(chunk_length(mode, __VA_ARGS__)), mode, __VA_ARGS__) + +/** + * Skip n bytes in chunk (forward pointer, shorten length) + */ +chunk_t chunk_skip(chunk_t chunk, size_t bytes); + +/** + * Compare two chunks, returns zero if a equals b + * or negative/positive if a is small/greater than b + */ +int chunk_compare(chunk_t a, chunk_t b); + +/** + * Compare two chunks for equality, + * NULL chunks are never equal. + */ +bool chunk_equals(chunk_t a, chunk_t b); + +/** + * Compare two chunks for equality, + * NULL chunks are always equal. + */ +bool chunk_equals_or_null(chunk_t a, chunk_t b); + +#endif /* CHUNK_H_ */ diff --git a/src/libstrongswan/credential_store.h b/src/libstrongswan/credential_store.h new file mode 100755 index 000000000..5d51981ec --- /dev/null +++ b/src/libstrongswan/credential_store.h @@ -0,0 +1,294 @@ +/** + * @file credential_store.h + * + * @brief Interface credential_store_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CREDENTIAL_STORE_H_ +#define CREDENTIAL_STORE_H_ + +typedef struct credential_store_t credential_store_t; + +#include +#include +#include +#include +#include +#include + + +/** + * @brief The interface for a credential_store backend. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct credential_store_t { + + /** + * @brief Returns the secret shared by two specific IDs. + * + * The returned chunk must be destroyed by the caller after usage. + * + * @param this calling object + * @param my_id my ID identifiying the secret. + * @param other_id peer ID identifying the secret. + * @param[out] secret the pre-shared secret will be written there. + * @return + * - NOT_FOUND if no preshared secrets for specific ID could be found + * - SUCCESS + * + */ + status_t (*get_shared_key) (credential_store_t *this, identification_t *my_id, + identification_t *other_id, chunk_t *shared_key); + + /** + * @brief Returns the EAP secret for two specified IDs. + * + * The returned chunk must be destroyed by the caller after usage. + * + * @param this calling object + * @param my_id my ID identifiying the secret. + * @param other_id peer ID identifying the secret. + * @param[out] eap_key the EAP secret will be written here + * @return + * - NOT_FOUND if no preshared secrets for specific ID could be found + * - SUCCESS + * + */ + status_t (*get_eap_key) (credential_store_t *this, identification_t *my_id, + identification_t *other_id, chunk_t *eap_key); + + /** + * @brief Returns the RSA public key of a specific ID. + * + * @param this calling object + * @param id identification_t object identifiying the key. + * @return public key, or NULL if not found + */ + rsa_public_key_t* (*get_rsa_public_key) (credential_store_t *this, identification_t *id); + + /** + * @brief Returns the RSA public key of a specific ID if is trusted + * + * @param this calling object + * @param id identification_t object identifiying the key. + * @return public key, or NULL if not found or not trusted + */ + rsa_public_key_t* (*get_trusted_public_key) (credential_store_t *this, identification_t *id); + + /** + * @brief Returns the RSA private key belonging to an RSA public key + * + * The returned rsa_private_key_t must be destroyed by the caller after usage. + * + * @param this calling object + * @param pubkey public key + * @return private key, or NULL if not found + */ + rsa_private_key_t* (*get_rsa_private_key) (credential_store_t *this, rsa_public_key_t *pubkey); + + /** + * @brief Is there a matching RSA private key belonging to an RSA public key? + * + * @param this calling object + * @param pubkey public key + * @return TRUE if matching private key was found + */ + bool (*has_rsa_private_key) (credential_store_t *this, rsa_public_key_t *pubkey); + + /** + * @brief Returns the certificate of a specific ID. + * + * @param this calling object + * @param id identification_t object identifiying the cert. + * @return certificate, or NULL if not found + */ + x509_t* (*get_certificate) (credential_store_t *this, identification_t *id); + + /** + * @brief Returns the auth certificate of a specific subject distinguished name. + * + * @param this calling object + * @param auth_flags set of allowed authority types + * @param id identification_t object identifiying the cacert. + * @return certificate, or NULL if not found + */ + x509_t* (*get_auth_certificate) (credential_store_t *this, u_int auth_flags, identification_t *id); + + /** + * @brief Returns the ca certificate of a specific keyID. + * + * @param this calling object + * @param keyid identification_t object identifiying the cacert. + * @return certificate, or NULL if not found + */ + x509_t* (*get_ca_certificate_by_keyid) (credential_store_t *this, chunk_t keyid); + + /** + * @brief Returns the issuing ca of a given certificate. + * + * @param this calling object + * @param cert certificate for which issuer ca info is required + * @return ca info, or NULL if not found + */ + ca_info_t* (*get_issuer) (credential_store_t *this, const x509_t* cert); + + /** + * @brief Verify an X.509 certificate up to trust anchor without any status checks + * + * @param this calling object + * @param cert certificate to be verified + * @return TRUE if trusted + */ + bool (*is_trusted) (credential_store_t *this, x509_t *cert); + + /** + * @brief Verify an X.509 certificate up to trust anchor including status checks + * + * @param this calling object + * @param cert certificate to be verified + * @param found found a certificate copy in the credential store + * @return TRUE if valid, trusted, and current status is good + */ + bool (*verify) (credential_store_t *this, x509_t *cert, bool *found); + + /** + * @brief If an end certificate does not already exists in the credential store then add it. + * + * @param this calling object + * @param cert certificate to be added + * @return pointer to the added or already existing certificate + */ + x509_t* (*add_end_certificate) (credential_store_t *this, x509_t *cert); + + /** + * @brief If an authority certificate does not already exists in the credential store then add it. + * + * @param this calling object + * @param cert authority certificate to be added + * @param auth_flag authority flags to add to the certificate + * @return pointer to the added or already existing certificate + */ + x509_t* (*add_auth_certificate) (credential_store_t *this, x509_t *cert, u_int auth_flag); + + /** + * @brief If a ca info record does not already exists in the credential store then add it. + * + * @param this calling object + * @param ca_info ca info record to be added + */ + void (*add_ca_info) (credential_store_t *this, ca_info_t *ca_info); + + /** + * @brief Release a ca info record with a given name. + * + * @param this calling object + * @param name name of the ca info record to be released + * @return + * - SUCCESS, or + * - NOT_FOUND + */ + status_t (*release_ca_info) (credential_store_t *this, const char *name); + + /** + * @brief Create an iterator over all end certificates. + * + * @param this calling object + * @return iterator + */ + iterator_t* (*create_cert_iterator) (credential_store_t *this); + + /** + * @brief Create an iterator over all authority certificates. + * + * @param this calling object + * @return iterator + */ + iterator_t* (*create_auth_cert_iterator) (credential_store_t *this); + + /** + * @brief Create an iterator over all CA info records + * + * @param this calling object + * @return iterator + */ + iterator_t* (*create_cainfo_iterator) (credential_store_t *this); + + /** + * @brief Loads ca certificates from a default directory. + * + * Certificates in both DER and PEM format are accepted + * + * @param this calling object + */ + void (*load_ca_certificates) (credential_store_t *this); + + /** + * @brief Loads ocsp certificates from a default directory. + * + * Certificates in both DER and PEM format are accepted + * + * @param this calling object + */ + void (*load_ocsp_certificates) (credential_store_t *this); + + /** + * @brief Loads CRLs from a default directory. + * + * Certificates in both DER and PEM format are accepted + * + * @param this calling object + * @param path directory to load crls from + */ + void (*load_crls) (credential_store_t *this); + + /** + * @brief Loads secrets in ipsec.secrets + * + * Currently, all RSA private key files must be in unencrypted form + * either in DER or PEM format. + * + * @param this calling object + */ + void (*load_secrets) (credential_store_t *this); + + /** + * @brief Destroys a credential_store_t object. + * + * @param this calling object + */ + void (*destroy) (credential_store_t *this); +}; + +/** + * @brief Creates a credential_store_t instance. + * + * @param strict enforce a strict crl policy + * @return credential store instance. + * + * @ingroup config + */ +credential_store_t *credential_store_create(bool strict); + + +#endif /*CREDENTIAL_STORE_H_*/ diff --git a/src/libstrongswan/crypto/ca.c b/src/libstrongswan/crypto/ca.c new file mode 100644 index 000000000..1f566a098 --- /dev/null +++ b/src/libstrongswan/crypto/ca.c @@ -0,0 +1,788 @@ +/** + * @file ca.c + * + * @brief Implementation of ca_info_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "x509.h" +#include "crl.h" +#include "ca.h" +#include "certinfo.h" +#include "ocsp.h" + +#include +#include +#include +#include +#include + +typedef struct private_ca_info_t private_ca_info_t; + +/** + * Private data of a ca_info_t object. + */ +struct private_ca_info_t { + /** + * Public interface for this ca info record + */ + ca_info_t public; + + /** + * Name of the ca info record + */ + char *name; + + /** + * Time when ca info record was installed + */ + time_t installed; + + /** + * Distinguished Name of the CA + */ + x509_t *cacert; + + /** + * List of crl URIs + */ + linked_list_t *crluris; + + /** + * List of ocsp URIs + */ + linked_list_t *ocspuris; + + /** + * CRL issued by this ca + */ + crl_t *crl; + + /** + * List of certificate info records + */ + linked_list_t *certinfos; + + /** + * mutex controls access to the elements: + * name, crluris, ocspuris, crl, and certinfos + */ + pthread_mutex_t mutex; +}; + +/** + * static options set by ca_info_set_options() + */ +static bool cache_crls = FALSE; +static u_int crl_check_interval = 0; + +/** + * Implements ca_info_t.equals + */ +static bool equals(const private_ca_info_t *this, const private_ca_info_t *that) +{ + return chunk_equals(this->cacert->get_keyid(this->cacert), + that->cacert->get_keyid(that->cacert)); +} + +/** + * Implements ca_info_t.equals_name_release_info + */ +static bool equals_name_release_info(private_ca_info_t *this, const char *name) +{ + bool found; + + pthread_mutex_lock(&(this->mutex)); + found = this->name != NULL && streq(this->name, name); + + if (found) + { + this->crluris->destroy_offset(this->crluris, + offsetof(identification_t, destroy)); + this->crluris = linked_list_create(); + + this->ocspuris->destroy_offset(this->ocspuris, + offsetof(identification_t, destroy)); + this->ocspuris = linked_list_create(); + + free(this->name); + this->name = NULL; + } + + pthread_mutex_unlock(&(this->mutex)); + return found; +} + +/** + * Implements ca_info_t.is_crl_issuer + */ +static bool is_cert_issuer(private_ca_info_t *this, const x509_t *cert) +{ + return cert->is_issuer(cert, this->cacert); +} + +/** + * Implements ca_info_t.is_crl_issuer + */ +static bool is_crl_issuer(private_ca_info_t *this, const crl_t *crl) +{ + return crl->is_issuer(crl, this->cacert); +} + +/** + * Implements ca_info_t.has_crl + */ +static bool has_crl(private_ca_info_t *this) +{ + bool found; + + pthread_mutex_lock(&(this->mutex)); + found = this->crl != NULL; + pthread_mutex_unlock(&(this->mutex)); + + return found; +} + +/** + * Implements ca_info_t.has_certinfos + */ +static bool has_certinfos(private_ca_info_t *this) +{ + bool found; + + pthread_mutex_lock(&(this->mutex)); + found = this->certinfos->get_count(this->certinfos) > 0; + pthread_mutex_unlock(&(this->mutex)); + + return found; +} + +/** + * Implements ca_info_t.add_crl + */ +static void add_crl(private_ca_info_t *this, crl_t *crl) +{ + pthread_mutex_lock(&(this->mutex)); + + if (this->crl) + { + if (crl->is_newer(crl, this->crl)) + { + this->crl->destroy(this->crl); + this->crl = crl; + DBG1(" this crl is newer - existing crl replaced"); + } + else + { + crl->destroy(crl); + DBG1(" this crl is not newer - existing crl retained"); + } + } + else + { + this->crl = crl; + DBG2(" crl added"); + } + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implements ca_info_t.list_crl + */ +static void list_crl(private_ca_info_t *this, FILE *out, bool utc) +{ + pthread_mutex_lock(&(this->mutex)); + + fprintf(out, "%#U\n", this->crl, utc); + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implements ca_info_t.list_certinfos + */ +static void list_certinfos(private_ca_info_t *this, FILE *out, bool utc) +{ + pthread_mutex_lock(&(this->mutex)); + + fprintf(out," authname: '%D'\n", this->cacert->get_subject(this->cacert)); + { + chunk_t authkey = this->cacert->get_subjectKeyID(this->cacert); + + fprintf(out," authkey: %#B\n", &authkey); + } + { + iterator_t *iterator = this->certinfos->create_iterator(this->certinfos, TRUE); + certinfo_t *certinfo; + + while (iterator->iterate(iterator, (void**)&certinfo)) + { + fprintf(out, "%#Y\n", certinfo, utc); + } + iterator->destroy(iterator); + } + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Find an exact copy of an identification in a linked list + */ +static identification_t* find_identification(linked_list_t *list, identification_t *id) +{ + identification_t *found_id = NULL, *current_id; + + iterator_t *iterator = list->create_iterator(list, TRUE); + + while (iterator->iterate(iterator, (void**)¤t_id)) + { + if (id->equals(id, current_id)) + { + found_id = current_id; + break; + } + } + iterator->destroy(iterator); + + return found_id; +} + +/** + * Add a unique identification to a linked list + */ +static identification_t *add_identification(linked_list_t *list, identification_t *id) +{ + identification_t *found_id = find_identification(list, id); + + if (found_id) + { + id->destroy(id); + return found_id; + } + else + { + list->insert_last(list, (void*)id); + return id; + } +} + +/** + * Implements ca_info_t.add_crluri + */ +static void add_crluri(private_ca_info_t *this, chunk_t uri) +{ + if (uri.len < 6 || + (strncasecmp(uri.ptr, "http", 4) != 0 && + strncasecmp(uri.ptr, "ldap", 4) != 0 && + strncasecmp(uri.ptr, "file", 4) != 0 && + strncasecmp(uri.ptr, "ftp", 3) != 0)) + { + DBG1(" invalid crl uri '%#B'", uri); + return; + } + else + { + identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri); + + pthread_mutex_lock(&(this->mutex)); + add_identification(this->crluris, crluri); + pthread_mutex_unlock(&(this->mutex)); + } +} + +/** + * Implements ca_info_t.add_ocspuri + */ +static void add_ocspuri(private_ca_info_t *this, chunk_t uri) +{ + if (uri.len < 7 || strncasecmp(uri.ptr, "http", 4) != 0) + { + DBG1(" invalid ocsp uri '%.*s'", uri.len, uri.ptr); + return; + } + else + { + identification_t *ocspuri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri); + + pthread_mutex_lock(&(this->mutex)); + add_identification(this->ocspuris, ocspuri); + pthread_mutex_unlock(&(this->mutex)); + } +} + +/** + * Implements ca_info_t.add_info. + */ +void add_info (private_ca_info_t *this, const private_ca_info_t *that) +{ + pthread_mutex_lock(&(this->mutex)); + + if (this->name == NULL && that->name != NULL) + { + this->name = strdup(that->name); + } + + pthread_mutex_unlock(&(this->mutex)); + + { + identification_t *uri; + + iterator_t *iterator = that->crluris->create_iterator(that->crluris, TRUE); + + while (iterator->iterate(iterator, (void**)&uri)) + { + add_crluri(this, uri->get_encoding(uri)); + } + iterator->destroy(iterator); + } + + { + identification_t *uri; + + iterator_t *iterator = that->ocspuris->create_iterator(that->ocspuris, TRUE); + + while (iterator->iterate(iterator, (void**)&uri)) + { + add_ocspuri(this, uri->get_encoding(uri)); + } + iterator->destroy(iterator); + } +} + +/** + * Implements ca_info_t.get_certificate. + */ +static x509_t* get_certificate(private_ca_info_t* this) +{ + return this->cacert; +} + +/** + * caches a crl by saving it to a given crl directory + */ +void cache_crl(private_ca_info_t* this, const char *crl_dir, crl_t *crl) +{ + char buffer[BUF_LEN]; + char *path; + char *pos = buffer; + int len = BUF_LEN; + int n; + + chunk_t authKeyID = this->cacert->get_subjectKeyID(this->cacert); + chunk_t uri; + + uri.ptr = buffer; + uri.len = 7 + strlen(crl_dir) + 1 + 2*authKeyID.len + 4; + + if (uri.len >= BUF_LEN) + { + DBG1("file uri exceeds buffer length of %d bytes - crl not saved", BUF_LEN); + return; + } + + /* print the file uri prefix */ + n = snprintf(pos, len, "file://"); + pos += n; len -= n; + + /* remember the start of the path string */ + path = pos; + + /* print the default crl directory path */ + n = snprintf(pos, len, "%s/", crl_dir); + pos += n; len -= n; + + /* create and print a unique crl filename derived from the authKeyID */ + while (authKeyID.len-- > 0) + { + n = snprintf(pos, len, "%02x", *authKeyID.ptr++); + pos += n; len -= n; + } + + /* add the file suffix */ + n = snprintf(pos, len, ".crl"); + + if (crl->write_to_file(crl, path, 0022, TRUE)) + { + identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri); + + add_identification(this->crluris, crluri); + } +} + +/** + * Implements ca_info_t.verify_by_crl. + */ +static cert_status_t verify_by_crl(private_ca_info_t* this, certinfo_t *certinfo, + const char *crl_dir) +{ + rsa_public_key_t *issuer_public_key = this->cacert->get_public_key(this->cacert); + bool stale; + + pthread_mutex_lock(&(this->mutex)); + if (this->crl == NULL) + { + stale = TRUE; + DBG1("no crl is locally available"); + } + else + { + stale = !this->crl->is_valid(this->crl); + DBG1("crl is %s", stale? "stale":"valid"); + } + + if (stale && crl_check_interval > 0) + { + iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE); + identification_t *uri; + + while (iterator->iterate(iterator, (void**)&uri)) + { + fetcher_t *fetcher; + char uri_string[BUF_LEN]; + chunk_t uri_chunk = uri->get_encoding(uri); + chunk_t response_chunk; + + snprintf(uri_string, BUF_LEN, "%.*s", uri_chunk.len, uri_chunk.ptr); + fetcher = fetcher_create(uri_string); + + response_chunk = fetcher->get(fetcher); + fetcher->destroy(fetcher); + if (response_chunk.ptr != NULL) + { + crl_t *crl = crl_create_from_chunk(response_chunk); + + if (crl == NULL) + { + free(response_chunk.ptr); + continue; + } + if (!is_crl_issuer(this, crl)) + { + DBG1(" fetched crl has wrong issuer"); + crl->destroy(crl); + continue; + } + if (!crl->verify(crl, issuer_public_key)) + { + DBG1("fetched crl signature is invalid"); + crl->destroy(crl); + continue; + } + DBG2("fetched crl signature is valid"); + + if (this->crl == NULL) + { + this->crl = crl; + } + else if (crl->is_newer(crl, this->crl)) + { + this->crl->destroy(this->crl); + this->crl = crl; + DBG1("this crl is newer - existing crl replaced"); + } + else + { + crl->destroy(crl); + DBG1("this crl is not newer - existing crl retained"); + continue; + } + if (crl->is_valid(crl)) + { + if (cache_crls && strncasecmp(uri_string, "file", 4) != 0) + { + cache_crl(this, crl_dir, crl); + } + /* we found a valid crl and therefore exit the fetch loop */ + break; + } + else + { + DBG1("fetched crl is stale"); + } + } + } + iterator->destroy(iterator); + } + + if (this->crl) + { + if (!this->crl->verify(this->crl, issuer_public_key)) + { + DBG1("crl signature is invalid"); + goto ret; + } + DBG2("crl signature is valid"); + + this->crl->get_status(this->crl, certinfo); + } + +ret: + pthread_mutex_unlock(&(this->mutex)); + return certinfo->get_status(certinfo); +} + +/** + * Implements ca_info_t.verify_by_ocsp. + */ +static cert_status_t verify_by_ocsp(private_ca_info_t* this, + certinfo_t *certinfo, + credential_store_t *credentials) +{ + bool stale; + iterator_t *iterator; + certinfo_t *cached_certinfo = NULL; + int comparison = 1; + + pthread_mutex_lock(&(this->mutex)); + + /* do we support OCSP at all? */ + if (this->ocspuris->get_count(this->ocspuris) == 0) + { + goto ret; + } + + iterator = this->certinfos->create_iterator(this->certinfos, TRUE); + + /* find the list insertion point in alphabetical order */ + while(iterator->iterate(iterator, (void**)&cached_certinfo)) + { + comparison = certinfo->compare_serialNumber(certinfo, cached_certinfo); + + if (comparison <= 0) + { + break; + } + } + + /* do we have a valid certinfo_t for this serial number in our cache? */ + if (comparison == 0) + { + stale = cached_certinfo->get_nextUpdate(cached_certinfo) < time(NULL); + DBG1("ocsp status in cache is %s", stale ? "stale":"fresh"); + } + else + { + stale = TRUE; + DBG1("ocsp status is not in cache"); + } + + if (stale) + { + ocsp_t *ocsp; + + ocsp = ocsp_create(this->cacert, this->ocspuris); + ocsp->fetch(ocsp, certinfo, credentials); + if (certinfo->get_status(certinfo) != CERT_UNDEFINED) + { + if (comparison != 0) + { + cached_certinfo = certinfo_create(certinfo->get_serialNumber(certinfo)); + + if (comparison > 0) + { + iterator->insert_after(iterator, (void *)cached_certinfo); + } + else + { + iterator->insert_before(iterator, (void *)cached_certinfo); + } + } + cached_certinfo->update(cached_certinfo, certinfo); + } + ocsp->destroy(ocsp); + } + else + { + certinfo->update(certinfo, cached_certinfo); + } + + iterator->destroy(iterator); + +ret: + pthread_mutex_unlock(&(this->mutex)); + return certinfo->get_status(certinfo); +} + +/** + * Implements ca_info_t.purge_ocsp + */ +static void purge_ocsp(private_ca_info_t *this) +{ + pthread_mutex_lock(&(this->mutex)); + + this->certinfos->destroy_offset(this->certinfos, + offsetof(certinfo_t, destroy)); + this->certinfos = linked_list_create(); + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implements ca_info_t.destroy + */ +static void destroy(private_ca_info_t *this) +{ + this->crluris->destroy_offset(this->crluris, + offsetof(identification_t, destroy)); + this->ocspuris->destroy_offset(this->ocspuris, + offsetof(identification_t, destroy)); + this->certinfos->destroy_offset(this->certinfos, + offsetof(certinfo_t, destroy)); + DESTROY_IF(this->crl); + free(this->name); + free(this); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_ca_info_t *this = *((private_ca_info_t**)(args[0])); + bool utc = TRUE; + int written = 0; + const x509_t *cacert; + + if (info->alt) + { + utc = *((bool*)args[1]); + } + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + pthread_mutex_lock(&(this->mutex)); + written += fprintf(stream, "%#T", &this->installed, utc); + + if (this->name) + { + written += fprintf(stream, ", \"%s\"\n", this->name); + } + else + { + written += fprintf(stream, "\n"); + } + + cacert = this->cacert; + written += fprintf(stream, " authname: '%D'\n", cacert->get_subject(cacert)); + { + chunk_t authkey = cacert->get_subjectKeyID(cacert); + + written += fprintf(stream, " authkey: %#B\n", &authkey); + } + { + chunk_t keyid = cacert->get_keyid(cacert); + + written += fprintf(stream, " keyid: %#B\n", &keyid); + } + { + identification_t *crluri; + iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE); + bool first = TRUE; + + while (iterator->iterate(iterator, (void**)&crluri)) + { + written += fprintf(stream, " %s '%D'\n", + first? "crluris:":" ", crluri); + first = FALSE; + } + iterator->destroy(iterator); + } + { + identification_t *ocspuri; + iterator_t *iterator = this->ocspuris->create_iterator(this->ocspuris, TRUE); + bool first = TRUE; + + while (iterator->iterate(iterator, (void**)&ocspuri)) + { + written += fprintf(stream, " %s '%D'\n", + first? "ocspuris:":" ", ocspuri); + first = FALSE; + } + iterator->destroy(iterator); + } + pthread_mutex_unlock(&(this->mutex)); + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_CAINFO, print, arginfo_ptr_alt_ptr_int); +} + +/* + * Described in header. + */ +void ca_info_set_options(bool cache, u_int interval) +{ + cache_crls = cache; + crl_check_interval = interval; +} + +/* + * Described in header. + */ +ca_info_t *ca_info_create(const char *name, x509_t *cacert) +{ + private_ca_info_t *this = malloc_thing(private_ca_info_t); + + /* initialize */ + this->installed = time(NULL); + this->name = (name == NULL)? NULL:strdup(name); + this->cacert = cacert; + this->crluris = linked_list_create(); + this->ocspuris = linked_list_create(); + this->certinfos = linked_list_create(); + this->crl = NULL; + + /* initialize the mutex */ + pthread_mutex_init(&(this->mutex), NULL); + + /* public functions */ + this->public.equals = (bool (*) (const ca_info_t*,const ca_info_t*))equals; + this->public.equals_name_release_info = (bool (*) (ca_info_t*,const char*))equals_name_release_info; + this->public.is_cert_issuer = (bool (*) (ca_info_t*,const x509_t*))is_cert_issuer; + this->public.is_crl_issuer = (bool (*) (ca_info_t*,const crl_t*))is_crl_issuer; + this->public.add_info = (void (*) (ca_info_t*,const ca_info_t*))add_info; + this->public.add_crl = (void (*) (ca_info_t*,crl_t*))add_crl; + this->public.has_crl = (bool (*) (ca_info_t*))has_crl; + this->public.has_certinfos = (bool (*) (ca_info_t*))has_certinfos; + this->public.list_crl = (void (*) (ca_info_t*,FILE*,bool))list_crl; + this->public.list_certinfos = (void (*) (ca_info_t*,FILE*,bool))list_certinfos; + this->public.add_crluri = (void (*) (ca_info_t*,chunk_t))add_crluri; + this->public.add_ocspuri = (void (*) (ca_info_t*,chunk_t))add_ocspuri; + this->public.get_certificate = (x509_t* (*) (ca_info_t*))get_certificate; + this->public.verify_by_crl = (cert_status_t (*) (ca_info_t*,certinfo_t*, const char*))verify_by_crl; + this->public.verify_by_ocsp = (cert_status_t (*) (ca_info_t*,certinfo_t*,credential_store_t*))verify_by_ocsp; + this->public.purge_ocsp = (void (*) (ca_info_t*))purge_ocsp; + this->public.destroy = (void (*) (ca_info_t*))destroy; + + return &this->public; +} diff --git a/src/libstrongswan/crypto/ca.h b/src/libstrongswan/crypto/ca.h new file mode 100644 index 000000000..c494a4468 --- /dev/null +++ b/src/libstrongswan/crypto/ca.h @@ -0,0 +1,215 @@ +/** + * @file ca.h + * + * @brief Interface of ca_info_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CA_H_ +#define CA_H_ + +typedef struct ca_info_t ca_info_t; + +#include +#include + +#include + +#include "x509.h" +#include "crl.h" + +/** + * @brief X.509 certification authority information record + * + * @b Constructors: + * - ca_info_create() + * + * @ingroup transforms + */ +struct ca_info_t { + + /** + * @brief Compare two ca info records + * + * Comparison is done via the keyid of the ca certificate + * + * @param this first ca info object + * @param that second ca info objct + * @return TRUE if a match is found + */ + bool (*equals) (const ca_info_t *this, const ca_info_t* that); + + /** + * @brief If the ca info record has the same name then release the name and URIs + * + * @param this ca info object + * @return TRUE if a match is found + */ + bool (*equals_name_release_info) (ca_info_t *this, const char *name); + + /** + * @brief Checks if a certificate was issued by this ca + * + * @param this ca info object + * @param cert certificate to be checked + * @return TRUE if the issuing ca has been found + */ + bool (*is_cert_issuer) (ca_info_t *this, const x509_t *cert); + + /** + * @brief Checks if a crl was issued by this ca + * + * @param this ca info object + * @param crl crl to be checked + * @return TRUE if the issuing ca has been found + */ + bool (*is_crl_issuer) (ca_info_t *this, const crl_t *crl); + + /** + * @brief Merges info from a secondary ca info object + * + * @param this primary ca info object + * @param that secondary ca info object + */ + void (*add_info) (ca_info_t *this, const ca_info_t *that); + + /** + * @brief Adds a new or replaces an obsoleted CRL + * + * @param this ca info object + * @param crl crl to be added + */ + void (*add_crl) (ca_info_t *this, crl_t *crl); + + /** + * @brief Does the CA have a CRL? + * + * @param this ca info object + * @return TRUE if crl is available + */ + bool (*has_crl) (ca_info_t *this); + + /** + * @brief Does the CA have OCSP certinfos? + * + * @param this ca info object + * @return TRUE if there are any certinfos + */ + bool (*has_certinfos) (ca_info_t *this); + + /** + * @brief List the CRL onto the console + * + * @param this ca info object + * @param out output stream + * @param utc TRUE - utc + FALSE - local time + */ + void (*list_crl) (ca_info_t *this, FILE *out, bool utc); + + /** + * @brief List the OCSP certinfos onto the console + * + * @param this ca info object + * @param out output stream + * @param utc TRUE - utc + FALSE - local time + */ + void (*list_certinfos) (ca_info_t *this, FILE *out, bool utc); + + /** + * @brief Adds a CRL URI to a list + * + * @param this ca info object + * @param uri crl uri to be added + */ + void (*add_crluri) (ca_info_t *this, chunk_t uri); + + /** + * @brief Adds a OCSP URI to a list + * + * @param this ca info object + * @param uri ocsp uri to be added + */ + void (*add_ocspuri) (ca_info_t *this, chunk_t uri); + + /** + * @brief Get the ca certificate + * + * @param this ca info object + * @return ca certificate + */ + x509_t* (*get_certificate) (ca_info_t *this); + + /** + * @brief Verify the status of a certificate by CRL + * + * @param this ca info object + * @param certinfo detailed certificate status information + * @param crl_dir directory where fetched crls should be stored + * @return certificate status + */ + cert_status_t (*verify_by_crl) (ca_info_t *this, certinfo_t *certinfo, const char *crl_dir); + + /** + * @brief Verify the status of a certificate by OCSP + * + * @param this ca info object + * @param certinfo detailed certificate status information + * @param credentials credential store needed for trust path verification + * @return certificate status + */ + cert_status_t (*verify_by_ocsp) (ca_info_t* this, certinfo_t* certinfo, credential_store_t* credentials); + + /** + * @brief Purge the OCSP certinfos of a ca info record + * + * @param this ca info object + */ + void (*purge_ocsp) (ca_info_t *this); + + /** + * @brief Destroys a ca info record + * + * @param this ca info to destroy + */ + void (*destroy) (ca_info_t *this); +}; + +/** + * @brief Set ca info options + * + * @param cache TRUE if crls shall be cached by storing them + * @param interval crl_check_interval to be set in seconds + * + * @ingroup crypto + */ +void ca_info_set_options(bool cache, u_int interval); + +/** + * @brief Create a ca info record + * + * @param name name of the ca info record + * @param cacert path to the ca certificate + * @return created ca_info_t, or NULL if invalid. + * + * @ingroup crypto + */ +ca_info_t *ca_info_create(const char *name, x509_t *cacert); + +#endif /* CA_H_ */ diff --git a/src/libstrongswan/crypto/certinfo.c b/src/libstrongswan/crypto/certinfo.c new file mode 100644 index 000000000..654e4c2bd --- /dev/null +++ b/src/libstrongswan/crypto/certinfo.c @@ -0,0 +1,305 @@ +/** + * @file certinfo.c + * + * @brief Implementation of certinfo_t. + * + */ + +/* + * Copyright (C) 2006 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include + +#include "certinfo.h" + +typedef struct private_certinfo_t private_certinfo_t; + +/** + * Private data of a certinfo_t object. + */ +struct private_certinfo_t { + /** + * Public interface for this certificate status information object. + */ + certinfo_t public; + + /** + * Serial number of the certificate + */ + chunk_t serialNumber; + + /** + * Certificate status + */ + cert_status_t status; + + /** + * Certificate status is for one-time use only + */ + bool once; + + /** + * Time when the certificate status info was generated + */ + time_t thisUpdate; + + /** + * Time when an updated certifcate status info will be available + */ + time_t nextUpdate; + + /** + * Time of certificate revocation + */ + time_t revocationTime; + + /** + * Reason of certificate revocation + */ + crl_reason_t revocationReason; +}; + +ENUM(cert_status_names, CERT_GOOD, CERT_UNTRUSTED, + "good", + "revoked", + "unknown", + "unknown", + "untrusted", +); + +ENUM(crl_reason_names, REASON_UNSPECIFIED, REASON_REMOVE_FROM_CRL, + "unspecified", + "key compromise", + "ca compromise", + "affiliation changed", + "superseded", + "cessation of operation", + "certificate hold", + "reason #7", + "remove from crl", +); + +/** + * Implements certinfo_t.compare_serialNumber + */ +static int compare_serialNumber(const private_certinfo_t *this, const private_certinfo_t *that) +{ + return chunk_compare(this->serialNumber, that->serialNumber); +} + +/** + * Implements certinfo_t.equals_serialNumber + */ +static bool equals_serialNumber(const private_certinfo_t *this, const private_certinfo_t *that) +{ + return chunk_equals(this->serialNumber, that->serialNumber); +} + +/** + * Implements certinfo_t.get_serialNumber + */ +static chunk_t get_serialNumber(const private_certinfo_t *this) +{ + return this->serialNumber; +} + +/** + * Implements certinfo_t.set_status + */ +static void set_status(private_certinfo_t *this, cert_status_t status) +{ + this->status = status; +} + +/** + * Implements certinfo_t.get_status + */ +static cert_status_t get_status(const private_certinfo_t *this) +{ + return this->status; +} + +/** + * Implements certinfo_t.set_thisUpdate + */ +static void set_thisUpdate(private_certinfo_t *this, time_t thisUpdate) +{ + this->thisUpdate = thisUpdate; +} + +/** + * Implements certinfo_t.get_thisUpdate + */ +static time_t get_thisUpdate(const private_certinfo_t *this) +{ + return this->thisUpdate; +} + +/** + * Implements certinfo_t.set_nextUpdate + */ +static void set_nextUpdate(private_certinfo_t *this, time_t nextUpdate) +{ + this->nextUpdate = nextUpdate; +} + +/** + * Implements certinfo_t.get_nextUpdate + */ +static time_t get_nextUpdate(const private_certinfo_t *this) +{ + return this->nextUpdate; +} + +/** + * Implements certinfo_t.set_revocationTime + */ +static void set_revocationTime(private_certinfo_t *this, time_t revocationTime) +{ + this->revocationTime = revocationTime; +} + +/** + * Implements certinfo_t.get_revocationTime + */ +static time_t get_revocationTime(const private_certinfo_t *this) +{ + return this->revocationTime; +} + +/** + * Implements certinfo_t.set_revocationReason + */ +static void set_revocationReason(private_certinfo_t *this, crl_reason_t reason) +{ + this->revocationReason = reason; +} + +/** + * Implements certinfo_t.get_revocationReason + */ +static crl_reason_t get_revocationReason(const private_certinfo_t *this) +{ + return this->revocationReason; +} + +/** + * Implements certinfo_t.update + */ +static void update(private_certinfo_t *this, const private_certinfo_t *that) +{ + if (equals_serialNumber(this, that)) + { + chunk_t this_serialNumber = this->serialNumber; + + *this = *that; + this->serialNumber = this_serialNumber; + } +} + +/** + * Implements certinfo_t.destroy + */ +static void destroy(private_certinfo_t *this) +{ + free(this->serialNumber.ptr); + free(this); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_certinfo_t *this = *((private_certinfo_t**)(args[0])); + bool utc = TRUE; + int written = 0; + time_t now; + + if (info->alt) + { + utc = *((bool*)args[1]); + } + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + now = time(NULL); + + written += fprintf(stream, "%#T, until %#T, ", + &this->thisUpdate, utc, + &this->nextUpdate, utc); + if (now > this->nextUpdate) + { + written += fprintf(stream, "expired (%V ago)\n", &now, &this->nextUpdate); + } + else + { + written += fprintf(stream, "ok (expires in %V)\n", &now, &this->nextUpdate); + } + written += fprintf(stream, " serial: %#B, %N", + &this->serialNumber, + cert_status_names, this->status); + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_CERTINFO, print, arginfo_ptr_alt_ptr_int); +} + +/* + * Described in header. + */ +certinfo_t *certinfo_create(chunk_t serial) +{ + private_certinfo_t *this = malloc_thing(private_certinfo_t); + + /* initialize */ + this->serialNumber = chunk_clone(serial); + this->status = CERT_UNDEFINED; + this->thisUpdate = UNDEFINED_TIME; + this->nextUpdate = UNDEFINED_TIME; + this->revocationTime = UNDEFINED_TIME; + this->revocationReason = REASON_UNSPECIFIED; + + /* public functions */ + this->public.compare_serialNumber = (int (*) (const certinfo_t*,const certinfo_t*))compare_serialNumber; + this->public.equals_serialNumber = (bool (*) (const certinfo_t*,const certinfo_t*))equals_serialNumber; + this->public.get_serialNumber = (chunk_t (*) (const certinfo_t*))get_serialNumber; + this->public.set_status = (void (*) (certinfo_t*,cert_status_t))set_status; + this->public.get_status = (cert_status_t (*) (const certinfo_t*))get_status; + this->public.set_thisUpdate = (void (*) (certinfo_t*,time_t))set_thisUpdate; + this->public.get_thisUpdate = (time_t (*) (const certinfo_t*))get_thisUpdate; + this->public.set_nextUpdate = (void (*) (certinfo_t*,time_t))set_nextUpdate; + this->public.get_nextUpdate = (time_t (*) (const certinfo_t*))get_nextUpdate; + this->public.set_revocationTime = (void (*) (certinfo_t*,time_t))set_revocationTime; + this->public.get_revocationTime = (time_t (*) (const certinfo_t*))get_revocationTime; + this->public.set_revocationReason = (void (*) (certinfo_t*, crl_reason_t))set_revocationReason; + this->public.get_revocationReason = (crl_reason_t(*) (const certinfo_t*))get_revocationReason; + this->public.update = (void (*) (certinfo_t*, const certinfo_t*))update; + this->public.destroy = (void (*) (certinfo_t*))destroy; + + return &this->public; +} diff --git a/src/libstrongswan/crypto/certinfo.h b/src/libstrongswan/crypto/certinfo.h new file mode 100644 index 000000000..476befda8 --- /dev/null +++ b/src/libstrongswan/crypto/certinfo.h @@ -0,0 +1,203 @@ +/** + * @file certinfo.h + * + * @brief Interface of certinfo_t. + * + */ + +/* + * Copyright (C) 2006 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CERTINFO_H_ +#define CERTINFO_H_ + +typedef enum cert_status_t cert_status_t; +typedef enum crl_reason_t crl_reason_t; +typedef struct certinfo_t certinfo_t; + +#include + +/** + * RFC 2560 OCSP - certificate status + */ +enum cert_status_t { + CERT_GOOD = 0, + CERT_REVOKED = 1, + CERT_UNKNOWN = 2, + CERT_UNDEFINED = 3, + CERT_UNTRUSTED = 4 /* private use */ +}; + +extern enum_name_t *cert_status_names; + +/** + * RFC 2459 CRL reason codes + */ +enum crl_reason_t { + REASON_UNSPECIFIED = 0, + REASON_KEY_COMPROMISE = 1, + REASON_CA_COMPROMISE = 2, + REASON_AFFILIATION_CHANGED = 3, + REASON_SUPERSEDED = 4, + REASON_CESSATION_OF_OPERATON = 5, + REASON_CERTIFICATE_HOLD = 6, + REASON_REMOVE_FROM_CRL = 8 +}; + +extern enum_name_t *crl_reason_names; + +/** + * @brief X.509 certificate status information + * + * @ingroup transforms + */ +struct certinfo_t { + + /** + * @brief Check if both certinfo objects have the same serialNumber. + * + * @param this calling object + * @param that second certinfo_t object + * @return TRUE if the same serialNumber + */ + bool (*equals_serialNumber) (const certinfo_t *this, const certinfo_t *that); + + /** + * @brief Compares two serial numbers. + * + * @param this calling object + * @param that second certinfo_t object + * @return negative if this is smaller than that + * zero if this equals that + * positive if this is greater than that + */ + int (*compare_serialNumber) (const certinfo_t *this, const certinfo_t *that); + + /** + * @brief Get serial number. + * + * @param this calling object + * @return serialNumber + */ + chunk_t (*get_serialNumber) (const certinfo_t *this); + + /** + * @brief Set certificate status. + * + * @param this calling object + * @param status status + */ + void (*set_status) (certinfo_t *this, cert_status_t status); + + /** + * @brief Get certificate status. + * + * @param this calling object + * @return status + */ + cert_status_t (*get_status) (const certinfo_t *this); + + /** + * @brief Set thisUpdate. + * + * @param this calling object + * @param thisUpdate thisUpdate + */ + void (*set_thisUpdate) (certinfo_t *this, time_t thisUpdate); + + /** + * @brief Get thisUpdate. + * + * @param this calling object + * @return thisUpdate + */ + time_t (*get_thisUpdate) (const certinfo_t *this); + + /** + * @brief Set nextUpdate. + * + * @param this calling object + * @param nextUpdate + */ + void (*set_nextUpdate) (certinfo_t *this, time_t nextUpdate); + + /** + * @brief Get nextUpdate. + * + * @param this calling object + * @return nextUpdate + */ + time_t (*get_nextUpdate) (const certinfo_t *this); + + /** + * @brief Set revocationTime. + * + * @param this calling object + * @param revocationTime revocationTime + */ + void (*set_revocationTime) (certinfo_t *this, time_t revocationTime); + + /** + * @brief Get revocationTime. + * + * @param this calling object + * @return revocationTime + */ + time_t (*get_revocationTime) (const certinfo_t *this); + + /** + * @brief Set revocationReason. + * + * @param this calling object + * @param reason revocationReason + */ + void (*set_revocationReason) (certinfo_t *this, crl_reason_t reason); + + /** + * @brief Get revocationReason. + * + * @param this calling object + * @return revocationReason + */ + crl_reason_t (*get_revocationReason) (const certinfo_t *this); + + /** + * @brief Set revocationReason. + * + * @param this calling object to be updated + * @param that object containing updated information + */ + void (*update) (certinfo_t *this, const certinfo_t *that); + + /** + * @brief Destroys the certinfo_t object. + * + * @param this certinfo_t to destroy + */ + void (*destroy) (certinfo_t *this); + +}; + +/** + * @brief Create a certinfo_t object. + * + * @param serial chunk serial number of the certificate + * @return created certinfo_t object + * + * @ingroup transforms + */ +certinfo_t *certinfo_create(chunk_t serial); + +#endif /* CERTINFO_H_ */ diff --git a/src/libstrongswan/crypto/crl.c b/src/libstrongswan/crypto/crl.c new file mode 100755 index 000000000..00d6a3ac3 --- /dev/null +++ b/src/libstrongswan/crypto/crl.c @@ -0,0 +1,533 @@ +/** + * @file crl.c + * + * @brief Implementation of crl_t. + * + */ + +/* + * Copyright (C) 2006 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "certinfo.h" +#include "x509.h" +#include "crl.h" + +#define CRL_WARNING_INTERVAL 7 /* days */ + +extern char* check_expiry(time_t expiration_date, int warning_interval, bool strict); +extern time_t parse_time(chunk_t blob, int level0); +extern void parse_authorityKeyIdentifier(chunk_t blob, int level0 , chunk_t *authKeyID, chunk_t *authKeySerialNumber); + +/* access structure for a revoked certificate */ + +typedef struct revokedCert_t revokedCert_t; + +struct revokedCert_t { + chunk_t userCertificate; + time_t revocationDate; + crl_reason_t revocationReason; +}; + +typedef struct private_crl_t private_crl_t; + +/** + * Private data of a crl_t object. + */ +struct private_crl_t { + /** + * Public interface for this crl. + */ + crl_t public; + + /** + * Time when crl was installed + */ + time_t installed; + + /** + * List of crlDistributionPoints + */ + linked_list_t *crlDistributionPoints; + + /** + * X.509 crl in DER format + */ + chunk_t certificateList; + + /** + * X.509 crl body over which signature is computed + */ + chunk_t tbsCertList; + + /** + * Version of the X.509 crl + */ + u_int version; + + /** + * Signature algorithm + */ + int sigAlg; + + /** + * ID representing the crl issuer + */ + identification_t *issuer; + + /** + * Time when the crl was generated + */ + time_t thisUpdate; + + /** + * Time when an update crl will be available + */ + time_t nextUpdate; + + /** + * List of identification_t's representing subjectAltNames + */ + linked_list_t *revokedCertificates; + + /** + * Authority Key Identifier + */ + chunk_t authKeyID; + + /** + * Authority Key Serial Number + */ + chunk_t authKeySerialNumber; + + /** + * Signature algorithm (must be identical to sigAlg) + */ + int algorithm; + + /** + * Signature + */ + chunk_t signature; +}; + +/** + * ASN.1 definition of an X.509 certificate revocation list + */ +static const asn1Object_t crlObjects[] = { + { 0, "certificateList", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "tbsCertList", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "version", ASN1_INTEGER, ASN1_OPT | + ASN1_BODY }, /* 2 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ + { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 4 */ + { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ + { 2, "thisUpdate", ASN1_EOC, ASN1_RAW }, /* 6 */ + { 2, "nextUpdate", ASN1_EOC, ASN1_RAW }, /* 7 */ + { 2, "revokedCertificates", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 8 */ + { 3, "certList", ASN1_SEQUENCE, ASN1_NONE }, /* 9 */ + { 4, "userCertificate", ASN1_INTEGER, ASN1_BODY }, /* 10 */ + { 4, "revocationDate", ASN1_EOC, ASN1_RAW }, /* 11 */ + { 4, "crlEntryExtensions", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 12 */ + { 5, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */ + { 6, "extnID", ASN1_OID, ASN1_BODY }, /* 14 */ + { 6, "critical", ASN1_BOOLEAN, ASN1_DEF | + ASN1_BODY }, /* 15 */ + { 6, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 16 */ + { 4, "end opt or loop", ASN1_EOC, ASN1_END }, /* 17 */ + { 2, "end opt or loop", ASN1_EOC, ASN1_END }, /* 18 */ + { 2, "optional extensions", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 19 */ + { 3, "crlExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */ + { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */ + { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */ + { 5, "critical", ASN1_BOOLEAN, ASN1_DEF | + ASN1_BODY }, /* 23 */ + { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */ + { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */ + { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */ + }; + +#define CRL_OBJ_CERTIFICATE_LIST 0 +#define CRL_OBJ_TBS_CERT_LIST 1 +#define CRL_OBJ_VERSION 2 +#define CRL_OBJ_SIG_ALG 4 +#define CRL_OBJ_ISSUER 5 +#define CRL_OBJ_THIS_UPDATE 6 +#define CRL_OBJ_NEXT_UPDATE 7 +#define CRL_OBJ_USER_CERTIFICATE 10 +#define CRL_OBJ_REVOCATION_DATE 11 +#define CRL_OBJ_CRL_ENTRY_EXTN_ID 14 +#define CRL_OBJ_CRL_ENTRY_CRITICAL 15 +#define CRL_OBJ_CRL_ENTRY_EXTN_VALUE 16 +#define CRL_OBJ_EXTN_ID 22 +#define CRL_OBJ_CRITICAL 23 +#define CRL_OBJ_EXTN_VALUE 24 +#define CRL_OBJ_ALGORITHM 27 +#define CRL_OBJ_SIGNATURE 28 +#define CRL_OBJ_ROOF 29 + +/** + * Parses a CRL revocation reason code + */ +static crl_reason_t parse_crl_reasonCode(chunk_t object) +{ + crl_reason_t reason = REASON_UNSPECIFIED; + + if (*object.ptr == ASN1_ENUMERATED && asn1_length(&object) == 1) + { + reason = *object.ptr; + } + DBG2(" '%N'", crl_reason_names, reason); + + return reason; +} + +/** + * Parses an X.509 Certificate Revocation List (CRL) + */ +bool parse_x509crl(chunk_t blob, u_int level0, private_crl_t *crl) +{ + asn1_ctx_t ctx; + bool critical; + chunk_t extnID; + chunk_t userCertificate = chunk_empty; + revokedCert_t *revokedCert = NULL; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < CRL_OBJ_ROOF) + { + if (!extract_object(crlObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + /* those objects which will parsed further need the next higher level */ + level++; + + switch (objectID) + { + case CRL_OBJ_CERTIFICATE_LIST: + crl->certificateList = object; + break; + case CRL_OBJ_TBS_CERT_LIST: + crl->tbsCertList = object; + break; + case CRL_OBJ_VERSION: + crl->version = (object.len) ? (1+(u_int)*object.ptr) : 1; + DBG2(" v%d", crl->version); + break; + case CRL_OBJ_SIG_ALG: + crl->sigAlg = parse_algorithmIdentifier(object, level, NULL); + break; + case CRL_OBJ_ISSUER: + crl->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); + DBG2(" '%D'", crl->issuer); + break; + case CRL_OBJ_THIS_UPDATE: + crl->thisUpdate = parse_time(object, level); + break; + case CRL_OBJ_NEXT_UPDATE: + crl->nextUpdate = parse_time(object, level); + break; + case CRL_OBJ_USER_CERTIFICATE: + userCertificate = object; + break; + case CRL_OBJ_REVOCATION_DATE: + revokedCert = malloc_thing(revokedCert_t); + revokedCert->userCertificate = userCertificate; + revokedCert->revocationDate = parse_time(object, level); + revokedCert->revocationReason = REASON_UNSPECIFIED; + crl->revokedCertificates->insert_last(crl->revokedCertificates, (void *)revokedCert); + break; + case CRL_OBJ_CRL_ENTRY_EXTN_ID: + case CRL_OBJ_EXTN_ID: + extnID = object; + break; + case CRL_OBJ_CRL_ENTRY_CRITICAL: + case CRL_OBJ_CRITICAL: + critical = object.len && *object.ptr; + DBG2(" %s",(critical)?"TRUE":"FALSE"); + break; + case CRL_OBJ_CRL_ENTRY_EXTN_VALUE: + case CRL_OBJ_EXTN_VALUE: + { + int extn_oid = known_oid(extnID); + + if (revokedCert && extn_oid == OID_CRL_REASON_CODE) + { + revokedCert->revocationReason = parse_crl_reasonCode(object); + } + else if (extn_oid == OID_AUTHORITY_KEY_ID) + { + parse_authorityKeyIdentifier(object, level, &crl->authKeyID, &crl->authKeySerialNumber); + } + } + break; + case CRL_OBJ_ALGORITHM: + crl->algorithm = parse_algorithmIdentifier(object, level, NULL); + break; + case CRL_OBJ_SIGNATURE: + crl->signature = object; + break; + default: + break; + } + objectID++; + } + time(&crl->installed); + return TRUE; +} + +/** + * Implements crl_t.is_valid + */ +static bool is_valid(const private_crl_t *this) +{ + time_t current_time = time(NULL); + + DBG2(" this update : %T", &this->thisUpdate); + DBG2(" current time: %T", ¤t_time); + DBG2(" next update: %T", &this->nextUpdate); + + return current_time < this->nextUpdate; +} + +/** + * Implements crl_t.get_issuer + */ +static identification_t *get_issuer(const private_crl_t *this) +{ + return this->issuer; +} + +/** + * Implements crl_t.equals_issuer + */ +static bool equals_issuer(const private_crl_t *this, const private_crl_t *other) +{ + return (this->authKeyID.ptr) + ? chunk_equals(this->authKeyID, other->authKeyID) + : (this->issuer->equals(this->issuer, other->issuer) + && chunk_equals_or_null(this->authKeySerialNumber, other->authKeySerialNumber)); +} + +/** + * Implements crl_t.is_issuer + */ +static bool is_issuer(const private_crl_t *this, const x509_t *issuer) +{ + return (this->authKeyID.ptr) + ? chunk_equals(this->authKeyID, issuer->get_subjectKeyID(issuer)) + : (this->issuer->equals(this->issuer, issuer->get_subject(issuer)) + && chunk_equals_or_null(this->authKeySerialNumber, issuer->get_serialNumber(issuer))); +} + +/** + * Implements crl_t.is_newer + */ +static bool is_newer(const private_crl_t *this, const private_crl_t *other) +{ + return (this->nextUpdate > other->nextUpdate); +} + +/** + * Implements crl_t.verify + */ +static bool verify(const private_crl_t *this, const rsa_public_key_t *signer) +{ + return signer->verify_emsa_pkcs1_signature(signer, this->tbsCertList, this->signature) == SUCCESS; +} + +/** + * Implements crl_t.get_status + */ +static void get_status(const private_crl_t *this, certinfo_t *certinfo) +{ + chunk_t serialNumber = certinfo->get_serialNumber(certinfo); + iterator_t *iterator; + revokedCert_t *revokedCert; + + certinfo->set_nextUpdate(certinfo, this->nextUpdate); + certinfo->set_status(certinfo, CERT_GOOD); + + iterator = this->revokedCertificates->create_iterator(this->revokedCertificates, TRUE); + while (iterator->iterate(iterator, (void**)&revokedCert)) + { + if (chunk_equals(serialNumber, revokedCert->userCertificate)) + { + certinfo->set_status(certinfo, CERT_REVOKED); + certinfo->set_revocationTime(certinfo, revokedCert->revocationDate); + certinfo->set_revocationReason(certinfo, revokedCert->revocationReason); + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implements crl_t.write_to_file. + */ +static bool write_to_file(private_crl_t *this, const char *path, mode_t mask, bool force) +{ + return chunk_write(this->certificateList, path, "crl", mask, force); +} + +/** + * Implements crl_t.destroy + */ +static void destroy(private_crl_t *this) +{ + this->revokedCertificates->destroy_function(this->revokedCertificates, free); + this->crlDistributionPoints->destroy_offset(this->crlDistributionPoints, + offsetof(identification_t, destroy)); + DESTROY_IF(this->issuer); + free(this->certificateList.ptr); + free(this); +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_crl_t *this = *((private_crl_t**)(args[0])); + bool utc = TRUE; + int written = 0; + time_t now; + + if (info->alt) + { + utc = *((bool*)args[1]); + } + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + now = time(NULL); + + written += fprintf(stream, "%#T, revoked certs: %d\n", &this->installed, utc, + this->revokedCertificates->get_count(this->revokedCertificates)); + written += fprintf(stream, " issuer: '%D'\n", this->issuer); + written += fprintf(stream, " updates: this %#T\n", &this->thisUpdate, utc); + written += fprintf(stream, " next %#T ", &this->nextUpdate, utc); + if (this->nextUpdate == UNDEFINED_TIME) + { + written += fprintf(stream, "ok (expires never)"); + } + else if (now > this->nextUpdate) + { + written += fprintf(stream, "expired (%V ago)", &now, &this->nextUpdate); + } + else if (now > this->nextUpdate - CRL_WARNING_INTERVAL * 60 * 60 * 24) + { + written += fprintf(stream, "ok (expires in %V)", &now, &this->nextUpdate); + } + else + { + written += fprintf(stream, "ok"); + } + if (this->authKeyID.ptr) + { + written += fprintf(stream, "\n authkey: %#B", &this->authKeyID); + } + if (this->authKeySerialNumber.ptr) + { + written += fprintf(stream, "\n aserial: %#B", &this->authKeySerialNumber); + } + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_CRL, print, arginfo_ptr_alt_ptr_int); +} + +/* + * Described in header. + */ +crl_t *crl_create_from_chunk(chunk_t chunk) +{ + private_crl_t *this = malloc_thing(private_crl_t); + + /* initialize */ + this->crlDistributionPoints = linked_list_create(); + this->tbsCertList = chunk_empty; + this->issuer = NULL; + this->revokedCertificates = linked_list_create(); + this->authKeyID = chunk_empty; + this->authKeySerialNumber = chunk_empty; + + /* public functions */ + this->public.get_issuer = (identification_t* (*) (const crl_t*))get_issuer; + this->public.equals_issuer = (bool (*) (const crl_t*,const crl_t*))equals_issuer; + this->public.is_issuer = (bool (*) (const crl_t*,const x509_t*))is_issuer; + this->public.is_valid = (bool (*) (const crl_t*))is_valid; + this->public.is_newer = (bool (*) (const crl_t*,const crl_t*))is_newer; + this->public.verify = (bool (*) (const crl_t*,const rsa_public_key_t*))verify; + this->public.get_status = (void (*) (const crl_t*,certinfo_t*))get_status; + this->public.write_to_file = (bool (*) (const crl_t*,const char*,mode_t,bool))write_to_file; + this->public.destroy = (void (*) (crl_t*))destroy; + + if (!parse_x509crl(chunk, 0, this)) + { + destroy(this); + return NULL; + } + + return &this->public; +} + +/* + * Described in header. + */ +crl_t *crl_create_from_file(const char *filename) +{ + bool pgp = FALSE; + chunk_t chunk = chunk_empty; + crl_t *crl = NULL; + + if (!pem_asn1_load_file(filename, NULL, "crl", &chunk, &pgp)) + return NULL; + + crl = crl_create_from_chunk(chunk); + + if (crl == NULL) + free(chunk.ptr); + return crl; +} diff --git a/src/libstrongswan/crypto/crl.h b/src/libstrongswan/crypto/crl.h new file mode 100755 index 000000000..8a11fc390 --- /dev/null +++ b/src/libstrongswan/crypto/crl.h @@ -0,0 +1,147 @@ +/** + * @file crl.h + * + * @brief Interface of crl_t. + * + */ + +/* + * Copyright (C) 2006 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CRL_H_ +#define CRL_H_ + +typedef struct crl_t crl_t; + +#include +#include +#include +#include +#include + +/** + * @brief X.509 certificate revocation list + * + * @b Constructors: + * - crl_create_from_chunk() + * - crl_create_from_file() + * + * @ingroup transforms + */ +struct crl_t { + + /** + * @brief Get the crl's issuer ID. + * + * The resulting ID is always a identification_t + * of type ID_DER_ASN1_DN. + * + * @param this calling object + * @return issuers ID + */ + identification_t *(*get_issuer) (const crl_t *this); + + /** + * @brief Check if both crls have the same issuer. + * + * @param this calling object + * @param other other crl + * @return TRUE if the same issuer + */ + bool (*equals_issuer) (const crl_t *this, const crl_t *other); + + /** + * @brief Check if ia candidate cert is the issuer of the crl + * + * @param this calling object + * @param issuer candidate issuer of the crl + * @return TRUE if issuer + */ + bool (*is_issuer) (const crl_t *this, const x509_t *issuer); + + /** + * @brief Checks the validity interval of the crl + * + * @param this calling object + * @return TRUE if the crl is valid + */ + bool (*is_valid) (const crl_t *this); + + /** + * @brief Checks if this crl is newer (thisUpdate) than the other crl + * + * @param this calling object + * @param other other crl object + * @return TRUE if this was issued more recently than other + */ + bool (*is_newer) (const crl_t *this, const crl_t *other); + + /** + * @brief Check if a crl is trustworthy. + * + * @param this calling object + * @param signer signer's RSA public key + * @return TRUE if crl is trustworthy + */ + bool (*verify) (const crl_t *this, const rsa_public_key_t *signer); + + /** + * @brief Get the certificate status + * + * @param this calling object + * @param certinfo certinfo is updated + */ + void (*get_status) (const crl_t *this, certinfo_t *certinfo); + + /** + * @brief Write a der-encoded crl to a file + * + * @param this calling object + * @param path path where the file is to be stored + * @param mask file access control rights + * @param force overwrite the file if it already exists + * @return TRUE if successfully written + */ + bool (*write_to_file) (const crl_t *this, const char *path, mode_t mask, bool force); + + /** + * @brief Destroys the crl. + * + * @param this crl to destroy + */ + void (*destroy) (crl_t *this); +}; + +/** + * @brief Read a x509 crl from a DER encoded blob. + * + * @param chunk chunk containing DER encoded data + * @return created crl_t, or NULL if invalid. + * + * @ingroup transforms + */ +crl_t *crl_create_from_chunk(chunk_t chunk); + +/** + * @brief Read a x509 crl from a DER encoded file. + * + * @param filename file containing DER encoded data + * @return created crl_t, or NULL if invalid. + * + * @ingroup transforms + */ +crl_t *crl_create_from_file(const char *filename); + +#endif /* CRL_H_ */ diff --git a/src/libstrongswan/crypto/crypters/aes_cbc_crypter.c b/src/libstrongswan/crypto/crypters/aes_cbc_crypter.c new file mode 100644 index 000000000..947188af3 --- /dev/null +++ b/src/libstrongswan/crypto/crypters/aes_cbc_crypter.c @@ -0,0 +1,1620 @@ +/** + * @file aes_cbc_crypter.c + * + * @brief Implementation of aes_cbc_crypter_t + * + */ + + /* + * Copyright (C) 2001 Dr B. R. Gladman + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "aes_cbc_crypter.h" + + + +/* + * The number of key schedule words for different block and key lengths + * allowing for method of computation which requires the length to be a + * multiple of the key length. This version of AES implementation supports + * all three keylengths 16, 24 and 32 bytes! + * + * Nk = 4 6 8 + * ------------- + * Nb = 4 | 60 60 64 + * 6 | 96 90 96 + * 8 | 120 120 120 + */ +#define AES_KS_LENGTH 120 +#define AES_RC_LENGTH 29 + +#define AES_BLOCK_SIZE 16 + +typedef struct private_aes_cbc_crypter_t private_aes_cbc_crypter_t; + +/** + * @brief Class implementing the AES symmetric encryption algorithm. + * + * @ingroup crypters + */ +struct private_aes_cbc_crypter_t { + + /** + * Public part of this class. + */ + aes_cbc_crypter_t public; + + /** + * Number of words in the key input block. + */ + u_int32_t aes_Nkey; + + /** + * The number of cipher rounds. + */ + u_int32_t aes_Nrnd; + + /** + * The encryption key schedule. + */ + u_int32_t aes_e_key[AES_KS_LENGTH]; + + /** + * The decryption key schedule. + */ + u_int32_t aes_d_key[AES_KS_LENGTH]; + + /** + * Key size of this AES cypher object. + */ + u_int32_t key_size; + + /** + * Decrypts a block. + * + * No memory gets allocated. + * + * @param this calling object + * @param[in] in_blk block to decrypt + * @param[out] out_blk decrypted data are written to this location + */ + void (*decrypt_block) (const private_aes_cbc_crypter_t *this, const unsigned char in_blk[], unsigned char out_blk[]); + + /** + * Encrypts a block. + * + * No memory gets allocated. + * + * @param this calling object + * @param[in] in_blk block to encrypt + * @param[out] out_blk encrypted data are written to this location + */ + void (*encrypt_block) (const private_aes_cbc_crypter_t *this, const unsigned char in_blk[], unsigned char out_blk[]); +}; + + +/* ugly macro stuff */ + +/* 1. Define UNROLL for full loop unrolling in encryption and decryption. + * 2. Define PARTIAL_UNROLL to unroll two loops in encryption and decryption. + * 3. Define FIXED_TABLES for compiled rather than dynamic tables. + * 4. Define FF_TABLES to use tables for field multiplies and inverses. + * Do not enable this without understanding stack space requirements. + * 5. Define ARRAYS to use arrays to hold the local state block. If this + * is not defined, individually declared 32-bit words are used. + * 6. Define FAST_VARIABLE if a high speed variable block implementation + * is needed (essentially three separate fixed block size code sequences) + * 7. Define either ONE_TABLE or FOUR_TABLES for a fast table driven + * version using 1 table (2 kbytes of table space) or 4 tables (8 + * kbytes of table space) for higher speed. + * 8. Define either ONE_LR_TABLE or FOUR_LR_TABLES for a further speed + * increase by using tables for the last rounds but with more table + * space (2 or 8 kbytes extra). + * 9. If neither ONE_TABLE nor FOUR_TABLES is defined, a compact but + * slower version is provided. + * 10. If fast decryption key scheduling is needed define ONE_IM_TABLE + * or FOUR_IM_TABLES for higher speed (2 or 8 kbytes extra). + */ + +#define UNROLL +//#define PARTIAL_UNROLL + +#define FIXED_TABLES +//#define FF_TABLES +//#define ARRAYS +#define FAST_VARIABLE + +//#define ONE_TABLE +#define FOUR_TABLES + +//#define ONE_LR_TABLE +#define FOUR_LR_TABLES + +//#define ONE_IM_TABLE +#define FOUR_IM_TABLES + +#if defined(UNROLL) && defined (PARTIAL_UNROLL) +#error both UNROLL and PARTIAL_UNROLL are defined +#endif + +#if defined(ONE_TABLE) && defined (FOUR_TABLES) +#error both ONE_TABLE and FOUR_TABLES are defined +#endif + +#if defined(ONE_LR_TABLE) && defined (FOUR_LR_TABLES) +#error both ONE_LR_TABLE and FOUR_LR_TABLES are defined +#endif + +#if defined(ONE_IM_TABLE) && defined (FOUR_IM_TABLES) +#error both ONE_IM_TABLE and FOUR_IM_TABLES are defined +#endif + +#if defined(AES_BLOCK_SIZE) && AES_BLOCK_SIZE != 16 && AES_BLOCK_SIZE != 24 && AES_BLOCK_SIZE != 32 +#error an illegal block size has been specified +#endif + +/** + * Rotates bytes within words by n positions, moving bytes + * to higher index positions with wrap around into low positions. + */ +#define upr(x,n) (((x) << 8 * (n)) | ((x) >> (32 - 8 * (n)))) +/** + * Moves bytes by n positions to higher index positions in + * words but without wrap around. + */ +#define ups(x,n) ((x) << 8 * (n)) + +/** + * Extracts a byte from a word. + */ +#define bval(x,n) ((unsigned char)((x) >> 8 * (n))) +#define bytes2word(b0, b1, b2, b3) \ + ((u_int32_t)(b3) << 24 | (u_int32_t)(b2) << 16 | (u_int32_t)(b1) << 8 | (b0)) + + +/* little endian processor without data alignment restrictions: AES_LE_OK */ +/* original code: i386 */ +#if defined(i386) || defined(_I386) || defined(__i386__) || defined(__i386) +#define AES_LE_OK 1 +/* added (tested): alpha --jjo */ +#elif defined(__alpha__)|| defined (__alpha) +#define AES_LE_OK 1 +/* added (tested): ia64 --jjo */ +#elif defined(__ia64__)|| defined (__ia64) +#define AES_LE_OK 1 +#endif + +#ifdef AES_LE_OK +/* little endian processor without data alignment restrictions */ +#define word_in(x) *(u_int32_t*)(x) +#define const_word_in(x) *(const u_int32_t*)(x) +#define word_out(x,v) *(u_int32_t*)(x) = (v) +#define const_word_out(x,v) *(const u_int32_t*)(x) = (v) +#else +/* slower but generic big endian or with data alignment restrictions */ +/* some additional "const" touches to stop "gcc -Wcast-qual" complains --jjo */ +#define word_in(x) ((u_int32_t)(((unsigned char *)(x))[0])|((u_int32_t)(((unsigned char *)(x))[1])<<8)|((u_int32_t)(((unsigned char *)(x))[2])<<16)|((u_int32_t)(((unsigned char *)(x))[3])<<24)) +#define const_word_in(x) ((const u_int32_t)(((const unsigned char *)(x))[0])|((const u_int32_t)(((const unsigned char *)(x))[1])<<8)|((const u_int32_t)(((const unsigned char *)(x))[2])<<16)|((const u_int32_t)(((const unsigned char *)(x))[3])<<24)) +#define word_out(x,v) ((unsigned char *)(x))[0]=(v),((unsigned char *)(x))[1]=((v)>>8),((unsigned char *)(x))[2]=((v)>>16),((unsigned char *)(x))[3]=((v)>>24) +#define const_word_out(x,v) ((const unsigned char *)(x))[0]=(v),((const unsigned char *)(x))[1]=((v)>>8),((const unsigned char *)(x))[2]=((v)>>16),((const unsigned char *)(x))[3]=((v)>>24) +#endif + +// Disable at least some poor combinations of options + +#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) +#define FIXED_TABLES +#undef UNROLL +#undef ONE_LR_TABLE +#undef FOUR_LR_TABLES +#undef ONE_IM_TABLE +#undef FOUR_IM_TABLES +#elif !defined(FOUR_TABLES) +#ifdef FOUR_LR_TABLES +#undef FOUR_LR_TABLES +#define ONE_LR_TABLE +#endif +#ifdef FOUR_IM_TABLES +#undef FOUR_IM_TABLES +#define ONE_IM_TABLE +#endif +#elif !defined(AES_BLOCK_SIZE) +#if defined(UNROLL) +#define PARTIAL_UNROLL +#undef UNROLL +#endif +#endif + +// the finite field modular polynomial and elements + +#define ff_poly 0x011b +#define ff_hi 0x80 + +// multiply four bytes in GF(2^8) by 'x' {02} in parallel + +#define m1 0x80808080 +#define m2 0x7f7f7f7f +#define m3 0x0000001b +#define FFmulX(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * m3)) + +// The following defines provide alternative definitions of FFmulX that might +// give improved performance if a fast 32-bit multiply is not available. Note +// that a temporary variable u needs to be defined where FFmulX is used. + +// #define FFmulX(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6)) +// #define m4 0x1b1b1b1b +// #define FFmulX(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4) + +// perform column mix operation on four bytes in parallel + +#define fwd_mcol(x) (f2 = FFmulX(x), f2 ^ upr(x ^ f2,3) ^ upr(x,2) ^ upr(x,1)) + +#if defined(FIXED_TABLES) + +// the S-Box table + +static const unsigned char s_box[256] = +{ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +// the inverse S-Box table + +static const unsigned char inv_s_box[256] = +{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, + 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, + 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, + 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, + 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, + 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, + 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, + 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, + 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, + 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, + 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, + 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +}; + +#define w0(p) 0x000000##p + +// Number of elements required in this table for different +// block and key lengths is: +// +// Nk = 4 6 8 +// ---------- +// Nb = 4 | 10 8 7 +// 6 | 19 12 11 +// 8 | 29 19 14 +// +// this table can be a table of bytes if the key schedule +// code is adjusted accordingly + +static const u_int32_t rcon_tab[29] = +{ + w0(01), w0(02), w0(04), w0(08), + w0(10), w0(20), w0(40), w0(80), + w0(1b), w0(36), w0(6c), w0(d8), + w0(ab), w0(4d), w0(9a), w0(2f), + w0(5e), w0(bc), w0(63), w0(c6), + w0(97), w0(35), w0(6a), w0(d4), + w0(b3), w0(7d), w0(fa), w0(ef), + w0(c5) +}; + +#undef w0 + +#define r0(p,q,r,s) 0x##p##q##r##s +#define r1(p,q,r,s) 0x##q##r##s##p +#define r2(p,q,r,s) 0x##r##s##p##q +#define r3(p,q,r,s) 0x##s##p##q##r +#define w0(p) 0x000000##p +#define w1(p) 0x0000##p##00 +#define w2(p) 0x00##p##0000 +#define w3(p) 0x##p##000000 + +#if defined(FIXED_TABLES) && (defined(ONE_TABLE) || defined(FOUR_TABLES)) + +// data for forward tables (other than last round) + +#define f_table \ + r(a5,63,63,c6), r(84,7c,7c,f8), r(99,77,77,ee), r(8d,7b,7b,f6),\ + r(0d,f2,f2,ff), r(bd,6b,6b,d6), r(b1,6f,6f,de), r(54,c5,c5,91),\ + r(50,30,30,60), r(03,01,01,02), r(a9,67,67,ce), r(7d,2b,2b,56),\ + r(19,fe,fe,e7), r(62,d7,d7,b5), r(e6,ab,ab,4d), r(9a,76,76,ec),\ + r(45,ca,ca,8f), r(9d,82,82,1f), r(40,c9,c9,89), r(87,7d,7d,fa),\ + r(15,fa,fa,ef), r(eb,59,59,b2), r(c9,47,47,8e), r(0b,f0,f0,fb),\ + r(ec,ad,ad,41), r(67,d4,d4,b3), r(fd,a2,a2,5f), r(ea,af,af,45),\ + r(bf,9c,9c,23), r(f7,a4,a4,53), r(96,72,72,e4), r(5b,c0,c0,9b),\ + r(c2,b7,b7,75), r(1c,fd,fd,e1), r(ae,93,93,3d), r(6a,26,26,4c),\ + r(5a,36,36,6c), r(41,3f,3f,7e), r(02,f7,f7,f5), r(4f,cc,cc,83),\ + r(5c,34,34,68), r(f4,a5,a5,51), r(34,e5,e5,d1), r(08,f1,f1,f9),\ + r(93,71,71,e2), r(73,d8,d8,ab), r(53,31,31,62), r(3f,15,15,2a),\ + r(0c,04,04,08), r(52,c7,c7,95), r(65,23,23,46), r(5e,c3,c3,9d),\ + r(28,18,18,30), r(a1,96,96,37), r(0f,05,05,0a), r(b5,9a,9a,2f),\ + r(09,07,07,0e), r(36,12,12,24), r(9b,80,80,1b), r(3d,e2,e2,df),\ + r(26,eb,eb,cd), r(69,27,27,4e), r(cd,b2,b2,7f), r(9f,75,75,ea),\ + r(1b,09,09,12), r(9e,83,83,1d), r(74,2c,2c,58), r(2e,1a,1a,34),\ + r(2d,1b,1b,36), r(b2,6e,6e,dc), r(ee,5a,5a,b4), r(fb,a0,a0,5b),\ + r(f6,52,52,a4), r(4d,3b,3b,76), r(61,d6,d6,b7), r(ce,b3,b3,7d),\ + r(7b,29,29,52), r(3e,e3,e3,dd), r(71,2f,2f,5e), r(97,84,84,13),\ + r(f5,53,53,a6), r(68,d1,d1,b9), r(00,00,00,00), r(2c,ed,ed,c1),\ + r(60,20,20,40), r(1f,fc,fc,e3), r(c8,b1,b1,79), r(ed,5b,5b,b6),\ + r(be,6a,6a,d4), r(46,cb,cb,8d), r(d9,be,be,67), r(4b,39,39,72),\ + r(de,4a,4a,94), r(d4,4c,4c,98), r(e8,58,58,b0), r(4a,cf,cf,85),\ + r(6b,d0,d0,bb), r(2a,ef,ef,c5), r(e5,aa,aa,4f), r(16,fb,fb,ed),\ + r(c5,43,43,86), r(d7,4d,4d,9a), r(55,33,33,66), r(94,85,85,11),\ + r(cf,45,45,8a), r(10,f9,f9,e9), r(06,02,02,04), r(81,7f,7f,fe),\ + r(f0,50,50,a0), r(44,3c,3c,78), r(ba,9f,9f,25), r(e3,a8,a8,4b),\ + r(f3,51,51,a2), r(fe,a3,a3,5d), r(c0,40,40,80), r(8a,8f,8f,05),\ + r(ad,92,92,3f), r(bc,9d,9d,21), r(48,38,38,70), r(04,f5,f5,f1),\ + r(df,bc,bc,63), r(c1,b6,b6,77), r(75,da,da,af), r(63,21,21,42),\ + r(30,10,10,20), r(1a,ff,ff,e5), r(0e,f3,f3,fd), r(6d,d2,d2,bf),\ + r(4c,cd,cd,81), r(14,0c,0c,18), r(35,13,13,26), r(2f,ec,ec,c3),\ + r(e1,5f,5f,be), r(a2,97,97,35), r(cc,44,44,88), r(39,17,17,2e),\ + r(57,c4,c4,93), r(f2,a7,a7,55), r(82,7e,7e,fc), r(47,3d,3d,7a),\ + r(ac,64,64,c8), r(e7,5d,5d,ba), r(2b,19,19,32), r(95,73,73,e6),\ + r(a0,60,60,c0), r(98,81,81,19), r(d1,4f,4f,9e), r(7f,dc,dc,a3),\ + r(66,22,22,44), r(7e,2a,2a,54), r(ab,90,90,3b), r(83,88,88,0b),\ + r(ca,46,46,8c), r(29,ee,ee,c7), r(d3,b8,b8,6b), r(3c,14,14,28),\ + r(79,de,de,a7), r(e2,5e,5e,bc), r(1d,0b,0b,16), r(76,db,db,ad),\ + r(3b,e0,e0,db), r(56,32,32,64), r(4e,3a,3a,74), r(1e,0a,0a,14),\ + r(db,49,49,92), r(0a,06,06,0c), r(6c,24,24,48), r(e4,5c,5c,b8),\ + r(5d,c2,c2,9f), r(6e,d3,d3,bd), r(ef,ac,ac,43), r(a6,62,62,c4),\ + r(a8,91,91,39), r(a4,95,95,31), r(37,e4,e4,d3), r(8b,79,79,f2),\ + r(32,e7,e7,d5), r(43,c8,c8,8b), r(59,37,37,6e), r(b7,6d,6d,da),\ + r(8c,8d,8d,01), r(64,d5,d5,b1), r(d2,4e,4e,9c), r(e0,a9,a9,49),\ + r(b4,6c,6c,d8), r(fa,56,56,ac), r(07,f4,f4,f3), r(25,ea,ea,cf),\ + r(af,65,65,ca), r(8e,7a,7a,f4), r(e9,ae,ae,47), r(18,08,08,10),\ + r(d5,ba,ba,6f), r(88,78,78,f0), r(6f,25,25,4a), r(72,2e,2e,5c),\ + r(24,1c,1c,38), r(f1,a6,a6,57), r(c7,b4,b4,73), r(51,c6,c6,97),\ + r(23,e8,e8,cb), r(7c,dd,dd,a1), r(9c,74,74,e8), r(21,1f,1f,3e),\ + r(dd,4b,4b,96), r(dc,bd,bd,61), r(86,8b,8b,0d), r(85,8a,8a,0f),\ + r(90,70,70,e0), r(42,3e,3e,7c), r(c4,b5,b5,71), r(aa,66,66,cc),\ + r(d8,48,48,90), r(05,03,03,06), r(01,f6,f6,f7), r(12,0e,0e,1c),\ + r(a3,61,61,c2), r(5f,35,35,6a), r(f9,57,57,ae), r(d0,b9,b9,69),\ + r(91,86,86,17), r(58,c1,c1,99), r(27,1d,1d,3a), r(b9,9e,9e,27),\ + r(38,e1,e1,d9), r(13,f8,f8,eb), r(b3,98,98,2b), r(33,11,11,22),\ + r(bb,69,69,d2), r(70,d9,d9,a9), r(89,8e,8e,07), r(a7,94,94,33),\ + r(b6,9b,9b,2d), r(22,1e,1e,3c), r(92,87,87,15), r(20,e9,e9,c9),\ + r(49,ce,ce,87), r(ff,55,55,aa), r(78,28,28,50), r(7a,df,df,a5),\ + r(8f,8c,8c,03), r(f8,a1,a1,59), r(80,89,89,09), r(17,0d,0d,1a),\ + r(da,bf,bf,65), r(31,e6,e6,d7), r(c6,42,42,84), r(b8,68,68,d0),\ + r(c3,41,41,82), r(b0,99,99,29), r(77,2d,2d,5a), r(11,0f,0f,1e),\ + r(cb,b0,b0,7b), r(fc,54,54,a8), r(d6,bb,bb,6d), r(3a,16,16,2c) + +// data for inverse tables (other than last round) + +#define i_table \ + r(50,a7,f4,51), r(53,65,41,7e), r(c3,a4,17,1a), r(96,5e,27,3a),\ + r(cb,6b,ab,3b), r(f1,45,9d,1f), r(ab,58,fa,ac), r(93,03,e3,4b),\ + r(55,fa,30,20), r(f6,6d,76,ad), r(91,76,cc,88), r(25,4c,02,f5),\ + r(fc,d7,e5,4f), r(d7,cb,2a,c5), r(80,44,35,26), r(8f,a3,62,b5),\ + r(49,5a,b1,de), r(67,1b,ba,25), r(98,0e,ea,45), r(e1,c0,fe,5d),\ + r(02,75,2f,c3), r(12,f0,4c,81), r(a3,97,46,8d), r(c6,f9,d3,6b),\ + r(e7,5f,8f,03), r(95,9c,92,15), r(eb,7a,6d,bf), r(da,59,52,95),\ + r(2d,83,be,d4), r(d3,21,74,58), r(29,69,e0,49), r(44,c8,c9,8e),\ + r(6a,89,c2,75), r(78,79,8e,f4), r(6b,3e,58,99), r(dd,71,b9,27),\ + r(b6,4f,e1,be), r(17,ad,88,f0), r(66,ac,20,c9), r(b4,3a,ce,7d),\ + r(18,4a,df,63), r(82,31,1a,e5), r(60,33,51,97), r(45,7f,53,62),\ + r(e0,77,64,b1), r(84,ae,6b,bb), r(1c,a0,81,fe), r(94,2b,08,f9),\ + r(58,68,48,70), r(19,fd,45,8f), r(87,6c,de,94), r(b7,f8,7b,52),\ + r(23,d3,73,ab), r(e2,02,4b,72), r(57,8f,1f,e3), r(2a,ab,55,66),\ + r(07,28,eb,b2), r(03,c2,b5,2f), r(9a,7b,c5,86), r(a5,08,37,d3),\ + r(f2,87,28,30), r(b2,a5,bf,23), r(ba,6a,03,02), r(5c,82,16,ed),\ + r(2b,1c,cf,8a), r(92,b4,79,a7), r(f0,f2,07,f3), r(a1,e2,69,4e),\ + r(cd,f4,da,65), r(d5,be,05,06), r(1f,62,34,d1), r(8a,fe,a6,c4),\ + r(9d,53,2e,34), r(a0,55,f3,a2), r(32,e1,8a,05), r(75,eb,f6,a4),\ + r(39,ec,83,0b), r(aa,ef,60,40), r(06,9f,71,5e), r(51,10,6e,bd),\ + r(f9,8a,21,3e), r(3d,06,dd,96), r(ae,05,3e,dd), r(46,bd,e6,4d),\ + r(b5,8d,54,91), r(05,5d,c4,71), r(6f,d4,06,04), r(ff,15,50,60),\ + r(24,fb,98,19), r(97,e9,bd,d6), r(cc,43,40,89), r(77,9e,d9,67),\ + r(bd,42,e8,b0), r(88,8b,89,07), r(38,5b,19,e7), r(db,ee,c8,79),\ + r(47,0a,7c,a1), r(e9,0f,42,7c), r(c9,1e,84,f8), r(00,00,00,00),\ + r(83,86,80,09), r(48,ed,2b,32), r(ac,70,11,1e), r(4e,72,5a,6c),\ + r(fb,ff,0e,fd), r(56,38,85,0f), r(1e,d5,ae,3d), r(27,39,2d,36),\ + r(64,d9,0f,0a), r(21,a6,5c,68), r(d1,54,5b,9b), r(3a,2e,36,24),\ + r(b1,67,0a,0c), r(0f,e7,57,93), r(d2,96,ee,b4), r(9e,91,9b,1b),\ + r(4f,c5,c0,80), r(a2,20,dc,61), r(69,4b,77,5a), r(16,1a,12,1c),\ + r(0a,ba,93,e2), r(e5,2a,a0,c0), r(43,e0,22,3c), r(1d,17,1b,12),\ + r(0b,0d,09,0e), r(ad,c7,8b,f2), r(b9,a8,b6,2d), r(c8,a9,1e,14),\ + r(85,19,f1,57), r(4c,07,75,af), r(bb,dd,99,ee), r(fd,60,7f,a3),\ + r(9f,26,01,f7), r(bc,f5,72,5c), r(c5,3b,66,44), r(34,7e,fb,5b),\ + r(76,29,43,8b), r(dc,c6,23,cb), r(68,fc,ed,b6), r(63,f1,e4,b8),\ + r(ca,dc,31,d7), r(10,85,63,42), r(40,22,97,13), r(20,11,c6,84),\ + r(7d,24,4a,85), r(f8,3d,bb,d2), r(11,32,f9,ae), r(6d,a1,29,c7),\ + r(4b,2f,9e,1d), r(f3,30,b2,dc), r(ec,52,86,0d), r(d0,e3,c1,77),\ + r(6c,16,b3,2b), r(99,b9,70,a9), r(fa,48,94,11), r(22,64,e9,47),\ + r(c4,8c,fc,a8), r(1a,3f,f0,a0), r(d8,2c,7d,56), r(ef,90,33,22),\ + r(c7,4e,49,87), r(c1,d1,38,d9), r(fe,a2,ca,8c), r(36,0b,d4,98),\ + r(cf,81,f5,a6), r(28,de,7a,a5), r(26,8e,b7,da), r(a4,bf,ad,3f),\ + r(e4,9d,3a,2c), r(0d,92,78,50), r(9b,cc,5f,6a), r(62,46,7e,54),\ + r(c2,13,8d,f6), r(e8,b8,d8,90), r(5e,f7,39,2e), r(f5,af,c3,82),\ + r(be,80,5d,9f), r(7c,93,d0,69), r(a9,2d,d5,6f), r(b3,12,25,cf),\ + r(3b,99,ac,c8), r(a7,7d,18,10), r(6e,63,9c,e8), r(7b,bb,3b,db),\ + r(09,78,26,cd), r(f4,18,59,6e), r(01,b7,9a,ec), r(a8,9a,4f,83),\ + r(65,6e,95,e6), r(7e,e6,ff,aa), r(08,cf,bc,21), r(e6,e8,15,ef),\ + r(d9,9b,e7,ba), r(ce,36,6f,4a), r(d4,09,9f,ea), r(d6,7c,b0,29),\ + r(af,b2,a4,31), r(31,23,3f,2a), r(30,94,a5,c6), r(c0,66,a2,35),\ + r(37,bc,4e,74), r(a6,ca,82,fc), r(b0,d0,90,e0), r(15,d8,a7,33),\ + r(4a,98,04,f1), r(f7,da,ec,41), r(0e,50,cd,7f), r(2f,f6,91,17),\ + r(8d,d6,4d,76), r(4d,b0,ef,43), r(54,4d,aa,cc), r(df,04,96,e4),\ + r(e3,b5,d1,9e), r(1b,88,6a,4c), r(b8,1f,2c,c1), r(7f,51,65,46),\ + r(04,ea,5e,9d), r(5d,35,8c,01), r(73,74,87,fa), r(2e,41,0b,fb),\ + r(5a,1d,67,b3), r(52,d2,db,92), r(33,56,10,e9), r(13,47,d6,6d),\ + r(8c,61,d7,9a), r(7a,0c,a1,37), r(8e,14,f8,59), r(89,3c,13,eb),\ + r(ee,27,a9,ce), r(35,c9,61,b7), r(ed,e5,1c,e1), r(3c,b1,47,7a),\ + r(59,df,d2,9c), r(3f,73,f2,55), r(79,ce,14,18), r(bf,37,c7,73),\ + r(ea,cd,f7,53), r(5b,aa,fd,5f), r(14,6f,3d,df), r(86,db,44,78),\ + r(81,f3,af,ca), r(3e,c4,68,b9), r(2c,34,24,38), r(5f,40,a3,c2),\ + r(72,c3,1d,16), r(0c,25,e2,bc), r(8b,49,3c,28), r(41,95,0d,ff),\ + r(71,01,a8,39), r(de,b3,0c,08), r(9c,e4,b4,d8), r(90,c1,56,64),\ + r(61,84,cb,7b), r(70,b6,32,d5), r(74,5c,6c,48), r(42,57,b8,d0) + +// generate the required tables in the desired endian format + +#undef r +#define r r0 + +#if defined(ONE_TABLE) +static const u_int32_t ft_tab[256] = + { f_table }; +#elif defined(FOUR_TABLES) +static const u_int32_t ft_tab[4][256] = +{ { f_table }, +#undef r +#define r r1 + { f_table }, +#undef r +#define r r2 + { f_table }, +#undef r +#define r r3 + { f_table } +}; +#endif + +#undef r +#define r r0 +#if defined(ONE_TABLE) +static const u_int32_t it_tab[256] = + { i_table }; +#elif defined(FOUR_TABLES) +static const u_int32_t it_tab[4][256] = +{ { i_table }, +#undef r +#define r r1 + { i_table }, +#undef r +#define r r2 + { i_table }, +#undef r +#define r r3 + { i_table } +}; +#endif + +#endif + +#if defined(FIXED_TABLES) && (defined(ONE_LR_TABLE) || defined(FOUR_LR_TABLES)) + +// data for inverse tables (last round) + +#define li_table \ + w(52), w(09), w(6a), w(d5), w(30), w(36), w(a5), w(38),\ + w(bf), w(40), w(a3), w(9e), w(81), w(f3), w(d7), w(fb),\ + w(7c), w(e3), w(39), w(82), w(9b), w(2f), w(ff), w(87),\ + w(34), w(8e), w(43), w(44), w(c4), w(de), w(e9), w(cb),\ + w(54), w(7b), w(94), w(32), w(a6), w(c2), w(23), w(3d),\ + w(ee), w(4c), w(95), w(0b), w(42), w(fa), w(c3), w(4e),\ + w(08), w(2e), w(a1), w(66), w(28), w(d9), w(24), w(b2),\ + w(76), w(5b), w(a2), w(49), w(6d), w(8b), w(d1), w(25),\ + w(72), w(f8), w(f6), w(64), w(86), w(68), w(98), w(16),\ + w(d4), w(a4), w(5c), w(cc), w(5d), w(65), w(b6), w(92),\ + w(6c), w(70), w(48), w(50), w(fd), w(ed), w(b9), w(da),\ + w(5e), w(15), w(46), w(57), w(a7), w(8d), w(9d), w(84),\ + w(90), w(d8), w(ab), w(00), w(8c), w(bc), w(d3), w(0a),\ + w(f7), w(e4), w(58), w(05), w(b8), w(b3), w(45), w(06),\ + w(d0), w(2c), w(1e), w(8f), w(ca), w(3f), w(0f), w(02),\ + w(c1), w(af), w(bd), w(03), w(01), w(13), w(8a), w(6b),\ + w(3a), w(91), w(11), w(41), w(4f), w(67), w(dc), w(ea),\ + w(97), w(f2), w(cf), w(ce), w(f0), w(b4), w(e6), w(73),\ + w(96), w(ac), w(74), w(22), w(e7), w(ad), w(35), w(85),\ + w(e2), w(f9), w(37), w(e8), w(1c), w(75), w(df), w(6e),\ + w(47), w(f1), w(1a), w(71), w(1d), w(29), w(c5), w(89),\ + w(6f), w(b7), w(62), w(0e), w(aa), w(18), w(be), w(1b),\ + w(fc), w(56), w(3e), w(4b), w(c6), w(d2), w(79), w(20),\ + w(9a), w(db), w(c0), w(fe), w(78), w(cd), w(5a), w(f4),\ + w(1f), w(dd), w(a8), w(33), w(88), w(07), w(c7), w(31),\ + w(b1), w(12), w(10), w(59), w(27), w(80), w(ec), w(5f),\ + w(60), w(51), w(7f), w(a9), w(19), w(b5), w(4a), w(0d),\ + w(2d), w(e5), w(7a), w(9f), w(93), w(c9), w(9c), w(ef),\ + w(a0), w(e0), w(3b), w(4d), w(ae), w(2a), w(f5), w(b0),\ + w(c8), w(eb), w(bb), w(3c), w(83), w(53), w(99), w(61),\ + w(17), w(2b), w(04), w(7e), w(ba), w(77), w(d6), w(26),\ + w(e1), w(69), w(14), w(63), w(55), w(21), w(0c), w(7d), + +// generate the required tables in the desired endian format + +#undef r +#define r(p,q,r,s) w0(q) +#if defined(ONE_LR_TABLE) +static const u_int32_t fl_tab[256] = + { f_table }; +#elif defined(FOUR_LR_TABLES) +static const u_int32_t fl_tab[4][256] = +{ { f_table }, +#undef r +#define r(p,q,r,s) w1(q) + { f_table }, +#undef r +#define r(p,q,r,s) w2(q) + { f_table }, +#undef r +#define r(p,q,r,s) w3(q) + { f_table } +}; +#endif + +#undef w +#define w w0 +#if defined(ONE_LR_TABLE) +static const u_int32_t il_tab[256] = + { li_table }; +#elif defined(FOUR_LR_TABLES) +static const u_int32_t il_tab[4][256] = +{ { li_table }, +#undef w +#define w w1 + { li_table }, +#undef w +#define w w2 + { li_table }, +#undef w +#define w w3 + { li_table } +}; +#endif + +#endif + +#if defined(FIXED_TABLES) && (defined(ONE_IM_TABLE) || defined(FOUR_IM_TABLES)) + +#define m_table \ + r(00,00,00,00), r(0b,0d,09,0e), r(16,1a,12,1c), r(1d,17,1b,12),\ + r(2c,34,24,38), r(27,39,2d,36), r(3a,2e,36,24), r(31,23,3f,2a),\ + r(58,68,48,70), r(53,65,41,7e), r(4e,72,5a,6c), r(45,7f,53,62),\ + r(74,5c,6c,48), r(7f,51,65,46), r(62,46,7e,54), r(69,4b,77,5a),\ + r(b0,d0,90,e0), r(bb,dd,99,ee), r(a6,ca,82,fc), r(ad,c7,8b,f2),\ + r(9c,e4,b4,d8), r(97,e9,bd,d6), r(8a,fe,a6,c4), r(81,f3,af,ca),\ + r(e8,b8,d8,90), r(e3,b5,d1,9e), r(fe,a2,ca,8c), r(f5,af,c3,82),\ + r(c4,8c,fc,a8), r(cf,81,f5,a6), r(d2,96,ee,b4), r(d9,9b,e7,ba),\ + r(7b,bb,3b,db), r(70,b6,32,d5), r(6d,a1,29,c7), r(66,ac,20,c9),\ + r(57,8f,1f,e3), r(5c,82,16,ed), r(41,95,0d,ff), r(4a,98,04,f1),\ + r(23,d3,73,ab), r(28,de,7a,a5), r(35,c9,61,b7), r(3e,c4,68,b9),\ + r(0f,e7,57,93), r(04,ea,5e,9d), r(19,fd,45,8f), r(12,f0,4c,81),\ + r(cb,6b,ab,3b), r(c0,66,a2,35), r(dd,71,b9,27), r(d6,7c,b0,29),\ + r(e7,5f,8f,03), r(ec,52,86,0d), r(f1,45,9d,1f), r(fa,48,94,11),\ + r(93,03,e3,4b), r(98,0e,ea,45), r(85,19,f1,57), r(8e,14,f8,59),\ + r(bf,37,c7,73), r(b4,3a,ce,7d), r(a9,2d,d5,6f), r(a2,20,dc,61),\ + r(f6,6d,76,ad), r(fd,60,7f,a3), r(e0,77,64,b1), r(eb,7a,6d,bf),\ + r(da,59,52,95), r(d1,54,5b,9b), r(cc,43,40,89), r(c7,4e,49,87),\ + r(ae,05,3e,dd), r(a5,08,37,d3), r(b8,1f,2c,c1), r(b3,12,25,cf),\ + r(82,31,1a,e5), r(89,3c,13,eb), r(94,2b,08,f9), r(9f,26,01,f7),\ + r(46,bd,e6,4d), r(4d,b0,ef,43), r(50,a7,f4,51), r(5b,aa,fd,5f),\ + r(6a,89,c2,75), r(61,84,cb,7b), r(7c,93,d0,69), r(77,9e,d9,67),\ + r(1e,d5,ae,3d), r(15,d8,a7,33), r(08,cf,bc,21), r(03,c2,b5,2f),\ + r(32,e1,8a,05), r(39,ec,83,0b), r(24,fb,98,19), r(2f,f6,91,17),\ + r(8d,d6,4d,76), r(86,db,44,78), r(9b,cc,5f,6a), r(90,c1,56,64),\ + r(a1,e2,69,4e), r(aa,ef,60,40), r(b7,f8,7b,52), r(bc,f5,72,5c),\ + r(d5,be,05,06), r(de,b3,0c,08), r(c3,a4,17,1a), r(c8,a9,1e,14),\ + r(f9,8a,21,3e), r(f2,87,28,30), r(ef,90,33,22), r(e4,9d,3a,2c),\ + r(3d,06,dd,96), r(36,0b,d4,98), r(2b,1c,cf,8a), r(20,11,c6,84),\ + r(11,32,f9,ae), r(1a,3f,f0,a0), r(07,28,eb,b2), r(0c,25,e2,bc),\ + r(65,6e,95,e6), r(6e,63,9c,e8), r(73,74,87,fa), r(78,79,8e,f4),\ + r(49,5a,b1,de), r(42,57,b8,d0), r(5f,40,a3,c2), r(54,4d,aa,cc),\ + r(f7,da,ec,41), r(fc,d7,e5,4f), r(e1,c0,fe,5d), r(ea,cd,f7,53),\ + r(db,ee,c8,79), r(d0,e3,c1,77), r(cd,f4,da,65), r(c6,f9,d3,6b),\ + r(af,b2,a4,31), r(a4,bf,ad,3f), r(b9,a8,b6,2d), r(b2,a5,bf,23),\ + r(83,86,80,09), r(88,8b,89,07), r(95,9c,92,15), r(9e,91,9b,1b),\ + r(47,0a,7c,a1), r(4c,07,75,af), r(51,10,6e,bd), r(5a,1d,67,b3),\ + r(6b,3e,58,99), r(60,33,51,97), r(7d,24,4a,85), r(76,29,43,8b),\ + r(1f,62,34,d1), r(14,6f,3d,df), r(09,78,26,cd), r(02,75,2f,c3),\ + r(33,56,10,e9), r(38,5b,19,e7), r(25,4c,02,f5), r(2e,41,0b,fb),\ + r(8c,61,d7,9a), r(87,6c,de,94), r(9a,7b,c5,86), r(91,76,cc,88),\ + r(a0,55,f3,a2), r(ab,58,fa,ac), r(b6,4f,e1,be), r(bd,42,e8,b0),\ + r(d4,09,9f,ea), r(df,04,96,e4), r(c2,13,8d,f6), r(c9,1e,84,f8),\ + r(f8,3d,bb,d2), r(f3,30,b2,dc), r(ee,27,a9,ce), r(e5,2a,a0,c0),\ + r(3c,b1,47,7a), r(37,bc,4e,74), r(2a,ab,55,66), r(21,a6,5c,68),\ + r(10,85,63,42), r(1b,88,6a,4c), r(06,9f,71,5e), r(0d,92,78,50),\ + r(64,d9,0f,0a), r(6f,d4,06,04), r(72,c3,1d,16), r(79,ce,14,18),\ + r(48,ed,2b,32), r(43,e0,22,3c), r(5e,f7,39,2e), r(55,fa,30,20),\ + r(01,b7,9a,ec), r(0a,ba,93,e2), r(17,ad,88,f0), r(1c,a0,81,fe),\ + r(2d,83,be,d4), r(26,8e,b7,da), r(3b,99,ac,c8), r(30,94,a5,c6),\ + r(59,df,d2,9c), r(52,d2,db,92), r(4f,c5,c0,80), r(44,c8,c9,8e),\ + r(75,eb,f6,a4), r(7e,e6,ff,aa), r(63,f1,e4,b8), r(68,fc,ed,b6),\ + r(b1,67,0a,0c), r(ba,6a,03,02), r(a7,7d,18,10), r(ac,70,11,1e),\ + r(9d,53,2e,34), r(96,5e,27,3a), r(8b,49,3c,28), r(80,44,35,26),\ + r(e9,0f,42,7c), r(e2,02,4b,72), r(ff,15,50,60), r(f4,18,59,6e),\ + r(c5,3b,66,44), r(ce,36,6f,4a), r(d3,21,74,58), r(d8,2c,7d,56),\ + r(7a,0c,a1,37), r(71,01,a8,39), r(6c,16,b3,2b), r(67,1b,ba,25),\ + r(56,38,85,0f), r(5d,35,8c,01), r(40,22,97,13), r(4b,2f,9e,1d),\ + r(22,64,e9,47), r(29,69,e0,49), r(34,7e,fb,5b), r(3f,73,f2,55),\ + r(0e,50,cd,7f), r(05,5d,c4,71), r(18,4a,df,63), r(13,47,d6,6d),\ + r(ca,dc,31,d7), r(c1,d1,38,d9), r(dc,c6,23,cb), r(d7,cb,2a,c5),\ + r(e6,e8,15,ef), r(ed,e5,1c,e1), r(f0,f2,07,f3), r(fb,ff,0e,fd),\ + r(92,b4,79,a7), r(99,b9,70,a9), r(84,ae,6b,bb), r(8f,a3,62,b5),\ + r(be,80,5d,9f), r(b5,8d,54,91), r(a8,9a,4f,83), r(a3,97,46,8d) + +#undef r +#define r r0 + +#if defined(ONE_IM_TABLE) +static const u_int32_t im_tab[256] = + { m_table }; +#elif defined(FOUR_IM_TABLES) +static const u_int32_t im_tab[4][256] = +{ { m_table }, +#undef r +#define r r1 + { m_table }, +#undef r +#define r r2 + { m_table }, +#undef r +#define r r3 + { m_table } +}; +#endif + +#endif + +#else + +static int tab_gen = 0; + +static unsigned char s_box[256]; // the S box +static unsigned char inv_s_box[256]; // the inverse S box +static u_int32_t rcon_tab[AES_RC_LENGTH]; // table of round constants + +#if defined(ONE_TABLE) +static u_int32_t ft_tab[256]; +static u_int32_t it_tab[256]; +#elif defined(FOUR_TABLES) +static u_int32_t ft_tab[4][256]; +static u_int32_t it_tab[4][256]; +#endif + +#if defined(ONE_LR_TABLE) +static u_int32_t fl_tab[256]; +static u_int32_t il_tab[256]; +#elif defined(FOUR_LR_TABLES) +static u_int32_t fl_tab[4][256]; +static u_int32_t il_tab[4][256]; +#endif + +#if defined(ONE_IM_TABLE) +static u_int32_t im_tab[256]; +#elif defined(FOUR_IM_TABLES) +static u_int32_t im_tab[4][256]; +#endif + +// Generate the tables for the dynamic table option + +#if !defined(FF_TABLES) + +// It will generally be sensible to use tables to compute finite +// field multiplies and inverses but where memory is scarse this +// code might sometimes be better. + +// return 2 ^ (n - 1) where n is the bit number of the highest bit +// set in x with x in the range 1 < x < 0x00000200. This form is +// used so that locals within FFinv can be bytes rather than words + +static unsigned char hibit(const u_int32_t x) +{ unsigned char r = (unsigned char)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +// return the inverse of the finite field element x + +static unsigned char FFinv(const unsigned char x) +{ unsigned char p1 = x, p2 = 0x1b, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) return x; + + for(;;) + { + if(!n1) return v1; + + while(n2 >= n1) + { + n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2); + } + + if(!n2) return v2; + + while(n1 >= n2) + { + n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1); + } + } +} + +// define the finite field multiplies required for Rijndael + +#define FFmul02(x) ((((x) & 0x7f) << 1) ^ ((x) & 0x80 ? 0x1b : 0)) +#define FFmul03(x) ((x) ^ FFmul02(x)) +#define FFmul09(x) ((x) ^ FFmul02(FFmul02(FFmul02(x)))) +#define FFmul0b(x) ((x) ^ FFmul02((x) ^ FFmul02(FFmul02(x)))) +#define FFmul0d(x) ((x) ^ FFmul02(FFmul02((x) ^ FFmul02(x)))) +#define FFmul0e(x) FFmul02((x) ^ FFmul02((x) ^ FFmul02(x))) + +#else + +#define FFinv(x) ((x) ? pow[255 - log[x]]: 0) + +#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0) +#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0) +#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0) +#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0) +#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0) +#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0) + +#endif + +// The forward and inverse affine transformations used in the S-box + +#define fwd_affine(x) \ + (w = (u_int32_t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(unsigned char)(w^(w>>8))) + +#define inv_affine(x) \ + (w = (u_int32_t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(unsigned char)(w^(w>>8))) + +static void gen_tabs(void) +{ u_int32_t i, w; + +#if defined(FF_TABLES) + + unsigned char pow[512], log[256]; + + // log and power tables for GF(2^8) finite field with + // 0x011b as modular polynomial - the simplest primitive + // root is 0x03, used here to generate the tables + + i = 0; w = 1; + do + { + pow[i] = (unsigned char)w; + pow[i + 255] = (unsigned char)w; + log[w] = (unsigned char)i++; + w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0); + } + while (w != 1); + +#endif + + for(i = 0, w = 1; i < AES_RC_LENGTH; ++i) + { + rcon_tab[i] = bytes2word(w, 0, 0, 0); + w = (w << 1) ^ (w & ff_hi ? ff_poly : 0); + } + + for(i = 0; i < 256; ++i) + { unsigned char b; + + s_box[i] = b = fwd_affine(FFinv((unsigned char)i)); + + w = bytes2word(b, 0, 0, 0); +#if defined(ONE_LR_TABLE) + fl_tab[i] = w; +#elif defined(FOUR_LR_TABLES) + fl_tab[0][i] = w; + fl_tab[1][i] = upr(w,1); + fl_tab[2][i] = upr(w,2); + fl_tab[3][i] = upr(w,3); +#endif + w = bytes2word(FFmul02(b), b, b, FFmul03(b)); +#if defined(ONE_TABLE) + ft_tab[i] = w; +#elif defined(FOUR_TABLES) + ft_tab[0][i] = w; + ft_tab[1][i] = upr(w,1); + ft_tab[2][i] = upr(w,2); + ft_tab[3][i] = upr(w,3); +#endif + inv_s_box[i] = b = FFinv(inv_affine((unsigned char)i)); + + w = bytes2word(b, 0, 0, 0); +#if defined(ONE_LR_TABLE) + il_tab[i] = w; +#elif defined(FOUR_LR_TABLES) + il_tab[0][i] = w; + il_tab[1][i] = upr(w,1); + il_tab[2][i] = upr(w,2); + il_tab[3][i] = upr(w,3); +#endif + w = bytes2word(FFmul0e(b), FFmul09(b), FFmul0d(b), FFmul0b(b)); +#if defined(ONE_TABLE) + it_tab[i] = w; +#elif defined(FOUR_TABLES) + it_tab[0][i] = w; + it_tab[1][i] = upr(w,1); + it_tab[2][i] = upr(w,2); + it_tab[3][i] = upr(w,3); +#endif +#if defined(ONE_IM_TABLE) + im_tab[b] = w; +#elif defined(FOUR_IM_TABLES) + im_tab[0][b] = w; + im_tab[1][b] = upr(w,1); + im_tab[2][b] = upr(w,2); + im_tab[3][b] = upr(w,3); +#endif + + } +} + +#endif + +#define no_table(x,box,vf,rf,c) bytes2word( \ + box[bval(vf(x,0,c),rf(0,c))], \ + box[bval(vf(x,1,c),rf(1,c))], \ + box[bval(vf(x,2,c),rf(2,c))], \ + box[bval(vf(x,3,c),rf(3,c))]) + +#define one_table(x,op,tab,vf,rf,c) \ + ( tab[bval(vf(x,0,c),rf(0,c))] \ + ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \ + ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \ + ^ op(tab[bval(vf(x,3,c),rf(3,c))],3)) + +#define four_tables(x,tab,vf,rf,c) \ + ( tab[0][bval(vf(x,0,c),rf(0,c))] \ + ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ + ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ + ^ tab[3][bval(vf(x,3,c),rf(3,c))]) + +#define vf1(x,r,c) (x) +#define rf1(r,c) (r) +#define rf2(r,c) ((r-c)&3) + +#if defined(FOUR_LR_TABLES) +#define ls_box(x,c) four_tables(x,fl_tab,vf1,rf2,c) +#elif defined(ONE_LR_TABLE) +#define ls_box(x,c) one_table(x,upr,fl_tab,vf1,rf2,c) +#else +#define ls_box(x,c) no_table(x,s_box,vf1,rf2,c) +#endif + +#if defined(FOUR_IM_TABLES) +#define inv_mcol(x) four_tables(x,im_tab,vf1,rf1,0) +#elif defined(ONE_IM_TABLE) +#define inv_mcol(x) one_table(x,upr,im_tab,vf1,rf1,0) +#else +#define inv_mcol(x) \ + (f9 = (x),f2 = FFmulX(f9), f4 = FFmulX(f2), f8 = FFmulX(f4), f9 ^= f8, \ + f2 ^= f4 ^ f8 ^ upr(f2 ^ f9,3) ^ upr(f4 ^ f9,2) ^ upr(f9,1)) +#endif + +#define nc (AES_BLOCK_SIZE/4) + +// Initialise the key schedule from the user supplied key. The key +// length is now specified in bytes - 16, 24 or 32 as appropriate. +// This corresponds to bit lengths of 128, 192 and 256 bits, and +// to Nk values of 4, 6 and 8 respectively. + +#define mx(t,f) (*t++ = inv_mcol(*f),f++) +#define cp(t,f) *t++ = *f++ + +#if AES_BLOCK_SIZE == 16 +#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s) +#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s) +#elif AES_BLOCK_SIZE == 24 +#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \ + cp(d,s); cp(d,s) +#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \ + mx(d,s); mx(d,s) +#elif AES_BLOCK_SIZE == 32 +#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \ + cp(d,s); cp(d,s); cp(d,s); cp(d,s) +#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \ + mx(d,s); mx(d,s); mx(d,s); mx(d,s) +#else + +#define cpy(d,s) \ +switch(nc) \ +{ case 8: cp(d,s); cp(d,s); \ + case 6: cp(d,s); cp(d,s); \ + case 4: cp(d,s); cp(d,s); \ + cp(d,s); cp(d,s); \ +} + +#define mix(d,s) \ +switch(nc) \ +{ case 8: mx(d,s); mx(d,s); \ + case 6: mx(d,s); mx(d,s); \ + case 4: mx(d,s); mx(d,s); \ + mx(d,s); mx(d,s); \ +} + +#endif + +// y = output word, x = input word, r = row, c = column +// for r = 0, 1, 2 and 3 = column accessed for row r + +#if defined(ARRAYS) +#define s(x,c) x[c] +#else +#define s(x,c) x##c +#endif + +// I am grateful to Frank Yellin for the following constructions +// which, given the column (c) of the output state variable that +// is being computed, return the input state variables which are +// needed for each row (r) of the state + +// For the fixed block size options, compilers reduce these two +// expressions to fixed variable references. For variable block +// size code conditional clauses will sometimes be returned + +#define unused 77 // Sunset Strip + +#define fwd_var(x,r,c) \ + ( r==0 ? \ + ( c==0 ? s(x,0) \ + : c==1 ? s(x,1) \ + : c==2 ? s(x,2) \ + : c==3 ? s(x,3) \ + : c==4 ? s(x,4) \ + : c==5 ? s(x,5) \ + : c==6 ? s(x,6) \ + : s(x,7)) \ + : r==1 ? \ + ( c==0 ? s(x,1) \ + : c==1 ? s(x,2) \ + : c==2 ? s(x,3) \ + : c==3 ? nc==4 ? s(x,0) : s(x,4) \ + : c==4 ? s(x,5) \ + : c==5 ? nc==8 ? s(x,6) : s(x,0) \ + : c==6 ? s(x,7) \ + : s(x,0)) \ + : r==2 ? \ + ( c==0 ? nc==8 ? s(x,3) : s(x,2) \ + : c==1 ? nc==8 ? s(x,4) : s(x,3) \ + : c==2 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \ + : c==3 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \ + : c==4 ? nc==8 ? s(x,7) : s(x,0) \ + : c==5 ? nc==8 ? s(x,0) : s(x,1) \ + : c==6 ? s(x,1) \ + : s(x,2)) \ + : \ + ( c==0 ? nc==8 ? s(x,4) : s(x,3) \ + : c==1 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \ + : c==2 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \ + : c==3 ? nc==4 ? s(x,2) : nc==8 ? s(x,7) : s(x,0) \ + : c==4 ? nc==8 ? s(x,0) : s(x,1) \ + : c==5 ? nc==8 ? s(x,1) : s(x,2) \ + : c==6 ? s(x,2) \ + : s(x,3))) + +#define inv_var(x,r,c) \ + ( r==0 ? \ + ( c==0 ? s(x,0) \ + : c==1 ? s(x,1) \ + : c==2 ? s(x,2) \ + : c==3 ? s(x,3) \ + : c==4 ? s(x,4) \ + : c==5 ? s(x,5) \ + : c==6 ? s(x,6) \ + : s(x,7)) \ + : r==1 ? \ + ( c==0 ? nc==4 ? s(x,3) : nc==8 ? s(x,7) : s(x,5) \ + : c==1 ? s(x,0) \ + : c==2 ? s(x,1) \ + : c==3 ? s(x,2) \ + : c==4 ? s(x,3) \ + : c==5 ? s(x,4) \ + : c==6 ? s(x,5) \ + : s(x,6)) \ + : r==2 ? \ + ( c==0 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \ + : c==1 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \ + : c==2 ? nc==8 ? s(x,7) : s(x,0) \ + : c==3 ? nc==8 ? s(x,0) : s(x,1) \ + : c==4 ? nc==8 ? s(x,1) : s(x,2) \ + : c==5 ? nc==8 ? s(x,2) : s(x,3) \ + : c==6 ? s(x,3) \ + : s(x,4)) \ + : \ + ( c==0 ? nc==4 ? s(x,1) : nc==8 ? s(x,4) : s(x,3) \ + : c==1 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \ + : c==2 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \ + : c==3 ? nc==8 ? s(x,7) : s(x,0) \ + : c==4 ? nc==8 ? s(x,0) : s(x,1) \ + : c==5 ? nc==8 ? s(x,1) : s(x,2) \ + : c==6 ? s(x,2) \ + : s(x,3))) + +#define si(y,x,k,c) s(y,c) = const_word_in(x + 4 * c) ^ k[c] +#define so(y,x,c) word_out(y + 4 * c, s(x,c)) + +#if defined(FOUR_TABLES) +#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,ft_tab,fwd_var,rf1,c) +#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,it_tab,inv_var,rf1,c) +#elif defined(ONE_TABLE) +#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,ft_tab,fwd_var,rf1,c) +#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,it_tab,inv_var,rf1,c) +#else +#define fwd_rnd(y,x,k,c) s(y,c) = fwd_mcol(no_table(x,s_box,fwd_var,rf1,c)) ^ (k)[c] +#define inv_rnd(y,x,k,c) s(y,c) = inv_mcol(no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c]) +#endif + +#if defined(FOUR_LR_TABLES) +#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,fl_tab,fwd_var,rf1,c) +#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,il_tab,inv_var,rf1,c) +#elif defined(ONE_LR_TABLE) +#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,fl_tab,fwd_var,rf1,c) +#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,il_tab,inv_var,rf1,c) +#else +#define fwd_lrnd(y,x,k,c) s(y,c) = no_table(x,s_box,fwd_var,rf1,c) ^ (k)[c] +#define inv_lrnd(y,x,k,c) s(y,c) = no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c] +#endif + +#if AES_BLOCK_SIZE == 16 + +#if defined(ARRAYS) +#define locals(y,x) x[4],y[4] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3 +// the following defines prevent the compiler requiring the declaration +// of generated but unused variables in the fwd_var and inv_var macros +#define b04 unused +#define b05 unused +#define b06 unused +#define b07 unused +#define b14 unused +#define b15 unused +#define b16 unused +#define b17 unused +#endif +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) + +#elif AES_BLOCK_SIZE == 24 + +#if defined(ARRAYS) +#define locals(y,x) x[6],y[6] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5, \ + y##0,y##1,y##2,y##3,y##4,y##5 +#define b06 unused +#define b07 unused +#define b16 unused +#define b17 unused +#endif +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); \ + s(y,4) = s(x,4); s(y,5) = s(x,5); +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); \ + si(y,x,k,3); si(y,x,k,4); si(y,x,k,5) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); \ + so(y,x,3); so(y,x,4); so(y,x,5) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); \ + rm(y,x,k,3); rm(y,x,k,4); rm(y,x,k,5) +#else + +#if defined(ARRAYS) +#define locals(y,x) x[8],y[8] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5,x##6,x##7, \ + y##0,y##1,y##2,y##3,y##4,y##5,y##6,y##7 +#endif +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); \ + s(y,4) = s(x,4); s(y,5) = s(x,5); \ + s(y,6) = s(x,6); s(y,7) = s(x,7); + +#if AES_BLOCK_SIZE == 32 + +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3); \ + si(y,x,k,4); si(y,x,k,5); si(y,x,k,6); si(y,x,k,7) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3); \ + so(y,x,4); so(y,x,5); so(y,x,6); so(y,x,7) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3); \ + rm(y,x,k,4); rm(y,x,k,5); rm(y,x,k,6); rm(y,x,k,7) +#else + +#define state_in(y,x,k) \ +switch(nc) \ +{ case 8: si(y,x,k,7); si(y,x,k,6); \ + case 6: si(y,x,k,5); si(y,x,k,4); \ + case 4: si(y,x,k,3); si(y,x,k,2); \ + si(y,x,k,1); si(y,x,k,0); \ +} + +#define state_out(y,x) \ +switch(nc) \ +{ case 8: so(y,x,7); so(y,x,6); \ + case 6: so(y,x,5); so(y,x,4); \ + case 4: so(y,x,3); so(y,x,2); \ + so(y,x,1); so(y,x,0); \ +} + +#if defined(FAST_VARIABLE) + +#define round(rm,y,x,k) \ +switch(nc) \ +{ case 8: rm(y,x,k,7); rm(y,x,k,6); \ + rm(y,x,k,5); rm(y,x,k,4); \ + rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ + break; \ + case 6: rm(y,x,k,5); rm(y,x,k,4); \ + rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ + break; \ + case 4: rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ + break; \ +} +#else + +#define round(rm,y,x,k) \ +switch(nc) \ +{ case 8: rm(y,x,k,7); rm(y,x,k,6); \ + case 6: rm(y,x,k,5); rm(y,x,k,4); \ + case 4: rm(y,x,k,3); rm(y,x,k,2); \ + rm(y,x,k,1); rm(y,x,k,0); \ +} + +#endif + +#endif +#endif + +/** + * Implementation of private_aes_cbc_crypter_t.encrypt_block. + */ +static void encrypt_block(const private_aes_cbc_crypter_t *this, const unsigned char in_blk[], unsigned char out_blk[]) +{ u_int32_t locals(b0, b1); + const u_int32_t *kp = this->aes_e_key; + +#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) + u_int32_t f2; +#endif + + state_in(b0, in_blk, kp); kp += nc; + +#if defined(UNROLL) + + switch(this->aes_Nrnd) + { + case 14: round(fwd_rnd, b1, b0, kp ); + round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 12: round(fwd_rnd, b1, b0, kp ); + round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 10: round(fwd_rnd, b1, b0, kp ); + round(fwd_rnd, b0, b1, kp + nc); + round(fwd_rnd, b1, b0, kp + 2 * nc); + round(fwd_rnd, b0, b1, kp + 3 * nc); + round(fwd_rnd, b1, b0, kp + 4 * nc); + round(fwd_rnd, b0, b1, kp + 5 * nc); + round(fwd_rnd, b1, b0, kp + 6 * nc); + round(fwd_rnd, b0, b1, kp + 7 * nc); + round(fwd_rnd, b1, b0, kp + 8 * nc); + round(fwd_lrnd, b0, b1, kp + 9 * nc); + } + +#elif defined(PARTIAL_UNROLL) + { u_int32_t rnd; + + for(rnd = 0; rnd < (this->aes_Nrnd >> 1) - 1; ++rnd) + { + round(fwd_rnd, b1, b0, kp); + round(fwd_rnd, b0, b1, kp + nc); kp += 2 * nc; + } + + round(fwd_rnd, b1, b0, kp); + round(fwd_lrnd, b0, b1, kp + nc); + } +#else + { u_int32_t rnd; + + for(rnd = 0; rnd < this->aes_Nrnd - 1; ++rnd) + { + round(fwd_rnd, b1, b0, kp); + l_copy(b0, b1); kp += nc; + } + + round(fwd_lrnd, b0, b1, kp); + } +#endif + + state_out(out_blk, b0); +} + +/** + * Implementation of private_aes_cbc_crypter_t.decrypt_block. + */ +static void decrypt_block(const private_aes_cbc_crypter_t *this, const unsigned char in_blk[], unsigned char out_blk[]) +{ u_int32_t locals(b0, b1); + const u_int32_t *kp = this->aes_d_key; + +#if !defined(ONE_TABLE) && !defined(FOUR_TABLES) + u_int32_t f2, f4, f8, f9; +#endif + + state_in(b0, in_blk, kp); kp += nc; + +#if defined(UNROLL) + + switch(this->aes_Nrnd) + { + case 14: round(inv_rnd, b1, b0, kp ); + round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 12: round(inv_rnd, b1, b0, kp ); + round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc; + case 10: round(inv_rnd, b1, b0, kp ); + round(inv_rnd, b0, b1, kp + nc); + round(inv_rnd, b1, b0, kp + 2 * nc); + round(inv_rnd, b0, b1, kp + 3 * nc); + round(inv_rnd, b1, b0, kp + 4 * nc); + round(inv_rnd, b0, b1, kp + 5 * nc); + round(inv_rnd, b1, b0, kp + 6 * nc); + round(inv_rnd, b0, b1, kp + 7 * nc); + round(inv_rnd, b1, b0, kp + 8 * nc); + round(inv_lrnd, b0, b1, kp + 9 * nc); + } + +#elif defined(PARTIAL_UNROLL) + { u_int32_t rnd; + + for(rnd = 0; rnd < (this->aes_Nrnd >> 1) - 1; ++rnd) + { + round(inv_rnd, b1, b0, kp); + round(inv_rnd, b0, b1, kp + nc); kp += 2 * nc; + } + + round(inv_rnd, b1, b0, kp); + round(inv_lrnd, b0, b1, kp + nc); + } +#else + { u_int32_t rnd; + + for(rnd = 0; rnd < this->aes_Nrnd - 1; ++rnd) + { + round(inv_rnd, b1, b0, kp); + l_copy(b0, b1); kp += nc; + } + + round(inv_lrnd, b0, b1, kp); + } +#endif + + state_out(out_blk, b0); +} + +/** + * Implementation of crypter_t.decrypt. + */ +static status_t decrypt (private_aes_cbc_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted) +{ + int ret, pos; + const u_int32_t *iv_i; + u_int8_t *in, *out; + + ret = data.len; + if (((data.len) % 16) != 0) + { + /* data length must be padded to a multiple of blocksize */ + return INVALID_ARG; + } + + decrypted->ptr = malloc(data.len); + if (decrypted->ptr == NULL) + { + return OUT_OF_RES; + } + decrypted->len = data.len; + + in = data.ptr; + out = decrypted->ptr; + + pos=data.len-16; + in+=pos; + out+=pos; + while(pos>=0) { + this->decrypt_block(this,in,out); + if (pos==0) + iv_i=(const u_int32_t*) (iv.ptr); + else + iv_i=(const u_int32_t*) (in-16); + *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; + *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; + *((u_int32_t *)(&out[ 8])) ^= iv_i[2]; + *((u_int32_t *)(&out[12])) ^= iv_i[3]; + in-=16; + out-=16; + pos-=16; + } + + return SUCCESS; +} + + +/** + * Implementation of crypter_t.decrypt. + */ +static status_t encrypt (private_aes_cbc_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted) +{ + int ret, pos; + const u_int32_t *iv_i; + u_int8_t *in, *out; + + ret = data.len; + if (((data.len) % 16) != 0) + { + /* data length must be padded to a multiple of blocksize */ + return INVALID_ARG; + } + + encrypted->ptr = malloc(data.len); + if (encrypted->ptr == NULL) + { + return OUT_OF_RES; + } + encrypted->len = data.len; + + in = data.ptr; + out = encrypted->ptr; + + pos=0; + while(posencrypt_block(this,out,out); + in+=16; + out+=16; + pos+=16; + } + return SUCCESS; +} + +/** + * Implementation of crypter_t.get_block_size. + */ +static size_t get_block_size (private_aes_cbc_crypter_t *this) +{ + return AES_BLOCK_SIZE; +} + +/** + * Implementation of crypter_t.get_key_size. + */ +static size_t get_key_size (private_aes_cbc_crypter_t *this) +{ + return this->key_size; +} + +/** + * Implementation of crypter_t.set_key. + */ +static status_t set_key (private_aes_cbc_crypter_t *this, chunk_t key) +{ + u_int32_t *kf, *kt, rci, f = 0; + u_int8_t *in_key = key.ptr; + + if (key.len != this->key_size) + { + return INVALID_ARG; + } + + this->aes_Nrnd = (this->aes_Nkey > (nc) ? this->aes_Nkey : (nc)) + 6; + + this->aes_e_key[0] = const_word_in(in_key ); + this->aes_e_key[1] = const_word_in(in_key + 4); + this->aes_e_key[2] = const_word_in(in_key + 8); + this->aes_e_key[3] = const_word_in(in_key + 12); + + kf = this->aes_e_key; + kt = kf + nc * (this->aes_Nrnd + 1) - this->aes_Nkey; + rci = 0; + + switch(this->aes_Nkey) + { + case 4: do + { kf[4] = kf[0] ^ ls_box(kf[3],3) ^ rcon_tab[rci++]; + kf[5] = kf[1] ^ kf[4]; + kf[6] = kf[2] ^ kf[5]; + kf[7] = kf[3] ^ kf[6]; + kf += 4; + } + while(kf < kt); + break; + + case 6: this->aes_e_key[4] = const_word_in(in_key + 16); + this->aes_e_key[5] = const_word_in(in_key + 20); + do + { kf[ 6] = kf[0] ^ ls_box(kf[5],3) ^ rcon_tab[rci++]; + kf[ 7] = kf[1] ^ kf[ 6]; + kf[ 8] = kf[2] ^ kf[ 7]; + kf[ 9] = kf[3] ^ kf[ 8]; + kf[10] = kf[4] ^ kf[ 9]; + kf[11] = kf[5] ^ kf[10]; + kf += 6; + } + while(kf < kt); + break; + + case 8: this->aes_e_key[4] = const_word_in(in_key + 16); + this->aes_e_key[5] = const_word_in(in_key + 20); + this->aes_e_key[6] = const_word_in(in_key + 24); + this->aes_e_key[7] = const_word_in(in_key + 28); + do + { kf[ 8] = kf[0] ^ ls_box(kf[7],3) ^ rcon_tab[rci++]; + kf[ 9] = kf[1] ^ kf[ 8]; + kf[10] = kf[2] ^ kf[ 9]; + kf[11] = kf[3] ^ kf[10]; + kf[12] = kf[4] ^ ls_box(kf[11],0); + kf[13] = kf[5] ^ kf[12]; + kf[14] = kf[6] ^ kf[13]; + kf[15] = kf[7] ^ kf[14]; + kf += 8; + } + while (kf < kt); + break; + } + + if(!f) + { + u_int32_t i; + + kt = this->aes_d_key + nc * this->aes_Nrnd; + kf = this->aes_e_key; + + cpy(kt, kf); kt -= 2 * nc; + + for(i = 1; i < this->aes_Nrnd; ++i) + { +#if defined(ONE_TABLE) || defined(FOUR_TABLES) +#if !defined(ONE_IM_TABLE) && !defined(FOUR_IM_TABLES) + u_int32_t f2, f4, f8, f9; +#endif + mix(kt, kf); +#else + cpy(kt, kf); +#endif + kt -= 2 * nc; + } + cpy(kt, kf); + } + + return SUCCESS; +} + +/** + * Implementation of crypter_t.destroy and aes_cbc_crypter_t.destroy. + */ +static void destroy (private_aes_cbc_crypter_t *this) +{ + free(this); +} + +/* + * Described in header + */ +aes_cbc_crypter_t *aes_cbc_crypter_create(size_t key_size) +{ + private_aes_cbc_crypter_t *this = malloc_thing(private_aes_cbc_crypter_t); + + #if !defined(FIXED_TABLES) + if(!tab_gen) { gen_tabs(); tab_gen = 1; } + #endif + + this->key_size = key_size; + switch(key_size) { + case 32: /* bytes */ + this->aes_Nkey = 8; + break; + case 24: /* bytes */ + this->aes_Nkey = 6; + break; + case 16: /* bytes */ + this->aes_Nkey = 4; + break; + default: + free(this); + return NULL; + } + + /* functions of crypter_t interface */ + this->public.crypter_interface.encrypt = (status_t (*) (crypter_t *, chunk_t,chunk_t, chunk_t *)) encrypt; + this->public.crypter_interface.decrypt = (status_t (*) (crypter_t *, chunk_t , chunk_t, chunk_t *)) decrypt; + this->public.crypter_interface.get_block_size = (size_t (*) (crypter_t *)) get_block_size; + this->public.crypter_interface.get_key_size = (size_t (*) (crypter_t *)) get_key_size; + this->public.crypter_interface.set_key = (status_t (*) (crypter_t *,chunk_t)) set_key; + this->public.crypter_interface.destroy = (void (*) (crypter_t *)) destroy; + + /* private functions */ + this->decrypt_block = decrypt_block; + this->encrypt_block = encrypt_block; + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/crypters/aes_cbc_crypter.h b/src/libstrongswan/crypto/crypters/aes_cbc_crypter.h new file mode 100644 index 000000000..5da248b8c --- /dev/null +++ b/src/libstrongswan/crypto/crypters/aes_cbc_crypter.h @@ -0,0 +1,61 @@ +/** + * @file aes_cbc_crypter.h + * + * @brief Interface of aes_cbc_crypter_t + * + */ + +/* + * Copyright (C) 2001 Dr B. R. Gladman + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef AES_CBC_CRYPTER_H_ +#define AES_CBC_CRYPTER_H_ + +typedef struct aes_cbc_crypter_t aes_cbc_crypter_t; + +#include + +/** + * @brief Class implementing the AES symmetric encryption algorithm. + * + * @b Constructors: + * - aes_cbc_crypter_create() + * + * @ingroup crypters + */ +struct aes_cbc_crypter_t { + + /** + * The crypter_t interface. + */ + crypter_t crypter_interface; +}; + +/** + * @brief Constructor to create aes_cbc_crypter_t objects. + * + * Supported key sizes are: 16, 24 or 32. + * + * @param key_size key size in bytes + * @return + * - aes_cbc_crypter_t object + * - NULL if key size not supported + */ +aes_cbc_crypter_t *aes_cbc_crypter_create(size_t key_size); + + +#endif /* AES_CBC_CRYPTER_H_ */ diff --git a/src/libstrongswan/crypto/crypters/crypter.c b/src/libstrongswan/crypto/crypters/crypter.c new file mode 100644 index 000000000..7f62741a7 --- /dev/null +++ b/src/libstrongswan/crypto/crypters/crypter.c @@ -0,0 +1,68 @@ +/** + * @file crypter.c + * + * @brief Generic constructor for crypter_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "crypter.h" + +#include +#include + + +ENUM_BEGIN(encryption_algorithm_names, ENCR_UNDEFINED, ENCR_UNDEFINED, + "UNDEFINED"); +ENUM_NEXT(encryption_algorithm_names, ENCR_DES_IV64, ENCR_DES_IV32, ENCR_UNDEFINED, + "DES_IV64", + "DES", + "3DES", + "RC5", + "IDEA", + "CAST", + "BLOWFISH", + "3IDEA", + "DES_IV32"); +ENUM_NEXT(encryption_algorithm_names, ENCR_NULL, ENCR_AES_CTR, ENCR_DES_IV32, + "NULL", + "AES_CBC", + "AES_CTR"); +ENUM_END(encryption_algorithm_names, ENCR_AES_CTR); + +/* + * Described in header. + */ +crypter_t *crypter_create(encryption_algorithm_t encryption_algorithm, size_t key_size) +{ + switch (encryption_algorithm) + { + case ENCR_AES_CBC: + { + return (crypter_t*)aes_cbc_crypter_create(key_size); + } + case ENCR_DES: + case ENCR_3DES: + { + return (crypter_t*)des_crypter_create(encryption_algorithm); + } + default: + return NULL; + } +} diff --git a/src/libstrongswan/crypto/crypters/crypter.h b/src/libstrongswan/crypto/crypters/crypter.h new file mode 100644 index 000000000..46d94ce93 --- /dev/null +++ b/src/libstrongswan/crypto/crypters/crypter.h @@ -0,0 +1,155 @@ +/** + * @file crypter.h + * + * @brief Interface crypter_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef CRYPTER_H_ +#define CRYPTER_H_ + +typedef enum encryption_algorithm_t encryption_algorithm_t; +typedef struct crypter_t crypter_t; + +#include + +/** + * @brief Encryption algorithm, as in IKEv2 RFC 3.3.2. + * + * Currently only the following algorithms are implemented: + * - ENCR_AES_CBC + * - ENCR_DES + * - ENCR_3DES + * + * @ingroup crypters + */ +enum encryption_algorithm_t { + ENCR_UNDEFINED = 1024, + ENCR_DES_IV64 = 1, + /** Implemented in class des_crypter_t */ + ENCR_DES = 2, + /** Implemented in class des_crypter_t */ + ENCR_3DES = 3, + ENCR_RC5 = 4, + ENCR_IDEA = 5, + ENCR_CAST = 6, + ENCR_BLOWFISH = 7, + ENCR_3IDEA = 8, + ENCR_DES_IV32 = 9, + ENCR_NULL = 11, + /** Implemented in class aes_cbc_crypter_t */ + ENCR_AES_CBC = 12, + ENCR_AES_CTR = 13 +}; + +/** + * enum name for encryption_algorithm_t. + */ +extern enum_name_t *encryption_algorithm_names; + +/** + * @brief Generic interface for symmetric encryption algorithms. + * + * @b Constructors: + * - crypter_create() + * + * @ingroup crypters + */ +struct crypter_t { + + /** + * @brief Encrypt a chunk of data and allocate space for the encrypted value. + * + * @param this calling object + * @param data data to encrypt + * @param iv initializing vector + * @param[out] encrypted pointer where the encrypted bytes will be written + * @return + * - SUCCESS + * - INVALID_ARG if data size not a multiple of block size + */ + status_t (*encrypt) (crypter_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted); + + /** + * @brief Decrypt a chunk of data and allocate space for the decrypted value. + * + * @param this calling object + * @param data data to decrypt + * @param iv initializing vector + * @param[out] encrypted pointer where the decrypted bytes will be written + * @return + * - SUCCESS + * - INVALID_ARG if data size not a multiple of block size + */ + status_t (*decrypt) (crypter_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted); + + /** + * @brief Get the block size of this crypter_t object. + * + * @param this calling object + * @return block size in bytes + */ + size_t (*get_block_size) (crypter_t *this); + + /** + * @brief Get the key size of this crypter_t object. + * + * @param this calling object + * @return key size in bytes + */ + size_t (*get_key_size) (crypter_t *this); + + /** + * @brief Set the key for this crypter_t object. + * + * @param this calling object + * @param key key to set + * @return + * - SUCCESS + * - INVALID_ARG if key length invalid + */ + status_t (*set_key) (crypter_t *this, chunk_t key); + + /** + * @brief Destroys a crypter_t object. + * + * @param this calling object + */ + void (*destroy) (crypter_t *this); +}; + +/** + * @brief Generic constructor for crypter_t objects. + * + * Currently only the following algorithms are implemented: + * - ENCR_AES_CBC + * - ENCR_DES + * - ENCR_3DES + * + * The key_size is ignored for algorithms with fixed key size. + * + * @param encryption_algorithm Algorithm to use for crypter + * @param key_size size of the key in bytes + * @return + * - crypter_t object + * - NULL if encryption algorithm/key_size is not supported + */ +crypter_t *crypter_create(encryption_algorithm_t encryption_algorithm, size_t key_size); + +#endif /*CRYPTER_H_*/ diff --git a/src/libstrongswan/crypto/crypters/des_crypter.c b/src/libstrongswan/crypto/crypters/des_crypter.c new file mode 100644 index 000000000..dc5a8ff55 --- /dev/null +++ b/src/libstrongswan/crypto/crypters/des_crypter.c @@ -0,0 +1,1535 @@ +/** + * @file des_crypter.c + * + * @brief Implementation of des_crypter_t + * + */ + +/* Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * Derived from Plutos DES library by Eric Young. + * + * Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "des_crypter.h" + +typedef u_char des_cblock[8]; + +typedef struct des_ks_struct { + des_cblock _; +} des_key_schedule[16]; + + +typedef struct private_des_crypter_t private_des_crypter_t; + +/** + * Private data for des_crypter_t + */ +struct private_des_crypter_t { + + /** + * Public part of this class. + */ + des_crypter_t public; + + /** + * Key size, depends on algoritm... + */ + size_t key_size; + + union { + /** key schedule for single des */ + des_key_schedule ks; + /** key schedule for 3des */ + des_key_schedule ks3[3]; + }; +}; + + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +#define DES_LONG u_int32_t + +#if defined(WIN32) || defined(WIN16) +#ifndef MSDOS +#define MSDOS +#endif +#endif + +#ifndef DES_DEFAULT_OPTIONS +/* the following is tweaked from a config script, that is why it is a + * protected undef/define */ +#ifndef DES_PTR +#define DES_PTR +#endif + +/* This helps C compiler generate the correct code for multiple functional + * units. It reduces register dependancies at the expense of 2 more + * registers */ +#ifndef DES_RISC1 +#define DES_RISC1 +#endif + +#ifndef DES_RISC2 +#undef DES_RISC2 +#endif + +#if defined(DES_RISC1) && defined(DES_RISC2) +YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!! +#endif + +/* Unroll the inner loop, this sometimes helps, sometimes hinders. + * Very mucy CPU dependant */ +#ifndef DES_UNROLL +#define DES_UNROLL +#endif + +/* These default values were supplied by + * Peter Gutman + * They are only used if nothing else has been defined */ +#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL) +/* Special defines which change the way the code is built depending on the + CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find + even newer MIPS CPU's, but at the moment one size fits all for + optimization options. Older Sparc's work better with only UNROLL, but + there's no way to tell at compile time what it is you're running on */ + +#if defined( sun ) /* Newer Sparc's */ +#define DES_PTR +#define DES_RISC1 +#define DES_UNROLL +#elif defined( __ultrix ) /* Older MIPS */ +#define DES_PTR +#define DES_RISC2 +#define DES_UNROLL +#elif defined( __osf1__ ) /* Alpha */ +#define DES_PTR +#define DES_RISC2 +#elif defined ( _AIX ) /* RS6000 */ + /* Unknown */ +#elif defined( __hpux ) /* HP-PA */ + /* Unknown */ +#elif defined( __aux ) /* 68K */ + /* Unknown */ +#elif defined( __dgux ) /* 88K (but P6 in latest boxes) */ +#define DES_UNROLL +#elif defined( __sgi ) /* Newer MIPS */ +#define DES_PTR +#define DES_RISC2 +#define DES_UNROLL +#elif defined( i386 ) /* x86 boxes, should be gcc */ +#define DES_PTR +#define DES_RISC1 +#define DES_UNROLL +#endif /* Systems-specific speed defines */ +#endif + +#endif /* DES_DEFAULT_OPTIONS */ + +#ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */ +#include +#include +#include +#include +#ifndef RAND +#define RAND +#endif +#undef NOPROTO +#endif + +#if defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS) +#ifndef __KERNEL__ +#include +#else +#include +#endif +#endif + +#ifndef RAND +#define RAND +#endif + +#ifdef linux +#undef RAND +#endif + +#ifdef MSDOS +#define getpid() 2 +#define RAND +#undef NOPROTO +#endif + +#if defined(NOCONST) +#define const +#endif + +#ifdef __STDC__ +#undef NOPROTO +#endif + +#ifdef RAND +#define srandom(s) srand(s) +#define random rand +#endif + +#define ITERATIONS 16 +#define HALF_ITERATIONS 8 + +/* used in des_read and des_write */ +#define MAXWRITE (1024*16) +#define BSIZE (MAXWRITE+4) + +#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ + l|=((DES_LONG)(*((c)++)))<< 8L, \ + l|=((DES_LONG)(*((c)++)))<<16L, \ + l|=((DES_LONG)(*((c)++)))<<24L) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ + case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ + case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ + case 5: l2|=((DES_LONG)(*(--(c)))); \ + case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ + case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ + case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ + case 1: l1|=((DES_LONG)(*(--(c)))); \ +} \ +} + +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +/* replacements for htonl and ntohl since I have no idea what to do + * when faced with machines with 8 byte longs. */ +#define HDRSIZE 4 + +#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \ + l|=((DES_LONG)(*((c)++)))<<16L, \ + l|=((DES_LONG)(*((c)++)))<< 8L, \ + l|=((DES_LONG)(*((c)++)))) + +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ +} \ +} + +#if defined(WIN32) +#define ROTATE(a,n) (_lrotr(a,n)) +#else +#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) +#endif + +/* Don't worry about the LOAD_DATA() stuff, that is used by + * fcrypt() to add it's little bit to the front */ + +#ifdef DES_FCRYPT + +#define LOAD_DATA_tmp(R,S,u,t,E0,E1) \ +{ DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); } + +#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ + t=R^(R>>16L); \ + u=t&E0; t&=E1; \ + tmp=(u<<16); u^=R^s[S ]; u^=tmp; \ + tmp=(t<<16); t^=R^s[S+1]; t^=tmp +#else +#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g) +#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \ + u=R^s[S ]; \ + t=R^s[S+1] +#endif + +/* The changes to this macro may help or hinder, depending on the + * compiler and the achitecture. gcc2 always seems to do well :-). + * Inspired by Dana How + * DO NOT use the alternative version on machines with 8 byte longs. + * It does not seem to work on the Alpha, even when DES_LONG is 4 + * bytes, probably an issue of accessing non-word aligned objects :-( */ +#ifdef DES_PTR + +/* It recently occured to me that 0^0^0^0^0^0^0 == 0, so there + * is no reason to not xor all the sub items together. This potentially + * saves a register since things can be xored directly into L */ + +#if defined(DES_RISC1) || defined(DES_RISC2) +#ifdef DES_RISC1 +#define D_ENCRYPT(LL,R,S) { \ + unsigned int u1,u2,u3; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u2=(int)u>>8L; \ + u1=(int)u&0xfc; \ + u2&=0xfc; \ + t=ROTATE(t,4); \ + u>>=16L; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \ + u3=(int)(u>>8L); \ + u1=(int)u&0xfc; \ + u3&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+u3); \ + u2=(int)t>>8L; \ + u1=(int)t&0xfc; \ + u2&=0xfc; \ + t>>=16L; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \ + u3=(int)t>>8L; \ + u1=(int)t&0xfc; \ + u3&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+u3); } +#endif +#ifdef DES_RISC2 +#define D_ENCRYPT(LL,R,S) { \ + unsigned int u1,u2,s1,s2; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u2=(int)u>>8L; \ + u1=(int)u&0xfc; \ + u2&=0xfc; \ + t=ROTATE(t,4); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \ + s1=(int)(u>>16L); \ + s2=(int)(u>>24L); \ + s1&=0xfc; \ + s2&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+s1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+s2); \ + u2=(int)t>>8L; \ + u1=(int)t&0xfc; \ + u2&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \ + s1=(int)(t>>16L); \ + s2=(int)(t>>24L); \ + s1&=0xfc; \ + s2&=0xfc; \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+s1); \ + LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+s2); } +#endif +#else +#define D_ENCRYPT(LL,R,S) { \ + LOAD_DATA_tmp(R,S,u,t,E0,E1); \ + t=ROTATE(t,4); \ + LL^= \ + *(DES_LONG *)((unsigned char *)des_SP +((u )&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x200+((u>> 8L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x400+((u>>16L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x600+((u>>24L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x100+((t )&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x300+((t>> 8L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x500+((t>>16L)&0xfc))^ \ + *(DES_LONG *)((unsigned char *)des_SP+0x700+((t>>24L)&0xfc)); } +#endif + +#else /* original version */ + +#if defined(DES_RISC1) || defined(DES_RISC2) +#ifdef DES_RISC1 +#define D_ENCRYPT(LL,R,S) {\ + unsigned int u1,u2,u3; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u>>=2L; \ + t=ROTATE(t,6); \ + u2=(int)u>>8L; \ + u1=(int)u&0x3f; \ + u2&=0x3f; \ + u>>=16L; \ + LL^=des_SPtrans[0][u1]; \ + LL^=des_SPtrans[2][u2]; \ + u3=(int)u>>8L; \ + u1=(int)u&0x3f; \ + u3&=0x3f; \ + LL^=des_SPtrans[4][u1]; \ + LL^=des_SPtrans[6][u3]; \ + u2=(int)t>>8L; \ + u1=(int)t&0x3f; \ + u2&=0x3f; \ + t>>=16L; \ + LL^=des_SPtrans[1][u1]; \ + LL^=des_SPtrans[3][u2]; \ + u3=(int)t>>8L; \ + u1=(int)t&0x3f; \ + u3&=0x3f; \ + LL^=des_SPtrans[5][u1]; \ + LL^=des_SPtrans[7][u3]; } +#endif +#ifdef DES_RISC2 +#define D_ENCRYPT(LL,R,S) {\ + unsigned int u1,u2,s1,s2; \ + LOAD_DATA(R,S,u,t,E0,E1,u1); \ + u>>=2L; \ + t=ROTATE(t,6); \ + u2=(int)u>>8L; \ + u1=(int)u&0x3f; \ + u2&=0x3f; \ + LL^=des_SPtrans[0][u1]; \ + LL^=des_SPtrans[2][u2]; \ + s1=(int)u>>16L; \ + s2=(int)u>>24L; \ + s1&=0x3f; \ + s2&=0x3f; \ + LL^=des_SPtrans[4][s1]; \ + LL^=des_SPtrans[6][s2]; \ + u2=(int)t>>8L; \ + u1=(int)t&0x3f; \ + u2&=0x3f; \ + LL^=des_SPtrans[1][u1]; \ + LL^=des_SPtrans[3][u2]; \ + s1=(int)t>>16; \ + s2=(int)t>>24L; \ + s1&=0x3f; \ + s2&=0x3f; \ + LL^=des_SPtrans[5][s1]; \ + LL^=des_SPtrans[7][s2]; } +#endif + +#else + +#define D_ENCRYPT(LL,R,S) {\ + LOAD_DATA_tmp(R,S,u,t,E0,E1); \ + t=ROTATE(t,4); \ + LL^=\ + des_SPtrans[0][(u>> 2L)&0x3f]^ \ + des_SPtrans[2][(u>>10L)&0x3f]^ \ + des_SPtrans[4][(u>>18L)&0x3f]^ \ + des_SPtrans[6][(u>>26L)&0x3f]^ \ + des_SPtrans[1][(t>> 2L)&0x3f]^ \ + des_SPtrans[3][(t>>10L)&0x3f]^ \ + des_SPtrans[5][(t>>18L)&0x3f]^ \ + des_SPtrans[7][(t>>26L)&0x3f]; } +#endif +#endif + + /* IP and FP + * The problem is more of a geometric problem that random bit fiddling. + 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 + 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 + 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 + 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 + + 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 + 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 + 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 + 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 + + The output has been subject to swaps of the form + 0 1 -> 3 1 but the odd and even bits have been put into + 2 3 2 0 + different words. The main trick is to remember that + t=((l>>size)^r)&(mask); + r^=t; + l^=(t<>(n))^(b))&(m)),\ + (b)^=(t),\ + (a)^=((t)<<(n))) + +#define IP(l,r) \ +{ \ + register DES_LONG tt; \ + PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ + PERM_OP(l,r,tt,16,0x0000ffffL); \ + PERM_OP(r,l,tt, 2,0x33333333L); \ + PERM_OP(l,r,tt, 8,0x00ff00ffL); \ + PERM_OP(r,l,tt, 1,0x55555555L); \ +} + +#define FP(l,r) \ +{ \ + register DES_LONG tt; \ + PERM_OP(l,r,tt, 1,0x55555555L); \ + PERM_OP(r,l,tt, 8,0x00ff00ffL); \ + PERM_OP(l,r,tt, 2,0x33333333L); \ + PERM_OP(r,l,tt,16,0x0000ffffL); \ + PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ +} + +#ifndef NOPROTO +void fcrypt_body(DES_LONG *out,des_key_schedule ks, + DES_LONG Eswap0, DES_LONG Eswap1); +#else +void fcrypt_body(); +#endif + +static const DES_LONG des_skb[8][64]={ + { /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ + 0x00000000L,0x00000010L,0x20000000L,0x20000010L, + 0x00010000L,0x00010010L,0x20010000L,0x20010010L, + 0x00000800L,0x00000810L,0x20000800L,0x20000810L, + 0x00010800L,0x00010810L,0x20010800L,0x20010810L, + 0x00000020L,0x00000030L,0x20000020L,0x20000030L, + 0x00010020L,0x00010030L,0x20010020L,0x20010030L, + 0x00000820L,0x00000830L,0x20000820L,0x20000830L, + 0x00010820L,0x00010830L,0x20010820L,0x20010830L, + 0x00080000L,0x00080010L,0x20080000L,0x20080010L, + 0x00090000L,0x00090010L,0x20090000L,0x20090010L, + 0x00080800L,0x00080810L,0x20080800L,0x20080810L, + 0x00090800L,0x00090810L,0x20090800L,0x20090810L, + 0x00080020L,0x00080030L,0x20080020L,0x20080030L, + 0x00090020L,0x00090030L,0x20090020L,0x20090030L, + 0x00080820L,0x00080830L,0x20080820L,0x20080830L, + 0x00090820L,0x00090830L,0x20090820L,0x20090830L, + }, + { /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ + 0x00000000L,0x02000000L,0x00002000L,0x02002000L, + 0x00200000L,0x02200000L,0x00202000L,0x02202000L, + 0x00000004L,0x02000004L,0x00002004L,0x02002004L, + 0x00200004L,0x02200004L,0x00202004L,0x02202004L, + 0x00000400L,0x02000400L,0x00002400L,0x02002400L, + 0x00200400L,0x02200400L,0x00202400L,0x02202400L, + 0x00000404L,0x02000404L,0x00002404L,0x02002404L, + 0x00200404L,0x02200404L,0x00202404L,0x02202404L, + 0x10000000L,0x12000000L,0x10002000L,0x12002000L, + 0x10200000L,0x12200000L,0x10202000L,0x12202000L, + 0x10000004L,0x12000004L,0x10002004L,0x12002004L, + 0x10200004L,0x12200004L,0x10202004L,0x12202004L, + 0x10000400L,0x12000400L,0x10002400L,0x12002400L, + 0x10200400L,0x12200400L,0x10202400L,0x12202400L, + 0x10000404L,0x12000404L,0x10002404L,0x12002404L, + 0x10200404L,0x12200404L,0x10202404L,0x12202404L, + }, + { /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ + 0x00000000L,0x00000001L,0x00040000L,0x00040001L, + 0x01000000L,0x01000001L,0x01040000L,0x01040001L, + 0x00000002L,0x00000003L,0x00040002L,0x00040003L, + 0x01000002L,0x01000003L,0x01040002L,0x01040003L, + 0x00000200L,0x00000201L,0x00040200L,0x00040201L, + 0x01000200L,0x01000201L,0x01040200L,0x01040201L, + 0x00000202L,0x00000203L,0x00040202L,0x00040203L, + 0x01000202L,0x01000203L,0x01040202L,0x01040203L, + 0x08000000L,0x08000001L,0x08040000L,0x08040001L, + 0x09000000L,0x09000001L,0x09040000L,0x09040001L, + 0x08000002L,0x08000003L,0x08040002L,0x08040003L, + 0x09000002L,0x09000003L,0x09040002L,0x09040003L, + 0x08000200L,0x08000201L,0x08040200L,0x08040201L, + 0x09000200L,0x09000201L,0x09040200L,0x09040201L, + 0x08000202L,0x08000203L,0x08040202L,0x08040203L, + 0x09000202L,0x09000203L,0x09040202L,0x09040203L, + }, + { /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ + 0x00000000L,0x00100000L,0x00000100L,0x00100100L, + 0x00000008L,0x00100008L,0x00000108L,0x00100108L, + 0x00001000L,0x00101000L,0x00001100L,0x00101100L, + 0x00001008L,0x00101008L,0x00001108L,0x00101108L, + 0x04000000L,0x04100000L,0x04000100L,0x04100100L, + 0x04000008L,0x04100008L,0x04000108L,0x04100108L, + 0x04001000L,0x04101000L,0x04001100L,0x04101100L, + 0x04001008L,0x04101008L,0x04001108L,0x04101108L, + 0x00020000L,0x00120000L,0x00020100L,0x00120100L, + 0x00020008L,0x00120008L,0x00020108L,0x00120108L, + 0x00021000L,0x00121000L,0x00021100L,0x00121100L, + 0x00021008L,0x00121008L,0x00021108L,0x00121108L, + 0x04020000L,0x04120000L,0x04020100L,0x04120100L, + 0x04020008L,0x04120008L,0x04020108L,0x04120108L, + 0x04021000L,0x04121000L,0x04021100L,0x04121100L, + 0x04021008L,0x04121008L,0x04021108L,0x04121108L, + }, + { /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ + 0x00000000L,0x10000000L,0x00010000L,0x10010000L, + 0x00000004L,0x10000004L,0x00010004L,0x10010004L, + 0x20000000L,0x30000000L,0x20010000L,0x30010000L, + 0x20000004L,0x30000004L,0x20010004L,0x30010004L, + 0x00100000L,0x10100000L,0x00110000L,0x10110000L, + 0x00100004L,0x10100004L,0x00110004L,0x10110004L, + 0x20100000L,0x30100000L,0x20110000L,0x30110000L, + 0x20100004L,0x30100004L,0x20110004L,0x30110004L, + 0x00001000L,0x10001000L,0x00011000L,0x10011000L, + 0x00001004L,0x10001004L,0x00011004L,0x10011004L, + 0x20001000L,0x30001000L,0x20011000L,0x30011000L, + 0x20001004L,0x30001004L,0x20011004L,0x30011004L, + 0x00101000L,0x10101000L,0x00111000L,0x10111000L, + 0x00101004L,0x10101004L,0x00111004L,0x10111004L, + 0x20101000L,0x30101000L,0x20111000L,0x30111000L, + 0x20101004L,0x30101004L,0x20111004L,0x30111004L, + }, + { /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ + 0x00000000L,0x08000000L,0x00000008L,0x08000008L, + 0x00000400L,0x08000400L,0x00000408L,0x08000408L, + 0x00020000L,0x08020000L,0x00020008L,0x08020008L, + 0x00020400L,0x08020400L,0x00020408L,0x08020408L, + 0x00000001L,0x08000001L,0x00000009L,0x08000009L, + 0x00000401L,0x08000401L,0x00000409L,0x08000409L, + 0x00020001L,0x08020001L,0x00020009L,0x08020009L, + 0x00020401L,0x08020401L,0x00020409L,0x08020409L, + 0x02000000L,0x0A000000L,0x02000008L,0x0A000008L, + 0x02000400L,0x0A000400L,0x02000408L,0x0A000408L, + 0x02020000L,0x0A020000L,0x02020008L,0x0A020008L, + 0x02020400L,0x0A020400L,0x02020408L,0x0A020408L, + 0x02000001L,0x0A000001L,0x02000009L,0x0A000009L, + 0x02000401L,0x0A000401L,0x02000409L,0x0A000409L, + 0x02020001L,0x0A020001L,0x02020009L,0x0A020009L, + 0x02020401L,0x0A020401L,0x02020409L,0x0A020409L, + }, + { /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ + 0x00000000L,0x00000100L,0x00080000L,0x00080100L, + 0x01000000L,0x01000100L,0x01080000L,0x01080100L, + 0x00000010L,0x00000110L,0x00080010L,0x00080110L, + 0x01000010L,0x01000110L,0x01080010L,0x01080110L, + 0x00200000L,0x00200100L,0x00280000L,0x00280100L, + 0x01200000L,0x01200100L,0x01280000L,0x01280100L, + 0x00200010L,0x00200110L,0x00280010L,0x00280110L, + 0x01200010L,0x01200110L,0x01280010L,0x01280110L, + 0x00000200L,0x00000300L,0x00080200L,0x00080300L, + 0x01000200L,0x01000300L,0x01080200L,0x01080300L, + 0x00000210L,0x00000310L,0x00080210L,0x00080310L, + 0x01000210L,0x01000310L,0x01080210L,0x01080310L, + 0x00200200L,0x00200300L,0x00280200L,0x00280300L, + 0x01200200L,0x01200300L,0x01280200L,0x01280300L, + 0x00200210L,0x00200310L,0x00280210L,0x00280310L, + 0x01200210L,0x01200310L,0x01280210L,0x01280310L, + }, + { /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ + 0x00000000L,0x04000000L,0x00040000L,0x04040000L, + 0x00000002L,0x04000002L,0x00040002L,0x04040002L, + 0x00002000L,0x04002000L,0x00042000L,0x04042000L, + 0x00002002L,0x04002002L,0x00042002L,0x04042002L, + 0x00000020L,0x04000020L,0x00040020L,0x04040020L, + 0x00000022L,0x04000022L,0x00040022L,0x04040022L, + 0x00002020L,0x04002020L,0x00042020L,0x04042020L, + 0x00002022L,0x04002022L,0x00042022L,0x04042022L, + 0x00000800L,0x04000800L,0x00040800L,0x04040800L, + 0x00000802L,0x04000802L,0x00040802L,0x04040802L, + 0x00002800L,0x04002800L,0x00042800L,0x04042800L, + 0x00002802L,0x04002802L,0x00042802L,0x04042802L, + 0x00000820L,0x04000820L,0x00040820L,0x04040820L, + 0x00000822L,0x04000822L,0x00040822L,0x04040822L, + 0x00002820L,0x04002820L,0x00042820L,0x04042820L, + 0x00002822L,0x04002822L,0x00042822L,0x04042822L, + } +}; + +const DES_LONG des_SPtrans[8][64]={ + { + /* nibble 0 */ + 0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L, + 0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L, + 0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L, + 0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L, + 0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L, + 0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L, + 0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L, + 0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L, + 0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L, + 0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L, + 0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L, + 0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L, + 0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L, + 0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L, + 0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L, + 0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L, + }, + { /* nibble 1 */ + 0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L, + 0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L, + 0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L, + 0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L, + 0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L, + 0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L, + 0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L, + 0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L, + 0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L, + 0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L, + 0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L, + 0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L, + 0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L, + 0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L, + 0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L, + 0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L, + }, + { /* nibble 2 */ + 0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L, + 0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L, + 0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L, + 0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L, + 0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L, + 0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L, + 0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L, + 0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L, + 0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L, + 0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L, + 0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L, + 0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L, + 0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L, + 0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L, + 0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L, + 0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L, + }, + { /* nibble 3 */ + 0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L, + 0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L, + 0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L, + 0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L, + 0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L, + 0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L, + 0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L, + 0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L, + 0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L, + 0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L, + 0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L, + 0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L, + 0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L, + 0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L, + 0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L, + 0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L, + }, + { /* nibble 4 */ + 0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L, + 0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L, + 0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L, + 0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L, + 0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L, + 0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L, + 0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L, + 0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L, + 0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L, + 0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L, + 0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L, + 0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L, + 0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L, + 0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L, + 0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L, + 0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L, + }, + { /* nibble 5 */ + 0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L, + 0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L, + 0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L, + 0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L, + 0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L, + 0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L, + 0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L, + 0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L, + 0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L, + 0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L, + 0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L, + 0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L, + 0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L, + 0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L, + 0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L, + 0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L, + }, + { /* nibble 6 */ + 0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L, + 0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L, + 0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L, + 0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L, + 0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L, + 0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L, + 0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L, + 0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L, + 0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L, + 0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L, + 0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L, + 0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L, + 0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L, + 0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L, + 0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L, + 0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L, + }, + { /* nibble 7 */ + 0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L, + 0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L, + 0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L, + 0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L, + 0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L, + 0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L, + 0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L, + 0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L, + 0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L, + 0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L, + 0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L, + 0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L, + 0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L, + 0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L, + 0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L, + 0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L, + } +}; + +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ + (a)=(a)^(t)^(t>>(16-(n)))) + +static const unsigned char odd_parity[256]={ + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 +}; + +/** + * Create key schedule for a single DES 64Bit key + */ +static int des_set_key(des_cblock *key, des_key_schedule *schedule) +{ + static int shifts2[16] = {0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; + register DES_LONG c,d,t,s,t2; + register unsigned char *in; + register DES_LONG *k; + register int i; + + for (i = 0; i < sizeof(des_cblock); i++) + { + (*key)[i] = odd_parity[(*key)[i]]; + } + + k=(DES_LONG *)schedule; + in=(unsigned char *)key; + + c2l(in,c); + c2l(in,d); + + /* do PC1 in 60 simple operations */ +/* PERM_OP(d,c,t,4,0x0f0f0f0fL); + HPERM_OP(c,t,-2, 0xcccc0000L); + HPERM_OP(c,t,-1, 0xaaaa0000L); + HPERM_OP(c,t, 8, 0x00ff0000L); + HPERM_OP(c,t,-1, 0xaaaa0000L); + HPERM_OP(d,t,-8, 0xff000000L); + HPERM_OP(d,t, 8, 0x00ff0000L); + HPERM_OP(d,t, 2, 0x33330000L); + d=((d&0x00aa00aaL)<<7L)|((d&0x55005500L)>>7L)|(d&0xaa55aa55L); + d=(d>>8)|((c&0xf0000000L)>>4); + c&=0x0fffffffL; */ + + /* I now do it in 47 simple operations :-) + * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) + * for the inspiration. :-) */ + PERM_OP (d,c,t,4,0x0f0f0f0fL); + HPERM_OP(c,t,-2,0xcccc0000L); + HPERM_OP(d,t,-2,0xcccc0000L); + PERM_OP (d,c,t,1,0x55555555L); + PERM_OP (c,d,t,8,0x00ff00ffL); + PERM_OP (d,c,t,1,0x55555555L); + d= (((d&0x000000ffL)<<16L)| (d&0x0000ff00L) | + ((d&0x00ff0000L)>>16L)|((c&0xf0000000L)>>4L)); + c&=0x0fffffffL; + + for (i=0; i>2L)|(c<<26L)); d=((d>>2L)|(d<<26L)); } + else + { c=((c>>1L)|(c<<27L)); d=((d>>1L)|(d<<27L)); } + c&=0x0fffffffL; + d&=0x0fffffffL; + /* could be a few less shifts but I am to lazy at this + * point in time to investigate */ + s= des_skb[0][ (c )&0x3f ]| + des_skb[1][((c>> 6)&0x03)|((c>> 7L)&0x3c)]| + des_skb[2][((c>>13)&0x0f)|((c>>14L)&0x30)]| + des_skb[3][((c>>20)&0x01)|((c>>21L)&0x06) | + ((c>>22L)&0x38)]; + t= des_skb[4][ (d )&0x3f ]| + des_skb[5][((d>> 7L)&0x03)|((d>> 8L)&0x3c)]| + des_skb[6][ (d>>15L)&0x3f ]| + des_skb[7][((d>>21L)&0x0f)|((d>>22L)&0x30)]; + + /* table contained 0213 4657 */ + t2=((t<<16L)|(s&0x0000ffffL))&0xffffffffL; + *(k++)=ROTATE(t2,30)&0xffffffffL; + + t2=((s>>16L)|(t&0xffff0000L)); + *(k++)=ROTATE(t2,26)&0xffffffffL; + } + return(0); +} + + +static void des_encrypt(DES_LONG *data, des_key_schedule ks, int enc) +{ + register DES_LONG l,r,t,u; +#ifdef DES_PTR + register unsigned char *des_SP=(unsigned char *)des_SPtrans; +#endif +#ifndef DES_UNROLL + register int i; +#endif + register DES_LONG *s; + + r=data[0]; + l=data[1]; + + IP(r,l); + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. */ + /* clear the top bits on machines with 8byte longs */ + /* shift left by 2 */ + r=ROTATE(r,29)&0xffffffffL; + l=ROTATE(l,29)&0xffffffffL; + + s=(DES_LONG *)ks; + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (enc) + { +#ifdef DES_UNROLL + D_ENCRYPT(l,r, 0); /* 1 */ + D_ENCRYPT(r,l, 2); /* 2 */ + D_ENCRYPT(l,r, 4); /* 3 */ + D_ENCRYPT(r,l, 6); /* 4 */ + D_ENCRYPT(l,r, 8); /* 5 */ + D_ENCRYPT(r,l,10); /* 6 */ + D_ENCRYPT(l,r,12); /* 7 */ + D_ENCRYPT(r,l,14); /* 8 */ + D_ENCRYPT(l,r,16); /* 9 */ + D_ENCRYPT(r,l,18); /* 10 */ + D_ENCRYPT(l,r,20); /* 11 */ + D_ENCRYPT(r,l,22); /* 12 */ + D_ENCRYPT(l,r,24); /* 13 */ + D_ENCRYPT(r,l,26); /* 14 */ + D_ENCRYPT(l,r,28); /* 15 */ + D_ENCRYPT(r,l,30); /* 16 */ +#else + for (i=0; i<32; i+=8) +{ + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + D_ENCRYPT(l,r,i+4); /* 3 */ + D_ENCRYPT(r,l,i+6); /* 4 */ +} +#endif + } + else +{ +#ifdef DES_UNROLL + D_ENCRYPT(l,r,30); /* 16 */ + D_ENCRYPT(r,l,28); /* 15 */ + D_ENCRYPT(l,r,26); /* 14 */ + D_ENCRYPT(r,l,24); /* 13 */ + D_ENCRYPT(l,r,22); /* 12 */ + D_ENCRYPT(r,l,20); /* 11 */ + D_ENCRYPT(l,r,18); /* 10 */ + D_ENCRYPT(r,l,16); /* 9 */ + D_ENCRYPT(l,r,14); /* 8 */ + D_ENCRYPT(r,l,12); /* 7 */ + D_ENCRYPT(l,r,10); /* 6 */ + D_ENCRYPT(r,l, 8); /* 5 */ + D_ENCRYPT(l,r, 6); /* 4 */ + D_ENCRYPT(r,l, 4); /* 3 */ + D_ENCRYPT(l,r, 2); /* 2 */ + D_ENCRYPT(r,l, 0); /* 1 */ +#else + for (i=30; i>0; i-=8) +{ + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + D_ENCRYPT(l,r,i-4); /* 14 */ + D_ENCRYPT(r,l,i-6); /* 13 */ +} +#endif +} + + /* rotate and clear the top bits on machines with 8byte longs */ + l=ROTATE(l,3)&0xffffffffL; + r=ROTATE(r,3)&0xffffffffL; + + FP(r,l); + data[0]=l; + data[1]=r; + l=r=t=u=0; +} + +/** + * DES CBC encrypt decrypt routine + */ +static void des_cbc_encrypt(des_cblock *input, des_cblock *output, long length, + des_key_schedule schedule, des_cblock *ivec, int enc) +{ + register DES_LONG tin0,tin1; + register DES_LONG tout0,tout1,xor0,xor1; + register unsigned char *in,*out; + register long l=length; + DES_LONG tin[2]; + unsigned char *iv; + + in=(unsigned char *)input; + out=(unsigned char *)output; + iv=(unsigned char *)ivec; + + if (enc) + { + c2l(iv,tout0); + c2l(iv,tout1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + tin0^=tout0; tin[0]=tin0; + tin1^=tout1; tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); + tout0=tin[0]; l2c(tout0,out); + tout1=tin[1]; l2c(tout1,out); + } + if (l != -8) + { + c2ln(in,tin0,tin1,l+8); + tin0^=tout0; tin[0]=tin0; + tin1^=tout1; tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT); + tout0=tin[0]; l2c(tout0,out); + tout1=tin[1]; l2c(tout1,out); + } + } + else + { + c2l(iv,xor0); + c2l(iv,xor1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); tin[0]=tin0; + c2l(in,tin1); tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2c(tout0,out); + l2c(tout1,out); + xor0=tin0; + xor1=tin1; + } + if (l != -8) + { + c2l(in,tin0); tin[0]=tin0; + c2l(in,tin1); tin[1]=tin1; + des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2cn(tout0,tout1,out,l+8); + /* xor0=tin0; + xor1=tin1; */ + } + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; +} + +static void des_encrypt2(DES_LONG *data, des_key_schedule ks, int enc) +{ + register DES_LONG l,r,t,u; +#ifdef DES_PTR + register unsigned char *des_SP=(unsigned char *)des_SPtrans; +#endif +#ifndef DES_UNROLL + register int i; +#endif + register DES_LONG *s; + + r=data[0]; + l=data[1]; + + /* Things have been modified so that the initial rotate is + * done outside the loop. This required the + * des_SPtrans values in sp.h to be rotated 1 bit to the right. + * One perl script later and things have a 5% speed up on a sparc2. + * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> + * for pointing this out. + * clear the top bits on machines with 8byte longs */ + r=ROTATE(r,29)&0xffffffffL; + l=ROTATE(l,29)&0xffffffffL; + + s=(DES_LONG *)ks; + /* I don't know if it is worth the effort of loop unrolling the + * inner loop */ + if (enc) + { +#ifdef DES_UNROLL + D_ENCRYPT(l,r, 0); /* 1 */ + D_ENCRYPT(r,l, 2); /* 2 */ + D_ENCRYPT(l,r, 4); /* 3 */ + D_ENCRYPT(r,l, 6); /* 4 */ + D_ENCRYPT(l,r, 8); /* 5 */ + D_ENCRYPT(r,l,10); /* 6 */ + D_ENCRYPT(l,r,12); /* 7 */ + D_ENCRYPT(r,l,14); /* 8 */ + D_ENCRYPT(l,r,16); /* 9 */ + D_ENCRYPT(r,l,18); /* 10 */ + D_ENCRYPT(l,r,20); /* 11 */ + D_ENCRYPT(r,l,22); /* 12 */ + D_ENCRYPT(l,r,24); /* 13 */ + D_ENCRYPT(r,l,26); /* 14 */ + D_ENCRYPT(l,r,28); /* 15 */ + D_ENCRYPT(r,l,30); /* 16 */ +#else + for (i=0; i<32; i+=8) +{ + D_ENCRYPT(l,r,i+0); /* 1 */ + D_ENCRYPT(r,l,i+2); /* 2 */ + D_ENCRYPT(l,r,i+4); /* 3 */ + D_ENCRYPT(r,l,i+6); /* 4 */ +} +#endif + } + else +{ +#ifdef DES_UNROLL + D_ENCRYPT(l,r,30); /* 16 */ + D_ENCRYPT(r,l,28); /* 15 */ + D_ENCRYPT(l,r,26); /* 14 */ + D_ENCRYPT(r,l,24); /* 13 */ + D_ENCRYPT(l,r,22); /* 12 */ + D_ENCRYPT(r,l,20); /* 11 */ + D_ENCRYPT(l,r,18); /* 10 */ + D_ENCRYPT(r,l,16); /* 9 */ + D_ENCRYPT(l,r,14); /* 8 */ + D_ENCRYPT(r,l,12); /* 7 */ + D_ENCRYPT(l,r,10); /* 6 */ + D_ENCRYPT(r,l, 8); /* 5 */ + D_ENCRYPT(l,r, 6); /* 4 */ + D_ENCRYPT(r,l, 4); /* 3 */ + D_ENCRYPT(l,r, 2); /* 2 */ + D_ENCRYPT(r,l, 0); /* 1 */ +#else + for (i=30; i>0; i-=8) +{ + D_ENCRYPT(l,r,i-0); /* 16 */ + D_ENCRYPT(r,l,i-2); /* 15 */ + D_ENCRYPT(l,r,i-4); /* 14 */ + D_ENCRYPT(r,l,i-6); /* 13 */ +} +#endif +} + /* rotate and clear the top bits on machines with 8byte longs */ + data[0]=ROTATE(l,3)&0xffffffffL; + data[1]=ROTATE(r,3)&0xffffffffL; + l=r=t=u=0; +} + +/** + * Single block 3DES EDE encrypt routine + */ +static void des_encrypt3(DES_LONG *data, des_key_schedule ks1, + des_key_schedule ks2, des_key_schedule ks3) +{ + register DES_LONG l,r; + + l=data[0]; + r=data[1]; + IP(l,r); + data[0]=l; + data[1]=r; + des_encrypt2((DES_LONG *)data,ks1,DES_ENCRYPT); + des_encrypt2((DES_LONG *)data,ks2,DES_DECRYPT); + des_encrypt2((DES_LONG *)data,ks3,DES_ENCRYPT); + l=data[0]; + r=data[1]; + FP(r,l); + data[0]=l; + data[1]=r; +} + +/** + * Single block 3DES EDE decrypt routine + */ +static void des_decrypt3(DES_LONG *data, des_key_schedule ks1, + des_key_schedule ks2, des_key_schedule ks3) +{ + register DES_LONG l,r; + + l=data[0]; + r=data[1]; + IP(l,r); + data[0]=l; + data[1]=r; + des_encrypt2((DES_LONG *)data,ks3,DES_DECRYPT); + des_encrypt2((DES_LONG *)data,ks2,DES_ENCRYPT); + des_encrypt2((DES_LONG *)data,ks1,DES_DECRYPT); + l=data[0]; + r=data[1]; + FP(r,l); + data[0]=l; + data[1]=r; +} + +/** + * 3DES EDE CBC encrypt/decrypt routine + */ +static void des_ede3_cbc_encrypt(des_cblock *input, des_cblock *output, long length, + des_key_schedule ks1, des_key_schedule ks2, + des_key_schedule ks3, des_cblock *ivec, int enc) +{ + register DES_LONG tin0,tin1; + register DES_LONG tout0,tout1,xor0,xor1; + register unsigned char *in,*out; + register long l=length; + DES_LONG tin[2]; + unsigned char *iv; + + in=(unsigned char *)input; + out=(unsigned char *)output; + iv=(unsigned char *)ivec; + + if (enc) + { + c2l(iv,tout0); + c2l(iv,tout1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + tin0^=tout0; + tin1^=tout1; + + tin[0]=tin0; + tin[1]=tin1; + des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + l2c(tout0,out); + l2c(tout1,out); + } + if (l != -8) + { + c2ln(in,tin0,tin1,l+8); + tin0^=tout0; + tin1^=tout1; + + tin[0]=tin0; + tin[1]=tin1; + des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + l2c(tout0,out); + l2c(tout1,out); + } + iv=(unsigned char *)ivec; + l2c(tout0,iv); + l2c(tout1,iv); + } + else + { + register DES_LONG t0,t1; + + c2l(iv,xor0); + c2l(iv,xor1); + for (l-=8; l>=0; l-=8) + { + c2l(in,tin0); + c2l(in,tin1); + + t0=tin0; + t1=tin1; + + tin[0]=tin0; + tin[1]=tin1; + des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + tout0^=xor0; + tout1^=xor1; + l2c(tout0,out); + l2c(tout1,out); + xor0=t0; + xor1=t1; + } + if (l != -8) + { + c2l(in,tin0); + c2l(in,tin1); + + t0=tin0; + t1=tin1; + + tin[0]=tin0; + tin[1]=tin1; + des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3); + tout0=tin[0]; + tout1=tin[1]; + + tout0^=xor0; + tout1^=xor1; + l2cn(tout0,tout1,out,l+8); + xor0=t0; + xor1=t1; + } + + iv=(unsigned char *)ivec; + l2c(xor0,iv); + l2c(xor1,iv); + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; +} + +/** + * Implementation of crypter_t.decrypt for DES. + */ +static status_t decrypt(private_des_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted) +{ + des_cblock ivb; + + if (data.len % sizeof(des_cblock) != 0 || + iv.len != sizeof(des_cblock)) + { + return INVALID_ARG; + } + + *decrypted = chunk_alloc(data.len); + memcpy(&ivb, iv.ptr, sizeof(des_cblock)); + des_cbc_encrypt((des_cblock*)(data.ptr), (des_cblock*)(decrypted->ptr), + data.len, this->ks, &ivb, DES_DECRYPT); + return SUCCESS; +} + + +/** + * Implementation of crypter_t.decrypt for DES. + */ +static status_t encrypt(private_des_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted) +{ + des_cblock ivb; + + if (data.len % sizeof(des_cblock) != 0 || + iv.len != sizeof(des_cblock)) + { + return INVALID_ARG; + } + + *encrypted = chunk_alloc(data.len); + memcpy(&ivb, iv.ptr, sizeof(des_cblock)); + des_cbc_encrypt((des_cblock*)(data.ptr), (des_cblock*)(encrypted->ptr), + data.len, this->ks, &ivb, DES_ENCRYPT); + return SUCCESS; +} + +/** + * Implementation of crypter_t.decrypt for 3DES. + */ +static status_t decrypt3(private_des_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted) +{ + des_cblock ivb; + + if (data.len % sizeof(des_cblock) != 0 || + iv.len != sizeof(des_cblock)) + { + return INVALID_ARG; + } + + *decrypted = chunk_alloc(data.len); + memcpy(&ivb, iv.ptr, sizeof(des_cblock)); + des_ede3_cbc_encrypt((des_cblock*)(data.ptr), (des_cblock*)(decrypted->ptr), + data.len, this->ks3[0], this->ks3[1], this->ks3[2], + &ivb, DES_DECRYPT); + return SUCCESS; +} + +/** + * Implementation of crypter_t.decrypt for 3DES. + */ +static status_t encrypt3(private_des_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted) +{ + des_cblock ivb; + + if (data.len % sizeof(des_cblock) != 0 || + iv.len != sizeof(des_cblock)) + { + return INVALID_ARG; + } + + *encrypted = chunk_alloc(data.len); + memcpy(&ivb, iv.ptr, sizeof(des_cblock)); + des_ede3_cbc_encrypt((des_cblock*)(data.ptr), (des_cblock*)(encrypted->ptr), + data.len, this->ks3[0], this->ks3[1], this->ks3[2], + &ivb, DES_ENCRYPT); + return SUCCESS; +} + +/** + * Implementation of crypter_t.get_block_size. + */ +static size_t get_block_size (private_des_crypter_t *this) +{ + return sizeof(des_cblock); +} + +/** + * Implementation of crypter_t.get_key_size. + */ +static size_t get_key_size (private_des_crypter_t *this) +{ + return this->key_size; +} + +/** + * Implementation of crypter_t.set_key for DES. + */ +static status_t set_key(private_des_crypter_t *this, chunk_t key) +{ + if (key.len != sizeof(des_cblock)) + { + return INVALID_ARG; + } + + des_set_key((des_cblock*)(key.ptr), &this->ks); + + return SUCCESS; +} + +/** + * Implementation of crypter_t.set_key for 3DES. + */ +static status_t set_key3(private_des_crypter_t *this, chunk_t key) +{ + if (key.len != 3 * sizeof(des_cblock)) + { + return INVALID_ARG; + } + + des_set_key((des_cblock*)(key.ptr) + 0, &this->ks3[0]); + des_set_key((des_cblock*)(key.ptr) + 1, &this->ks3[1]); + des_set_key((des_cblock*)(key.ptr) + 2, &this->ks3[2]); + + return SUCCESS; +} + +/** + * Implementation of crypter_t.destroy and des_crypter_t.destroy. + */ +static void destroy(private_des_crypter_t *this) +{ + free(this); +} + +/* + * Described in header + */ +des_crypter_t *des_crypter_create(encryption_algorithm_t algo) +{ + private_des_crypter_t *this = malloc_thing(private_des_crypter_t); + + /* functions of crypter_t interface */ + this->public.crypter_interface.get_block_size = (size_t (*) (crypter_t *)) get_block_size; + this->public.crypter_interface.get_key_size = (size_t (*) (crypter_t *)) get_key_size; + this->public.crypter_interface.destroy = (void (*) (crypter_t *)) destroy; + + /* use functions depending on algorithm */ + switch (algo) + { + case ENCR_DES: + this->key_size = sizeof(des_cblock); + this->public.crypter_interface.set_key = (status_t (*) (crypter_t *,chunk_t)) set_key; + this->public.crypter_interface.encrypt = (status_t (*) (crypter_t *, chunk_t,chunk_t, chunk_t *)) encrypt; + this->public.crypter_interface.decrypt = (status_t (*) (crypter_t *, chunk_t , chunk_t, chunk_t *)) decrypt; + break; + case ENCR_3DES: + this->key_size = 3 * sizeof(des_cblock); + this->public.crypter_interface.set_key = (status_t (*) (crypter_t *,chunk_t)) set_key3; + this->public.crypter_interface.encrypt = (status_t (*) (crypter_t *, chunk_t,chunk_t, chunk_t *)) encrypt3; + this->public.crypter_interface.decrypt = (status_t (*) (crypter_t *, chunk_t , chunk_t, chunk_t *)) decrypt3; + break; + default: + free(this); + return NULL; + } + return &(this->public); +} diff --git a/src/libstrongswan/crypto/crypters/des_crypter.h b/src/libstrongswan/crypto/crypters/des_crypter.h new file mode 100644 index 000000000..0c87b0a9c --- /dev/null +++ b/src/libstrongswan/crypto/crypters/des_crypter.h @@ -0,0 +1,58 @@ +/** + * @file des_crypter.h + * + * @brief Interface of des_crypter_t + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DES_CRYPTER_H_ +#define DES_CRYPTER_H_ + +typedef struct des_crypter_t des_crypter_t; + +#include + + +/** + * @brief Class implementing the DES and 3DES encryption algorithms. + * + * @b Constructors: + * - des_crypter_create() + * + * @ingroup crypters + */ +struct des_crypter_t { + + /** + * The crypter_t interface. + */ + crypter_t crypter_interface; +}; + +/** + * @brief Constructor to create des_crypter_t objects. + * + * @param algo ENCR_DES for single DES, ENCR_3DES for triple DES + * @return + * - des_crypter_t object + * - NULL if algo not supported + */ +des_crypter_t *des_crypter_create(encryption_algorithm_t algo); + + +#endif /* DES_CRYPTER_H_ */ diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c new file mode 100644 index 000000000..e4062066c --- /dev/null +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -0,0 +1,612 @@ +/** + * @file diffie_hellman.c + * + * @brief Implementation of diffie_hellman_t. + * + */ + +/* + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * Copyright (C) 1999, 2000, 2001 Henry Spencer. + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "diffie_hellman.h" + +#include + +ENUM_BEGIN(diffie_hellman_group_names, MODP_NONE, MODP_1024_BIT, + "MODP_NONE", + "MODP_768_BIT", + "MODP_1024_BIT"); +ENUM_NEXT(diffie_hellman_group_names, MODP_1536_BIT, MODP_1536_BIT, MODP_1024_BIT, + "MODP_1536_BIT"); +ENUM_NEXT(diffie_hellman_group_names, MODP_2048_BIT, MODP_8192_BIT, MODP_1536_BIT, + "MODP_2048_BIT", + "MODP_3072_BIT", + "MODP_4096_BIT", + "MODP_6144_BIT", + "MODP_8192_BIT"); +ENUM_END(diffie_hellman_group_names, MODP_8192_BIT); + + +/** + * Modulus of Group 1 (MODP_768_BIT). + */ +static u_int8_t group1_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80 ,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x3A,0x36,0x20,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/** + * Modulus of Group 2 (MODP_1024_BIT). + */ +static u_int8_t group2_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE6,0x53,0x81,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/** + * Modulus of Group 5 (MODP_1536_BIT). + */ +static u_int8_t group5_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05, + 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, + 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, + 0xF1,0x74,0x6C,0x08,0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; +/** + * Modulus of Group 14 (MODP_2048_BIT). + */ +static u_int8_t group14_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05, + 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, + 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, + 0xF1,0x74,0x6C,0x08,0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F, + 0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18, + 0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAC,0xAA,0x68,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/** + * Modulus of Group 15 (MODP_3072_BIT). + */ +static u_int8_t group15_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05, + 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, + 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, + 0xF1,0x74,0x6C,0x08,0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F, + 0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18, + 0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D,0x04,0x50,0x7A,0x33, + 0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64,0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A, + 0x8A,0xEA,0x71,0x57,0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0,0x4A,0x25,0x61,0x9D, + 0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B,0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64, + 0xD8,0x76,0x02,0x73,0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0,0xBA,0xD9,0x46,0xE2, + 0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31,0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E, + 0x4B,0x82,0xD1,0x20,0xA9,0x3A,0xD2,0xCA,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/** + * Modulus of Group 16 (MODP_4096_BIT). + */ +static u_int8_t group16_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05, + 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, + 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, + 0xF1,0x74,0x6C,0x08,0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F, + 0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18, + 0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D,0x04,0x50,0x7A,0x33, + 0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64,0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A, + 0x8A,0xEA,0x71,0x57,0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0,0x4A,0x25,0x61,0x9D, + 0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B,0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64, + 0xD8,0x76,0x02,0x73,0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0,0xBA,0xD9,0x46,0xE2, + 0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31,0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E, + 0x4B,0x82,0xD1,0x20,0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, + 0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18,0x6A,0xF4,0xE2,0x3C, + 0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA,0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8, + 0xDB,0xBB,0xC2,0xDB,0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, + 0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F,0xA0,0x90,0xC3,0xA2, + 0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED,0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF, + 0xB8,0x1B,0xDD,0x76,0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, + 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC,0x90,0xA6,0xC0,0x8F, + 0x4D,0xF4,0x35,0xC9,0x34,0x06,0x31,0x99,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/** + * Modulus of Group 17 (MODP_6144_BIT). + */ +static u_int8_t group17_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05, + 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, + 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, + 0xF1,0x74,0x6C,0x08,0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F, + 0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18, + 0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D,0x04,0x50,0x7A,0x33, + 0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64,0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A, + 0x8A,0xEA,0x71,0x57,0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0,0x4A,0x25,0x61,0x9D, + 0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B,0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64, + 0xD8,0x76,0x02,0x73,0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0,0xBA,0xD9,0x46,0xE2, + 0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31,0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E, + 0x4B,0x82,0xD1,0x20,0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, + 0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18,0x6A,0xF4,0xE2,0x3C, + 0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA,0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8, + 0xDB,0xBB,0xC2,0xDB,0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, + 0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F,0xA0,0x90,0xC3,0xA2, + 0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED,0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF, + 0xB8,0x1B,0xDD,0x76,0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, + 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC,0x90,0xA6,0xC0,0x8F, + 0x4D,0xF4,0x35,0xC9,0x34,0x02,0x84,0x92,0x36,0xC3,0xFA,0xB4,0xD2,0x7C,0x70,0x26, + 0xC1,0xD4,0xDC,0xB2,0x60,0x26,0x46,0xDE,0xC9,0x75,0x1E,0x76,0x3D,0xBA,0x37,0xBD, + 0xF8,0xFF,0x94,0x06,0xAD,0x9E,0x53,0x0E,0xE5,0xDB,0x38,0x2F,0x41,0x30,0x01,0xAE, + 0xB0,0x6A,0x53,0xED,0x90,0x27,0xD8,0x31,0x17,0x97,0x27,0xB0,0x86,0x5A,0x89,0x18, + 0xDA,0x3E,0xDB,0xEB,0xCF,0x9B,0x14,0xED,0x44,0xCE,0x6C,0xBA,0xCE,0xD4,0xBB,0x1B, + 0xDB,0x7F,0x14,0x47,0xE6,0xCC,0x25,0x4B,0x33,0x20,0x51,0x51,0x2B,0xD7,0xAF,0x42, + 0x6F,0xB8,0xF4,0x01,0x37,0x8C,0xD2,0xBF,0x59,0x83,0xCA,0x01,0xC6,0x4B,0x92,0xEC, + 0xF0,0x32,0xEA,0x15,0xD1,0x72,0x1D,0x03,0xF4,0x82,0xD7,0xCE,0x6E,0x74,0xFE,0xF6, + 0xD5,0x5E,0x70,0x2F,0x46,0x98,0x0C,0x82,0xB5,0xA8,0x40,0x31,0x90,0x0B,0x1C,0x9E, + 0x59,0xE7,0xC9,0x7F,0xBE,0xC7,0xE8,0xF3,0x23,0xA9,0x7A,0x7E,0x36,0xCC,0x88,0xBE, + 0x0F,0x1D,0x45,0xB7,0xFF,0x58,0x5A,0xC5,0x4B,0xD4,0x07,0xB2,0x2B,0x41,0x54,0xAA, + 0xCC,0x8F,0x6D,0x7E,0xBF,0x48,0xE1,0xD8,0x14,0xCC,0x5E,0xD2,0x0F,0x80,0x37,0xE0, + 0xA7,0x97,0x15,0xEE,0xF2,0x9B,0xE3,0x28,0x06,0xA1,0xD5,0x8B,0xB7,0xC5,0xDA,0x76, + 0xF5,0x50,0xAA,0x3D,0x8A,0x1F,0xBF,0xF0,0xEB,0x19,0xCC,0xB1,0xA3,0x13,0xD5,0x5C, + 0xDA,0x56,0xC9,0xEC,0x2E,0xF2,0x96,0x32,0x38,0x7F,0xE8,0xD7,0x6E,0x3C,0x04,0x68, + 0x04,0x3E,0x8F,0x66,0x3F,0x48,0x60,0xEE,0x12,0xBF,0x2D,0x5B,0x0B,0x74,0x74,0xD6, + 0xE6,0x94,0xF9,0x1E,0x6D,0xCC,0x40,0x24,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/** + * Modulus of Group 18 (MODP_8192_BIT). + */ +static u_int8_t group18_modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34, + 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74, + 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, + 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, + 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, + 0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05, + 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, + 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, + 0xF1,0x74,0x6C,0x08,0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F, + 0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18, + 0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D,0x04,0x50,0x7A,0x33, + 0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64,0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A, + 0x8A,0xEA,0x71,0x57,0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0,0x4A,0x25,0x61,0x9D, + 0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B,0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64, + 0xD8,0x76,0x02,0x73,0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0,0xBA,0xD9,0x46,0xE2, + 0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31,0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E, + 0x4B,0x82,0xD1,0x20,0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, + 0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18,0x6A,0xF4,0xE2,0x3C, + 0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA,0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8, + 0xDB,0xBB,0xC2,0xDB,0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, + 0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F,0xA0,0x90,0xC3,0xA2, + 0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED,0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF, + 0xB8,0x1B,0xDD,0x76,0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, + 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC,0x90,0xA6,0xC0,0x8F, + 0x4D,0xF4,0x35,0xC9,0x34,0x02,0x84,0x92,0x36,0xC3,0xFA,0xB4,0xD2,0x7C,0x70,0x26, + 0xC1,0xD4,0xDC,0xB2,0x60,0x26,0x46,0xDE,0xC9,0x75,0x1E,0x76,0x3D,0xBA,0x37,0xBD, + 0xF8,0xFF,0x94,0x06,0xAD,0x9E,0x53,0x0E,0xE5,0xDB,0x38,0x2F,0x41,0x30,0x01,0xAE, + 0xB0,0x6A,0x53,0xED,0x90,0x27,0xD8,0x31,0x17,0x97,0x27,0xB0,0x86,0x5A,0x89,0x18, + 0xDA,0x3E,0xDB,0xEB,0xCF,0x9B,0x14,0xED,0x44,0xCE,0x6C,0xBA,0xCE,0xD4,0xBB,0x1B, + 0xDB,0x7F,0x14,0x47,0xE6,0xCC,0x25,0x4B,0x33,0x20,0x51,0x51,0x2B,0xD7,0xAF,0x42, + 0x6F,0xB8,0xF4,0x01,0x37,0x8C,0xD2,0xBF,0x59,0x83,0xCA,0x01,0xC6,0x4B,0x92,0xEC, + 0xF0,0x32,0xEA,0x15,0xD1,0x72,0x1D,0x03,0xF4,0x82,0xD7,0xCE,0x6E,0x74,0xFE,0xF6, + 0xD5,0x5E,0x70,0x2F,0x46,0x98,0x0C,0x82,0xB5,0xA8,0x40,0x31,0x90,0x0B,0x1C,0x9E, + 0x59,0xE7,0xC9,0x7F,0xBE,0xC7,0xE8,0xF3,0x23,0xA9,0x7A,0x7E,0x36,0xCC,0x88,0xBE, + 0x0F,0x1D,0x45,0xB7,0xFF,0x58,0x5A,0xC5,0x4B,0xD4,0x07,0xB2,0x2B,0x41,0x54,0xAA, + 0xCC,0x8F,0x6D,0x7E,0xBF,0x48,0xE1,0xD8,0x14,0xCC,0x5E,0xD2,0x0F,0x80,0x37,0xE0, + 0xA7,0x97,0x15,0xEE,0xF2,0x9B,0xE3,0x28,0x06,0xA1,0xD5,0x8B,0xB7,0xC5,0xDA,0x76, + 0xF5,0x50,0xAA,0x3D,0x8A,0x1F,0xBF,0xF0,0xEB,0x19,0xCC,0xB1,0xA3,0x13,0xD5,0x5C, + 0xDA,0x56,0xC9,0xEC,0x2E,0xF2,0x96,0x32,0x38,0x7F,0xE8,0xD7,0x6E,0x3C,0x04,0x68, + 0x04,0x3E,0x8F,0x66,0x3F,0x48,0x60,0xEE,0x12,0xBF,0x2D,0x5B,0x0B,0x74,0x74,0xD6, + 0xE6,0x94,0xF9,0x1E,0x6D,0xBE,0x11,0x59,0x74,0xA3,0x92,0x6F,0x12,0xFE,0xE5,0xE4, + 0x38,0x77,0x7C,0xB6,0xA9,0x32,0xDF,0x8C,0xD8,0xBE,0xC4,0xD0,0x73,0xB9,0x31,0xBA, + 0x3B,0xC8,0x32,0xB6,0x8D,0x9D,0xD3,0x00,0x74,0x1F,0xA7,0xBF,0x8A,0xFC,0x47,0xED, + 0x25,0x76,0xF6,0x93,0x6B,0xA4,0x24,0x66,0x3A,0xAB,0x63,0x9C,0x5A,0xE4,0xF5,0x68, + 0x34,0x23,0xB4,0x74,0x2B,0xF1,0xC9,0x78,0x23,0x8F,0x16,0xCB,0xE3,0x9D,0x65,0x2D, + 0xE3,0xFD,0xB8,0xBE,0xFC,0x84,0x8A,0xD9,0x22,0x22,0x2E,0x04,0xA4,0x03,0x7C,0x07, + 0x13,0xEB,0x57,0xA8,0x1A,0x23,0xF0,0xC7,0x34,0x73,0xFC,0x64,0x6C,0xEA,0x30,0x6B, + 0x4B,0xCB,0xC8,0x86,0x2F,0x83,0x85,0xDD,0xFA,0x9D,0x4B,0x7F,0xA2,0xC0,0x87,0xE8, + 0x79,0x68,0x33,0x03,0xED,0x5B,0xDD,0x3A,0x06,0x2B,0x3C,0xF5,0xB3,0xA2,0x78,0xA6, + 0x6D,0x2A,0x13,0xF8,0x3F,0x44,0xF8,0x2D,0xDF,0x31,0x0E,0xE0,0x74,0xAB,0x6A,0x36, + 0x45,0x97,0xE8,0x99,0xA0,0x25,0x5D,0xC1,0x64,0xF3,0x1C,0xC5,0x08,0x46,0x85,0x1D, + 0xF9,0xAB,0x48,0x19,0x5D,0xED,0x7E,0xA1,0xB1,0xD5,0x10,0xBD,0x7E,0xE7,0x4D,0x73, + 0xFA,0xF3,0x6B,0xC3,0x1E,0xCF,0xA2,0x68,0x35,0x90,0x46,0xF4,0xEB,0x87,0x9F,0x92, + 0x40,0x09,0x43,0x8B,0x48,0x1C,0x6C,0xD7,0x88,0x9A,0x00,0x2E,0xD5,0xEE,0x38,0x2B, + 0xC9,0x19,0x0D,0xA6,0xFC,0x02,0x6E,0x47,0x95,0x58,0xE4,0x47,0x56,0x77,0xE9,0xAA, + 0x9E,0x30,0x50,0xE2,0x76,0x56,0x94,0xDF,0xC8,0x1F,0x56,0xE8,0x80,0xB9,0x6E,0x71, + 0x60,0xC9,0x80,0xDD,0x98,0xED,0xD3,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +typedef struct modulus_info_entry_t modulus_info_entry_t; + +/** + * Entry of the modulus list. + */ +struct modulus_info_entry_t { + /** + * Group number as it is defined in file transform_substructure.h. + */ + diffie_hellman_group_t group; + + /** + * Pointer to first byte of modulus (network order). + */ + u_int8_t *modulus; + + /* + * Length of modulus in bytes. + */ + size_t modulus_length; + + /* + * Generator value. + */ + u_int16_t generator; +}; + + +/** + * All supported modulus values. + */ +static modulus_info_entry_t modulus_info_entries[] = { + {MODP_768_BIT,group1_modulus,sizeof(group1_modulus),2}, + {MODP_1024_BIT,group2_modulus,sizeof(group2_modulus),2}, + {MODP_1536_BIT,group5_modulus,sizeof(group5_modulus),2}, + {MODP_2048_BIT,group14_modulus,sizeof(group14_modulus),2}, + {MODP_3072_BIT,group15_modulus,sizeof(group15_modulus),2}, + {MODP_4096_BIT,group16_modulus,sizeof(group16_modulus),2}, + {MODP_6144_BIT,group17_modulus,sizeof(group17_modulus),2}, + {MODP_8192_BIT,group18_modulus,sizeof(group18_modulus),2}, +}; + +typedef struct private_diffie_hellman_t private_diffie_hellman_t; + +/** + * Private data of an diffie_hellman_t object. + * + */ +struct private_diffie_hellman_t { + /** + * Public diffie_hellman_t interface. + */ + diffie_hellman_t public; + + /** + * Diffie Hellman group number. + */ + u_int16_t dh_group_number; + + /** + * Modulus. + */ + mpz_t modulus; + + /** + * Modulus length. + */ + size_t modulus_length; + + /* + * Generator value. + */ + u_int16_t generator; + + /** + * My private value . + */ + mpz_t my_private_value; + + /** + * My public value. + */ + mpz_t my_public_value; + + /** + * Other public value. + */ + mpz_t other_public_value; + + /** + * Shared secret. + */ + mpz_t shared_secret; + + /** + * True if shared secret is computed and stored in my_public_value. + */ + bool shared_secret_is_computed; + + /** + * Sets the modulus for a specific diffie hellman group. + * + * @param this calling object + * @return + * SUCCESS if modulus could be found + * NOT_FOUND if modulus not supported + */ + status_t (*set_modulus) (private_diffie_hellman_t *this); + + /** + * Makes sure my public value is computed. + * + * @param this calling object + */ + void (*compute_public_value) (private_diffie_hellman_t *this); + + /** + * Computes shared secret (other public value must be available). + * + * @param this calling object + */ + void (*compute_shared_secret) (private_diffie_hellman_t *this); +}; + +/** + * Implementation of private_diffie_hellman_t.set_modulus. + */ +static status_t set_modulus(private_diffie_hellman_t *this) +{ + int i; + status_t status = NOT_FOUND; + + for (i = 0; i < (sizeof(modulus_info_entries) / sizeof(modulus_info_entry_t)); i++) + { + if (modulus_info_entries[i].group == this->dh_group_number) + { + chunk_t modulus_chunk; + modulus_chunk.ptr = modulus_info_entries[i].modulus; + modulus_chunk.len = modulus_info_entries[i].modulus_length; + mpz_import(this->modulus, modulus_chunk.len, 1, 1, 1, 0, modulus_chunk.ptr); + this->modulus_length = modulus_chunk.len; + this->generator = modulus_info_entries[i].generator; + status = SUCCESS; + break; + } + } + return status; +} + +/** + * Implementation of diffie_hellman_t.set_other_public_value. + */ +static void set_other_public_value(private_diffie_hellman_t *this,chunk_t public_value) +{ + mpz_import(this->other_public_value, public_value.len, 1, 1, 1, 0, public_value.ptr); + this->compute_shared_secret(this); +} + +/** + * Implementation of diffie_hellman_t.get_other_public_value. + */ +static status_t get_other_public_value(private_diffie_hellman_t *this,chunk_t *public_value) +{ + if (!this->shared_secret_is_computed) + { + return FAILED; + } + public_value->len = this->modulus_length; + public_value->ptr = mpz_export(NULL, NULL, 1, public_value->len, 1, 0, this->other_public_value); + return SUCCESS; +} + +/** + * Implementation of private_diffie_hellman_t.compute_shared_secret. + */ +static void compute_shared_secret (private_diffie_hellman_t *this) +{ + /* initialize my public value */ + mpz_init(this->shared_secret); + /* calculate my public value */ + mpz_powm(this->shared_secret,this->other_public_value,this->my_private_value,this->modulus); + + this->shared_secret_is_computed = TRUE; +} + +/** + * Implementation of private_diffie_hellman_t.compute_public_value. + */ +static void compute_public_value (private_diffie_hellman_t *this) +{ + mpz_t generator; + /* initialize generator and set it*/ + mpz_init_set_ui (generator,this->generator); + /* initialize my public value */ + mpz_init(this->my_public_value); + /* calculate my public value */ + mpz_powm(this->my_public_value,generator,this->my_private_value,this->modulus); + /* generator not used anymore */ + mpz_clear(generator); +} + +/** + * Implementation of diffie_hellman_t.get_my_public_value. + */ +static void get_my_public_value(private_diffie_hellman_t *this,chunk_t *public_value) +{ + public_value->len = this->modulus_length; + public_value->ptr = mpz_export(NULL, NULL, 1, public_value->len, 1, 0, this->my_public_value); +} + +/** + * Implementation of diffie_hellman_t.get_shared_secret. + */ +static status_t get_shared_secret(private_diffie_hellman_t *this,chunk_t *secret) +{ + if (!this->shared_secret_is_computed) + { + return FAILED; + } + secret->len = this->modulus_length; + secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->shared_secret); + return SUCCESS; +} + +/** + * Implementation of diffie_hellman_t.get_dh_group. + */ +static diffie_hellman_group_t get_dh_group(private_diffie_hellman_t *this) +{ + return this->dh_group_number; +} + +/** + * Implementation of diffie_hellman_t.destroy. + */ +static void destroy(private_diffie_hellman_t *this) +{ + mpz_clear(this->modulus); + mpz_clear(this->my_private_value); + mpz_clear(this->my_public_value); + mpz_clear(this->other_public_value); + + if (this->shared_secret_is_computed) + { + /* other public value gets initialized together with shared secret */ + mpz_clear(this->shared_secret); + } + free(this); +} + +/* + * Described in header. + */ +diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t dh_group_number) +{ + private_diffie_hellman_t *this = malloc_thing(private_diffie_hellman_t); + randomizer_t *randomizer; + chunk_t random_bytes; + + /* public functions */ + this->public.get_shared_secret = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_shared_secret; + this->public.set_other_public_value = (void (*)(diffie_hellman_t *, chunk_t )) set_other_public_value; + this->public.get_other_public_value = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_other_public_value; + this->public.get_my_public_value = (void (*)(diffie_hellman_t *, chunk_t *)) get_my_public_value; + this->public.get_dh_group = (diffie_hellman_group_t (*)(diffie_hellman_t *)) get_dh_group; + this->public.destroy = (void (*)(diffie_hellman_t *)) destroy; + + /* private functions */ + this->set_modulus = set_modulus; + this->compute_public_value = compute_public_value; + this->compute_shared_secret = compute_shared_secret; + + /* private variables */ + this->dh_group_number = dh_group_number; + mpz_init(this->modulus); + mpz_init(this->other_public_value); + mpz_init(this->my_private_value); + + /* set this->modulus */ + if (this->set_modulus(this) != SUCCESS) + { + free(this); + return NULL; + } + randomizer = randomizer_create(); + if (randomizer == NULL) + { + free(this); + return NULL; + } + if (randomizer->allocate_pseudo_random_bytes(randomizer, this->modulus_length, &random_bytes) != SUCCESS) + { + randomizer->destroy(randomizer); + free(this); + return NULL; + } + + mpz_import(this->my_private_value, random_bytes.len, 1, 1, 1, 0, random_bytes.ptr); + chunk_free(&random_bytes); + + randomizer->destroy(randomizer); + + this->compute_public_value(this); + + this->shared_secret_is_computed = FALSE; + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/diffie_hellman.h b/src/libstrongswan/crypto/diffie_hellman.h new file mode 100644 index 000000000..29a2ab45b --- /dev/null +++ b/src/libstrongswan/crypto/diffie_hellman.h @@ -0,0 +1,147 @@ +/** + * @file diffie_hellman.h + * + * @brief Interface of diffie_hellman_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DIFFIE_HELLMAN_H_ +#define DIFFIE_HELLMAN_H_ + +typedef enum diffie_hellman_group_t diffie_hellman_group_t; +typedef struct diffie_hellman_t diffie_hellman_t; + +#include + +/** + * @brief Diffie-Hellman group. + * + * The modulus (or group) to use for a Diffie-Hellman calculation. + * + * See IKEv2 RFC 3.3.2 and RFC 3526. + * + * @ingroup transforms + */ +enum diffie_hellman_group_t { + MODP_NONE = 0, + MODP_768_BIT = 1, + MODP_1024_BIT = 2, + MODP_1536_BIT = 5, + MODP_2048_BIT = 14, + MODP_3072_BIT = 15, + MODP_4096_BIT = 16, + MODP_6144_BIT = 17, + MODP_8192_BIT = 18 +}; + +/** + * enum name for diffie_hellman_group_t. + */ +extern enum_name_t *diffie_hellman_group_names; + +/** + * @brief Implementation of the widely used Diffie-Hellman algorithm. + * + * @b Constructors: + * - diffie_hellman_create() + * + * @ingroup transforms + */ +struct diffie_hellman_t { + + /** + * @brief Returns the shared secret of this diffie hellman exchange. + * + * @warning Space for returned secret is allocated and must be + * freed by the caller. + * + * @param this calling diffie_hellman_t object + * @param[out] secret shared secret will be written into this chunk + * @return + * - SUCCESS + * - FAILED if not both DH values are set + */ + status_t (*get_shared_secret) (diffie_hellman_t *this, chunk_t *secret); + + /** + * @brief Sets the public value of partner. + * + * chunk gets cloned and can be destroyed afterwards. + * + * @param this calling diffie_hellman_t object + * @param public_value public value of partner + */ + void (*set_other_public_value) (diffie_hellman_t *this, chunk_t public_value); + + /** + * @brief Gets the public value of partner. + * + * @warning Space for returned chunk is allocated and must be + * freed by the caller. + * + * @param this calling diffie_hellman_t object + * @param[out] public_value public value of partner is stored at this location + * @return + * - SUCCESS + * - FAILED if other public value not set + */ + status_t (*get_other_public_value) (diffie_hellman_t *this, chunk_t *public_value); + + /** + * @brief Gets the public value of caller + * + * @warning Space for returned chunk is allocated and must be + * freed by the caller. + * + * @param this calling diffie_hellman_t object + * @param[out] public_value public value of caller is stored at this location + */ + void (*get_my_public_value) (diffie_hellman_t *this, chunk_t *public_value); + + /** + * @brief Get the DH group used. + * + * @param this calling diffie_hellman_t object + * @return DH group set in construction + */ + diffie_hellman_group_t (*get_dh_group) (diffie_hellman_t *this); + + /** + * @brief Destroys an diffie_hellman_t object. + * + * @param this diffie_hellman_t object to destroy + */ + void (*destroy) (diffie_hellman_t *this); +}; + +/** + * @brief Creates a new diffie_hellman_t object. + * + * The first diffie hellman public value gets automatically created. + * + * @param dh_group_number Diffie Hellman group number to use + * @return + * - diffie_hellman_t object + * - NULL if dh group not supported + * + * @ingroup transforms + */ +diffie_hellman_t *diffie_hellman_create(diffie_hellman_group_t dh_group_number); + +#endif /*DIFFIE_HELLMAN_H_*/ diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c new file mode 100644 index 000000000..7fa6346d6 --- /dev/null +++ b/src/libstrongswan/crypto/hashers/hasher.c @@ -0,0 +1,65 @@ +/** + * @file hasher.c + * + * @brief Generic constructor for hasher_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "hasher.h" + +#include +#include +#include + + +ENUM(hash_algorithm_names, HASH_MD2, HASH_SHA512, + "HASH_MD2", + "HASH_MD5", + "HASH_SHA1", + "HASH_SHA256", + "HASH_SHA384", + "HASH_SHA512" +); + +/* + * Described in header. + */ +hasher_t *hasher_create(hash_algorithm_t hash_algorithm) +{ + switch (hash_algorithm) + { + case HASH_SHA1: + { + return (hasher_t*)sha1_hasher_create(); + } + case HASH_SHA256: + case HASH_SHA384: + case HASH_SHA512: + { + return (hasher_t*)sha2_hasher_create(hash_algorithm); + } + case HASH_MD5: + { + return (hasher_t*)md5_hasher_create(); + } + default: + return NULL; + } +} diff --git a/src/libstrongswan/crypto/hashers/hasher.h b/src/libstrongswan/crypto/hashers/hasher.h new file mode 100644 index 000000000..6c17f892d --- /dev/null +++ b/src/libstrongswan/crypto/hashers/hasher.h @@ -0,0 +1,159 @@ +/** + * @file hasher.h + * + * @brief Interface hasher_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef HASHER_H_ +#define HASHER_H_ + +typedef enum hash_algorithm_t hash_algorithm_t; +typedef struct hasher_t hasher_t; + +#include + +/** + * @brief Algorithms to use for hashing. + * + * Currently only the following algorithms are implemented: + * - HASH_MD5 + * - HASH_SHA1 + * - HASH_SHA256 + * - HASH_SHA384 + * - HASH_SHA512 + * + * @ingroup hashers + */ +enum hash_algorithm_t { + HASH_MD2 = 0, + /** Implemented in class md5_hasher_t */ + HASH_MD5 = 1, + /** Implemented in class sha1_hasher_t */ + HASH_SHA1 = 2, + /** Implemented in class sha2_hasher_t */ + HASH_SHA256 = 3, + /** Implemented in class sha2_hasher_t */ + HASH_SHA384 = 4, + /** Implemented in class sha2_hasher_t */ + HASH_SHA512 = 5, +}; + +#define HASH_SIZE_MD2 16 +#define HASH_SIZE_MD5 16 +#define HASH_SIZE_SHA1 20 +#define HASH_SIZE_SHA256 32 +#define HASH_SIZE_SHA384 48 +#define HASH_SIZE_SHA512 64 +#define HASH_SIZE_MAX 64 + +/** + * enum names for hash_algorithm_t. + */ +extern enum_name_t *hash_algorithm_names; + + +/** + * @brief Generic interface for all hash functions. + * + * @b Constructors: + * - hasher_create() + * + * @ingroup hashers + */ +struct hasher_t { + /** + * @brief Hash data and write it in the buffer. + * + * If the parameter hash is NULL, no result is written back + * an more data can be appended to already hashed data. + * If not, the result is written back and the hasher is reset. + * + * The hash output parameter must hold at least + * hash_t.get_block_size() bytes. + * + * @param this calling object + * @param data data to hash + * @param[out] hash pointer where the hash will be written + */ + void (*get_hash) (hasher_t *this, chunk_t data, u_int8_t *hash); + + /** + * @brief Hash data and allocate space for the hash. + * + * If the parameter hash is NULL, no result is written back + * an more data can be appended to already hashed data. + * If not, the result is written back and the hasher is reset. + * + * @param this calling object + * @param data chunk with data to hash + * @param[out] hash chunk which will hold allocated hash + */ + void (*allocate_hash) (hasher_t *this, chunk_t data, chunk_t *hash); + + /** + * @brief Get the size of the resulting hash. + * + * @param this calling object + * @return hash size in bytes + */ + size_t (*get_hash_size) (hasher_t *this); + + /** + * @brief Resets the hashers state. + * + * @param this calling object + */ + void (*reset) (hasher_t *this); + + /** + * @brief Get the state of the hasher. + * + * A hasher stores internal state information. This state may be + * manipulated to include a "seed" into the hashing operation. It used by + * some exotic protocols (such as AKA). + * The data pointed by chunk may be manipulated, but not replaced nor freed. + * This is more a hack than a feature. The hasher's state may be byte + * order dependant; use with care. + * + * @param this calling object + */ + chunk_t (*get_state) (hasher_t *this); + + /** + * @brief Destroys a hasher object. + * + * @param this calling object + */ + void (*destroy) (hasher_t *this); +}; + +/** + * @brief Generic interface to create a hasher_t. + * + * @param hash_algorithm Algorithm to use for hashing + * @return + * - hasher_t object + * - NULL if algorithm not supported + * + * @ingroup hashers + */ +hasher_t *hasher_create(hash_algorithm_t hash_algorithm); + +#endif /* HASHER_H_ */ diff --git a/src/libstrongswan/crypto/hashers/md5_hasher.c b/src/libstrongswan/crypto/hashers/md5_hasher.c new file mode 100644 index 000000000..d4dde3693 --- /dev/null +++ b/src/libstrongswan/crypto/hashers/md5_hasher.c @@ -0,0 +1,405 @@ +/** + * @file md5_hasher.c + * + * @brief Implementation of md5_hasher_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * Copyright (C) 1991-1992, RSA Data Security, Inc. Created 1991. + * All rights reserved. + * + * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm. + * Ported to fulfill hasher_t interface. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "md5_hasher.h" + + +/* Constants for MD5Transform routine. */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static u_int8_t PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * ugly macro stuff + */ +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (u_int32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + + + +typedef struct private_md5_hasher_t private_md5_hasher_t; + +/** + * Private data structure with hasing context. + */ +struct private_md5_hasher_t { + /** + * Public interface for this hasher. + */ + md5_hasher_t public; + + /* + * State of the hasher. + */ + u_int32_t state[5]; + u_int32_t count[2]; + u_int8_t buffer[64]; +}; + + +#if BYTE_ORDER != LITTLE_ENDIAN + +/* Encodes input (u_int32_t) into output (u_int8_t). Assumes len is + * a multiple of 4. + */ +static void Encode (u_int8_t *output, u_int32_t *input, size_t len) +{ + size_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (u_int8_t)(input[i] & 0xff); + output[j+1] = (u_int8_t)((input[i] >> 8) & 0xff); + output[j+2] = (u_int8_t)((input[i] >> 16) & 0xff); + output[j+3] = (u_int8_t)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (u_int8_t) into output (u_int32_t). Assumes len is + * a multiple of 4. + */ +static void Decode(u_int32_t *output, u_int8_t *input, size_t len) +{ + size_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[i] = ((u_int32_t)input[j]) | (((u_int32_t)input[j+1]) << 8) | + (((u_int32_t)input[j+2]) << 16) | (((u_int32_t)input[j+3]) << 24); + } +} + +#elif BYTE_ORDER == LITTLE_ENDIAN + #define Encode memcpy + #define Decode memcpy +#endif + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform(u_int32_t state[4], u_int8_t block[64]) +{ + u_int32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/* MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ +static void MD5Update(private_md5_hasher_t *this, u_int8_t *input, size_t inputLen) +{ + u_int32_t i; + size_t index, partLen; + + /* Compute number of bytes mod 64 */ + index = (u_int8_t)((this->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((this->count[0] += (inputLen << 3)) < (inputLen << 3)) + { + this->count[1]++; + } + this->count[1] += (inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) + { + memcpy(&this->buffer[index], input, partLen); + MD5Transform (this->state, this->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + { + MD5Transform (this->state, &input[i]); + } + index = 0; + } + else + { + i = 0; + } + + /* Buffer remaining input */ + memcpy(&this->buffer[index], &input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +static void MD5Final (private_md5_hasher_t *this, u_int8_t digest[16]) +{ + u_int8_t bits[8]; + size_t index, padLen; + + /* Save number of bits */ + Encode (bits, this->count, 8); + + /* Pad out to 56 mod 64. */ + index = (size_t)((this->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (this, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (this, bits, 8); + + if (digest != NULL) /* Bill Simpson's padding */ + { + /* store state in digest */ + Encode (digest, this->state, 16); + } +} + + + +/** + * Implementation of hasher_t.get_hash. + */ +static void get_hash(private_md5_hasher_t *this, chunk_t chunk, u_int8_t *buffer) +{ + MD5Update(this, chunk.ptr, chunk.len); + if (buffer != NULL) + { + MD5Final(this, buffer); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + } +} + + +/** + * Implementation of hasher_t.allocate_hash. + */ +static void allocate_hash(private_md5_hasher_t *this, chunk_t chunk, chunk_t *hash) +{ + chunk_t allocated_hash; + + MD5Update(this, chunk.ptr, chunk.len); + if (hash != NULL) + { + allocated_hash.ptr = malloc(HASH_SIZE_MD5); + allocated_hash.len = HASH_SIZE_MD5; + + MD5Final(this, allocated_hash.ptr); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + + *hash = allocated_hash; + } +} + +/** + * Implementation of hasher_t.get_hash_size. + */ +static size_t get_hash_size(private_md5_hasher_t *this) +{ + return HASH_SIZE_MD5; +} + +/** + * Implementation of hasher_t.reset. + */ +static void reset(private_md5_hasher_t *this) +{ + this->state[0] = 0x67452301; + this->state[1] = 0xefcdab89; + this->state[2] = 0x98badcfe; + this->state[3] = 0x10325476; + this->count[0] = 0; + this->count[1] = 0; +} + +/** + * Implementation of hasher_t.get_state + */ +static chunk_t get_state(private_md5_hasher_t *this) +{ + chunk_t chunk; + + chunk.ptr = (u_char*)&this->state[0]; + chunk.len = sizeof(this->state); + + return chunk; +} + +/** + * Implementation of hasher_t.destroy. + */ +static void destroy(private_md5_hasher_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +md5_hasher_t *md5_hasher_create(void) +{ + private_md5_hasher_t *this = malloc_thing(private_md5_hasher_t); + + this->public.hasher_interface.get_hash = (void (*) (hasher_t*, chunk_t, u_int8_t*))get_hash; + this->public.hasher_interface.allocate_hash = (void (*) (hasher_t*, chunk_t, chunk_t*))allocate_hash; + this->public.hasher_interface.get_hash_size = (size_t (*) (hasher_t*))get_hash_size; + this->public.hasher_interface.reset = (void (*) (hasher_t*))reset; + this->public.hasher_interface.get_state = (chunk_t (*) (hasher_t*))get_state; + this->public.hasher_interface.destroy = (void (*) (hasher_t*))destroy; + + /* initialize */ + reset(this); + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/hashers/md5_hasher.h b/src/libstrongswan/crypto/hashers/md5_hasher.h new file mode 100644 index 000000000..715f11663 --- /dev/null +++ b/src/libstrongswan/crypto/hashers/md5_hasher.h @@ -0,0 +1,60 @@ +/** + * @file md5_hasher.h + * + * @brief Interface for md5_hasher_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef MD5_HASHER_H_ +#define MD5_HASHER_H_ + +typedef struct md5_hasher_t md5_hasher_t; + +#include + +/** + * @brief Implementation of hasher_t interface using the + * MD5 algorithm. + * + * @b Constructors: + * - hasher_create() using HASH_MD5 as algorithm + * - md5_hasher_create() + * + * @see hasher_t + * + * @ingroup hashers + */ +struct md5_hasher_t { + + /** + * Generic hasher_t interface for this hasher. + */ + hasher_t hasher_interface; +}; + +/** + * @brief Creates a new md5_hasher_t. + * + * @return md5_hasher_t object + * + * @ingroup hashers + */ +md5_hasher_t *md5_hasher_create(void); + +#endif /*MD5_HASHER_H_*/ diff --git a/src/libstrongswan/crypto/hashers/sha1_hasher.c b/src/libstrongswan/crypto/hashers/sha1_hasher.c new file mode 100644 index 000000000..6a86937ae --- /dev/null +++ b/src/libstrongswan/crypto/hashers/sha1_hasher.c @@ -0,0 +1,280 @@ +/** + * @file sha1_hasher.c + * + * @brief Implementation of hasher_sha_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * Ported from Steve Reid's implementation + * "SHA1 in C" found in strongSwan. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "sha1_hasher.h" + +/* + * ugly macro stuff + */ +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +#if BYTE_ORDER == LITTLE_ENDIAN + #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN + #define blk0(i) block->l[i] +#else + #error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +typedef struct private_sha1_hasher_t private_sha1_hasher_t; + +/** + * Private data structure with hasing context. + */ +struct private_sha1_hasher_t { + /** + * Public interface for this hasher. + */ + sha1_hasher_t public; + + /* + * State of the hasher. + */ + u_int32_t state[5]; + u_int32_t count[2]; + u_int8_t buffer[64]; +}; + +/* + * Hash a single 512-bit block. This is the core of the algorithm. * + */ +static void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) +{ + u_int32_t a, b, c, d, e; + typedef union { + u_int8_t c[64]; + u_int32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + memcpy(block, buffer, 64); + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; + memset(block, '\0', sizeof(block)); +} + +/* + * Run your data through this. + */ +static void SHA1Update(private_sha1_hasher_t* this, u_int8_t *data, u_int32_t len) +{ + u_int32_t i; + u_int32_t j; + + j = this->count[0]; + if ((this->count[0] += len << 3) < j) + { + this->count[1]++; + } + this->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) + { + memcpy(&this->buffer[j], data, (i = 64-j)); + SHA1Transform(this->state, this->buffer); + for ( ; i + 63 < len; i += 64) + { + SHA1Transform(this->state, &data[i]); + } + j = 0; + } + else + { + i = 0; + } + memcpy(&this->buffer[j], &data[i], len - i); +} + + +/* + * Add padding and return the message digest. + */ +static void SHA1Final(private_sha1_hasher_t *this, u_int8_t *digest) +{ + u_int32_t i; + u_int8_t finalcount[8]; + u_int8_t c; + + for (i = 0; i < 8; i++) + { + finalcount[i] = (u_int8_t)((this->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + c = 0200; + SHA1Update(this, &c, 1); + while ((this->count[0] & 504) != 448) + { + c = 0000; + SHA1Update(this, &c, 1); + } + SHA1Update(this, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) + { + digest[i] = (u_int8_t)((this->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + + +/** + * Implementation of hasher_t.get_hash. + */ +static void get_hash(private_sha1_hasher_t *this, chunk_t chunk, u_int8_t *buffer) +{ + SHA1Update(this, chunk.ptr, chunk.len); + if (buffer != NULL) + { + SHA1Final(this, buffer); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + } +} + + +/** + * Implementation of hasher_t.allocate_hash. + */ +static void allocate_hash(private_sha1_hasher_t *this, chunk_t chunk, chunk_t *hash) +{ + chunk_t allocated_hash; + + SHA1Update(this, chunk.ptr, chunk.len); + if (hash != NULL) + { + allocated_hash.ptr = malloc(HASH_SIZE_SHA1); + allocated_hash.len = HASH_SIZE_SHA1; + + SHA1Final(this, allocated_hash.ptr); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + + *hash = allocated_hash; + } +} + +/** + * Implementation of hasher_t.get_hash_size. + */ +static size_t get_hash_size(private_sha1_hasher_t *this) +{ + return HASH_SIZE_SHA1; +} + +/** + * Implementation of hasher_t.reset. + */ +static void reset(private_sha1_hasher_t *this) +{ + this->state[0] = 0x67452301; + this->state[1] = 0xEFCDAB89; + this->state[2] = 0x98BADCFE; + this->state[3] = 0x10325476; + this->state[4] = 0xC3D2E1F0; + this->count[0] = 0; + this->count[1] = 0; +} + +/** + * Implementation of hasher_t.get_state + */ +static chunk_t get_state(private_sha1_hasher_t *this) +{ + chunk_t chunk; + + chunk.ptr = (u_char*)&this->state[0]; + chunk.len = sizeof(this->state); + + return chunk; +} + +/** + * Implementation of hasher_t.destroy. + */ +static void destroy(private_sha1_hasher_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +sha1_hasher_t *sha1_hasher_create(void) +{ + private_sha1_hasher_t *this = malloc_thing(private_sha1_hasher_t); + + this->public.hasher_interface.get_hash = (void (*) (hasher_t*, chunk_t, u_int8_t*))get_hash; + this->public.hasher_interface.allocate_hash = (void (*) (hasher_t*, chunk_t, chunk_t*))allocate_hash; + this->public.hasher_interface.get_hash_size = (size_t (*) (hasher_t*))get_hash_size; + this->public.hasher_interface.reset = (void (*) (hasher_t*))reset; + this->public.hasher_interface.get_state = (chunk_t (*) (hasher_t*))get_state; + this->public.hasher_interface.destroy = (void (*) (hasher_t*))destroy; + + /* initialize */ + reset(this); + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/hashers/sha1_hasher.h b/src/libstrongswan/crypto/hashers/sha1_hasher.h new file mode 100644 index 000000000..380fa9845 --- /dev/null +++ b/src/libstrongswan/crypto/hashers/sha1_hasher.h @@ -0,0 +1,60 @@ +/** + * @file sha1_hasher.h + * + * @brief Interface of sha1_hasher_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SHA1_HASHER_H_ +#define SHA1_HASHER_H_ + +typedef struct sha1_hasher_t sha1_hasher_t; + +#include + +/** + * @brief Implementation of hasher_t interface using the + * SHA1 algorithm. + * + * @b Constructors: + * - hasher_create() using HASH_SHA1 as algorithm + * - sha1_hasher_create() + * + * @see hasher_t + * + * @ingroup hashers + */ +struct sha1_hasher_t { + + /** + * Generic hasher_t interface for this hasher. + */ + hasher_t hasher_interface; +}; + +/** + * @brief Creates a new sha1_hasher_t. + * + * @return sha1_hasher_t object + * + * @ingroup hashers + */ +sha1_hasher_t *sha1_hasher_create(void); + +#endif /*SHA1_HASHER_H_*/ diff --git a/src/libstrongswan/crypto/hashers/sha2_hasher.c b/src/libstrongswan/crypto/hashers/sha2_hasher.c new file mode 100644 index 000000000..b68972cec --- /dev/null +++ b/src/libstrongswan/crypto/hashers/sha2_hasher.c @@ -0,0 +1,672 @@ +/** + * @file sha2_hasher.c + * + * @brief Implementation of hasher_sha_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2001 Jari Ruusu. + * + * Ported from strongSwans implementation written by Jari Ruusu. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "sha2_hasher.h" + + +typedef struct private_sha512_hasher_t private_sha512_hasher_t; + +/** + * Private data structure with hasing context for SHA384 and SHA512 + */ +struct private_sha512_hasher_t { + /** + * Public interface for this hasher. + */ + sha2_hasher_t public; + + unsigned char sha_out[128]; /* results are here, bytes 0..47/0..63 */ + u_int64_t sha_H[8]; + u_int64_t sha_blocks; + u_int64_t sha_blocksMSB; + int sha_bufCnt; +}; + + +typedef struct private_sha256_hasher_t private_sha256_hasher_t; + +/** + * Private data structure with hasing context for SHA256 + */ +struct private_sha256_hasher_t { + /** + * Public interface for this hasher. + */ + sha2_hasher_t public; + + unsigned char sha_out[64]; /* results are here, bytes 0...31 */ + u_int32_t sha_H[8]; + u_int64_t sha_blocks; + int sha_bufCnt; +}; + + +static const u_int32_t sha256_hashInit[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19 +}; + +static const u_int32_t sha256_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static const u_int64_t sha512_hashInit[8] = { + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +static const u_int64_t sha384_hashInit[8] = { + 0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL +}; + +static const u_int64_t sha512_K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + + +/* set macros for SHA256 */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define R(x,y) ((y) >> (x)) + +#define S(x,y) (((y) >> (x)) | ((y) << (32 - (x)))) +#define uSig0(x) ((S(2,(x))) ^ (S(13,(x))) ^ (S(22,(x)))) +#define uSig1(x) ((S(6,(x))) ^ (S(11,(x))) ^ (S(25,(x)))) +#define lSig0(x) ((S(7,(x))) ^ (S(18,(x))) ^ (R(3,(x)))) +#define lSig1(x) ((S(17,(x))) ^ (S(19,(x))) ^ (R(10,(x)))) + +/** + * Single block SHA256 transformation + */ +static void sha256_transform(private_sha256_hasher_t *ctx, + const unsigned char *datap) +{ + register int j; + u_int32_t a, b, c, d, e, f, g, h; + u_int32_t T1, T2, W[64], Wm2, Wm15; + + /* read the data, big endian byte order */ + j = 0; + do { + W[j] = (((u_int32_t)(datap[0]))<<24) | (((u_int32_t)(datap[1]))<<16) | + (((u_int32_t)(datap[2]))<<8 ) | ((u_int32_t)(datap[3])); + datap += 4; + } while(++j < 16); + + /* initialize variables a...h */ + a = ctx->sha_H[0]; + b = ctx->sha_H[1]; + c = ctx->sha_H[2]; + d = ctx->sha_H[3]; + e = ctx->sha_H[4]; + f = ctx->sha_H[5]; + g = ctx->sha_H[6]; + h = ctx->sha_H[7]; + + /* apply compression function */ + j = 0; + do + { + if(j >= 16) + { + Wm2 = W[j - 2]; + Wm15 = W[j - 15]; + W[j] = lSig1(Wm2) + W[j - 7] + lSig0(Wm15) + W[j - 16]; + } + T1 = h + uSig1(e) + Ch(e,f,g) + sha256_K[j] + W[j]; + T2 = uSig0(a) + Maj(a,b,c); + h = g; g = f; f = e; + e = d + T1; + d = c; c = b; b = a; + a = T1 + T2; + } while(++j < 64); + + /* compute intermediate hash value */ + ctx->sha_H[0] += a; + ctx->sha_H[1] += b; + ctx->sha_H[2] += c; + ctx->sha_H[3] += d; + ctx->sha_H[4] += e; + ctx->sha_H[5] += f; + ctx->sha_H[6] += g; + ctx->sha_H[7] += h; + + ctx->sha_blocks++; +} + +/** + * Update SHA256 hash + */ +static void sha256_write(private_sha256_hasher_t *ctx, + const unsigned char *datap, int length) +{ + while(length > 0) + { + if(!ctx->sha_bufCnt) + { + while(length >= sizeof(ctx->sha_out)) + { + sha256_transform(ctx, datap); + datap += sizeof(ctx->sha_out); + length -= sizeof(ctx->sha_out); + } + if(!length) return; + } + ctx->sha_out[ctx->sha_bufCnt] = *datap++; + length--; + if(++ctx->sha_bufCnt == sizeof(ctx->sha_out)) + { + sha256_transform(ctx, &ctx->sha_out[0]); + ctx->sha_bufCnt = 0; + } + } +} + +/** + * finalize SHA256 hash + */ +static void sha256_final(private_sha256_hasher_t *ctx) +{ + register int j; + u_int64_t bitLength; + u_int32_t i; + unsigned char padByte, *datap; + + bitLength = (ctx->sha_blocks << 9) | (ctx->sha_bufCnt << 3); + padByte = 0x80; + sha256_write(ctx, &padByte, 1); + + /* pad extra space with zeroes */ + padByte = 0; + while(ctx->sha_bufCnt != 56) + { + sha256_write(ctx, &padByte, 1); + } + + /* write bit length, big endian byte order */ + ctx->sha_out[56] = bitLength >> 56; + ctx->sha_out[57] = bitLength >> 48; + ctx->sha_out[58] = bitLength >> 40; + ctx->sha_out[59] = bitLength >> 32; + ctx->sha_out[60] = bitLength >> 24; + ctx->sha_out[61] = bitLength >> 16; + ctx->sha_out[62] = bitLength >> 8; + ctx->sha_out[63] = bitLength; + sha256_transform(ctx, &ctx->sha_out[0]); + + /* return results in ctx->sha_out[0...31] */ + datap = &ctx->sha_out[0]; + j = 0; + do { + i = ctx->sha_H[j]; + datap[0] = i >> 24; + datap[1] = i >> 16; + datap[2] = i >> 8; + datap[3] = i; + datap += 4; + } while(++j < 8); +} + +/* update macros for SHA512 */ +#undef S +#undef uSig0 +#undef uSig1 +#undef lSig0 +#undef lSig1 +#define S(x,y) (((y) >> (x)) | ((y) << (64 - (x)))) +#define uSig0(x) ((S(28,(x))) ^ (S(34,(x))) ^ (S(39,(x)))) +#define uSig1(x) ((S(14,(x))) ^ (S(18,(x))) ^ (S(41,(x)))) +#define lSig0(x) ((S(1,(x))) ^ (S(8,(x))) ^ (R(7,(x)))) +#define lSig1(x) ((S(19,(x))) ^ (S(61,(x))) ^ (R(6,(x)))) + +/** + * Single block SHA384/SHA512 transformation + */ +static void sha512_transform(private_sha512_hasher_t *ctx, + const unsigned char *datap) +{ + register int j; + u_int64_t a, b, c, d, e, f, g, h; + u_int64_t T1, T2, W[80], Wm2, Wm15; + + /* read the data, big endian byte order */ + j = 0; + do { + W[j] = (((u_int64_t)(datap[0]))<<56) | (((u_int64_t)(datap[1]))<<48) | + (((u_int64_t)(datap[2]))<<40) | (((u_int64_t)(datap[3]))<<32) | + (((u_int64_t)(datap[4]))<<24) | (((u_int64_t)(datap[5]))<<16) | + (((u_int64_t)(datap[6]))<<8 ) | ((u_int64_t)(datap[7])); + datap += 8; + } while(++j < 16); + + /* initialize variables a...h */ + a = ctx->sha_H[0]; + b = ctx->sha_H[1]; + c = ctx->sha_H[2]; + d = ctx->sha_H[3]; + e = ctx->sha_H[4]; + f = ctx->sha_H[5]; + g = ctx->sha_H[6]; + h = ctx->sha_H[7]; + + /* apply compression function */ + j = 0; + do { + if(j >= 16) { + Wm2 = W[j - 2]; + Wm15 = W[j - 15]; + W[j] = lSig1(Wm2) + W[j - 7] + lSig0(Wm15) + W[j - 16]; + } + T1 = h + uSig1(e) + Ch(e,f,g) + sha512_K[j] + W[j]; + T2 = uSig0(a) + Maj(a,b,c); + h = g; g = f; f = e; + e = d + T1; + d = c; c = b; b = a; + a = T1 + T2; + } while(++j < 80); + + /* compute intermediate hash value */ + ctx->sha_H[0] += a; + ctx->sha_H[1] += b; + ctx->sha_H[2] += c; + ctx->sha_H[3] += d; + ctx->sha_H[4] += e; + ctx->sha_H[5] += f; + ctx->sha_H[6] += g; + ctx->sha_H[7] += h; + + ctx->sha_blocks++; + if(!ctx->sha_blocks) ctx->sha_blocksMSB++; +} + +/** + * Update a SHA384/SHA512 hash + */ +static void sha512_write(private_sha512_hasher_t *ctx, + const unsigned char *datap, int length) +{ + while(length > 0) + { + if(!ctx->sha_bufCnt) + { + while(length >= sizeof(ctx->sha_out)) + { + sha512_transform(ctx, datap); + datap += sizeof(ctx->sha_out); + length -= sizeof(ctx->sha_out); + } + if(!length) return; + } + ctx->sha_out[ctx->sha_bufCnt] = *datap++; + length--; + if(++ctx->sha_bufCnt == sizeof(ctx->sha_out)) + { + sha512_transform(ctx, &ctx->sha_out[0]); + ctx->sha_bufCnt = 0; + } + } +} + +/** + * Finalize a SHA384/SHA512 hash + */ +static void sha512_final(private_sha512_hasher_t *ctx) +{ + register int j; + u_int64_t bitLength, bitLengthMSB; + u_int64_t i; + unsigned char padByte, *datap; + + bitLength = (ctx->sha_blocks << 10) | (ctx->sha_bufCnt << 3); + bitLengthMSB = (ctx->sha_blocksMSB << 10) | (ctx->sha_blocks >> 54); + padByte = 0x80; + sha512_write(ctx, &padByte, 1); + + /* pad extra space with zeroes */ + padByte = 0; + while(ctx->sha_bufCnt != 112) + { + sha512_write(ctx, &padByte, 1); + } + + /* write bit length, big endian byte order */ + ctx->sha_out[112] = bitLengthMSB >> 56; + ctx->sha_out[113] = bitLengthMSB >> 48; + ctx->sha_out[114] = bitLengthMSB >> 40; + ctx->sha_out[115] = bitLengthMSB >> 32; + ctx->sha_out[116] = bitLengthMSB >> 24; + ctx->sha_out[117] = bitLengthMSB >> 16; + ctx->sha_out[118] = bitLengthMSB >> 8; + ctx->sha_out[119] = bitLengthMSB; + ctx->sha_out[120] = bitLength >> 56; + ctx->sha_out[121] = bitLength >> 48; + ctx->sha_out[122] = bitLength >> 40; + ctx->sha_out[123] = bitLength >> 32; + ctx->sha_out[124] = bitLength >> 24; + ctx->sha_out[125] = bitLength >> 16; + ctx->sha_out[126] = bitLength >> 8; + ctx->sha_out[127] = bitLength; + sha512_transform(ctx, &ctx->sha_out[0]); + + /* return results in ctx->sha_out[0...63] */ + datap = &ctx->sha_out[0]; + j = 0; + do { + i = ctx->sha_H[j]; + datap[0] = i >> 56; + datap[1] = i >> 48; + datap[2] = i >> 40; + datap[3] = i >> 32; + datap[4] = i >> 24; + datap[5] = i >> 16; + datap[6] = i >> 8; + datap[7] = i; + datap += 8; + } while(++j < 8); +} + +/** + * Implementation of hasher_t.get_hash for SHA256. + */ +static void get_hash256(private_sha256_hasher_t *this, + chunk_t chunk, u_int8_t *buffer) +{ + sha256_write(this, chunk.ptr, chunk.len); + if (buffer != NULL) + { + sha256_final(this); + memcpy(buffer, this->sha_out, HASH_SIZE_SHA256); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + } +} + +/** + * Implementation of hasher_t.get_hash for SHA384. + */ +static void get_hash384(private_sha512_hasher_t *this, + chunk_t chunk, u_int8_t *buffer) +{ + sha512_write(this, chunk.ptr, chunk.len); + if (buffer != NULL) + { + sha512_final(this); + memcpy(buffer, this->sha_out, HASH_SIZE_SHA384); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + } +} + +/** + * Implementation of hasher_t.get_hash for SHA512. + */ +static void get_hash512(private_sha512_hasher_t *this, + chunk_t chunk, u_int8_t *buffer) +{ + sha512_write(this, chunk.ptr, chunk.len); + if (buffer != NULL) + { + sha512_final(this); + memcpy(buffer, this->sha_out, HASH_SIZE_SHA512); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + } +} + +/** + * Implementation of hasher_t.allocate_hash for SHA256. + */ +static void allocate_hash256(private_sha256_hasher_t *this, + chunk_t chunk, chunk_t *hash) +{ + chunk_t allocated_hash; + + sha256_write(this, chunk.ptr, chunk.len); + if (hash != NULL) + { + sha256_final(this); + allocated_hash = chunk_alloc(HASH_SIZE_SHA256); + memcpy(allocated_hash.ptr, this->sha_out, HASH_SIZE_SHA256); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + *hash = allocated_hash; + } +} + +/** + * Implementation of hasher_t.allocate_hash for SHA384. + */ +static void allocate_hash384(private_sha512_hasher_t *this, + chunk_t chunk, chunk_t *hash) +{ + chunk_t allocated_hash; + + sha512_write(this, chunk.ptr, chunk.len); + if (hash != NULL) + { + sha512_final(this); + allocated_hash = chunk_alloc(HASH_SIZE_SHA384); + memcpy(allocated_hash.ptr, this->sha_out, HASH_SIZE_SHA384); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + *hash = allocated_hash; + } +} + +/** + * Implementation of hasher_t.allocate_hash for SHA512. + */ +static void allocate_hash512(private_sha512_hasher_t *this, + chunk_t chunk, chunk_t *hash) +{ + chunk_t allocated_hash; + + sha512_write(this, chunk.ptr, chunk.len); + if (hash != NULL) + { + sha512_final(this); + allocated_hash = chunk_alloc(HASH_SIZE_SHA512); + memcpy(allocated_hash.ptr, this->sha_out, HASH_SIZE_SHA512); + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + *hash = allocated_hash; + } +} + +/** + * Implementation of hasher_t.get_hash_size for SHA256. + */ +static size_t get_hash_size256(private_sha256_hasher_t *this) +{ + return HASH_SIZE_SHA256; +} + +/** + * Implementation of hasher_t.get_hash_size for SHA384. + */ +static size_t get_hash_size384(private_sha512_hasher_t *this) +{ + return HASH_SIZE_SHA384; +} + +/** + * Implementation of hasher_t.get_hash_size for SHA512. + */ +static size_t get_hash_size512(private_sha512_hasher_t *this) +{ + return HASH_SIZE_SHA512; +} + +/** + * Implementation of hasher_t.reset for SHA256 + */ +static void reset256(private_sha256_hasher_t *ctx) +{ + memcpy(&ctx->sha_H[0], &sha256_hashInit[0], sizeof(ctx->sha_H)); + ctx->sha_blocks = 0; + ctx->sha_bufCnt = 0; +} + +/** + * Implementation of hasher_t.reset for SHA384 + */ +static void reset384(private_sha512_hasher_t *ctx) +{ + memcpy(&ctx->sha_H[0], &sha384_hashInit[0], sizeof(ctx->sha_H)); + ctx->sha_blocks = 0; + ctx->sha_blocksMSB = 0; + ctx->sha_bufCnt = 0; +} + +/** + * Implementation of hasher_t.reset for SHA512 + */ +static void reset512(private_sha512_hasher_t *ctx) +{ + memcpy(&ctx->sha_H[0], &sha512_hashInit[0], sizeof(ctx->sha_H)); + ctx->sha_blocks = 0; + ctx->sha_blocksMSB = 0; + ctx->sha_bufCnt = 0; +} + +/** + * Implementation of hasher_t.get_state for SHA256 + */ +static chunk_t get_state256(private_sha256_hasher_t *ctx) +{ + chunk_t chunk; + chunk.ptr = (u_char*)&ctx->sha_H[0]; + chunk.len = HASH_SIZE_SHA256; + return chunk; +} + +/** + * Implementation of hasher_t.get_state for SHA384 + */ +static chunk_t get_state384(private_sha512_hasher_t *ctx) +{ + chunk_t chunk; + chunk.ptr = (u_char*)&ctx->sha_H[0]; + chunk.len = HASH_SIZE_SHA384; + return chunk; +} +/** + * Implementation of hasher_t.get_state for SHA512 + */ +static chunk_t get_state512(private_sha512_hasher_t *ctx) +{ + chunk_t chunk; + chunk.ptr = (u_char*)&ctx->sha_H[0]; + chunk.len = HASH_SIZE_SHA512; + return chunk; +} + +/** + * Implementation of hasher_t.destroy. + */ +static void destroy(sha2_hasher_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +sha2_hasher_t *sha2_hasher_create(hash_algorithm_t algorithm) +{ + sha2_hasher_t *this; + + switch (algorithm) + { + case HASH_SHA256: + this = (sha2_hasher_t*)malloc_thing(private_sha256_hasher_t); + this->hasher_interface.reset = (void(*)(hasher_t*))reset256; + this->hasher_interface.get_state = (chunk_t(*)(hasher_t*))get_state256; + this->hasher_interface.get_hash_size = (size_t(*)(hasher_t*))get_hash_size256; + this->hasher_interface.get_hash = (void(*)(hasher_t*,chunk_t,u_int8_t*))get_hash256; + this->hasher_interface.allocate_hash = (void(*)(hasher_t*,chunk_t,chunk_t*))allocate_hash256; + break; + case HASH_SHA384: + /* uses SHA512 data structure */ + this = (sha2_hasher_t*)malloc_thing(private_sha512_hasher_t); + this->hasher_interface.reset = (void(*)(hasher_t*))reset384; + this->hasher_interface.get_state = (chunk_t(*)(hasher_t*))get_state384; + this->hasher_interface.get_hash_size = (size_t(*)(hasher_t*))get_hash_size384; + this->hasher_interface.get_hash = (void(*)(hasher_t*,chunk_t,u_int8_t*))get_hash384; + this->hasher_interface.allocate_hash = (void(*)(hasher_t*,chunk_t,chunk_t*))allocate_hash384; + break; + case HASH_SHA512: + this = (sha2_hasher_t*)malloc_thing(private_sha512_hasher_t); + this->hasher_interface.reset = (void(*)(hasher_t*))reset512; + this->hasher_interface.get_state = (chunk_t(*)(hasher_t*))get_state512; + this->hasher_interface.get_hash_size = (size_t(*)(hasher_t*))get_hash_size512; + this->hasher_interface.get_hash = (void(*)(hasher_t*,chunk_t,u_int8_t*))get_hash512; + this->hasher_interface.allocate_hash = (void(*)(hasher_t*,chunk_t,chunk_t*))allocate_hash512; + break; + default: + return NULL; + } + this->hasher_interface.destroy = (void(*)(hasher_t*))destroy; + + /* initialize */ + this->hasher_interface.reset(&this->hasher_interface); + + return this; +} diff --git a/src/libstrongswan/crypto/hashers/sha2_hasher.h b/src/libstrongswan/crypto/hashers/sha2_hasher.h new file mode 100644 index 000000000..91e82fedb --- /dev/null +++ b/src/libstrongswan/crypto/hashers/sha2_hasher.h @@ -0,0 +1,62 @@ +/** + * @file sha2_hasher.h + * + * @brief Interface of sha2_hasher_t + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SHA2_HASHER_H_ +#define SHA2_HASHER_H_ + +typedef struct sha2_hasher_t sha2_hasher_t; + +#include + +/** + * @brief Implementation of hasher_t interface using the SHA2 algorithms. + * + * SHA2 is an other name for the SHA-256, SHA-384 and SHA-512 variants of + * the SHA hash algorithm. + * + * @b Constructors: + * - hasher_create() using HASH_SHA256, HASH_SHA384 or HASH_SHA512 as algorithm + * - sha2_hasher_create() + * + * @see hasher_t + * + * @ingroup hashers + */ +struct sha2_hasher_t { + + /** + * Generic hasher_t interface for this hasher. + */ + hasher_t hasher_interface; +}; + +/** + * @brief Creates a new sha2_hasher_t. + * + * @param algorithm HASH_SHA256, HASH_SHA384 or HASH_SHA512 + * @return sha2_hasher_t object + * + * @ingroup hashers + */ +sha2_hasher_t *sha2_hasher_create(hash_algorithm_t algorithm); + +#endif /* SHA2_HASHER_H_ */ diff --git a/src/libstrongswan/crypto/hmac.c b/src/libstrongswan/crypto/hmac.c new file mode 100644 index 000000000..df4f90bc8 --- /dev/null +++ b/src/libstrongswan/crypto/hmac.c @@ -0,0 +1,215 @@ +/** + * @file hmac.c + * + * @brief Implementation of hmac_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General hmac License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * 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 General hmac License + * for more details. + */ + +#include + +#include "hmac.h" + + +typedef struct private_hmac_t private_hmac_t; + +/** + * Private data of a hmac_t object. + * + * The variable names are the same as in the RFC. + */ +struct private_hmac_t { + /** + * Public hmac_t interface. + */ + hmac_t hmac; + + /** + * Block size, as in RFC. + */ + u_int8_t b; + + /** + * Hash function. + */ + hasher_t *h; + + /** + * Previously xor'ed key using opad. + */ + chunk_t opaded_key; + + /** + * Previously xor'ed key using ipad. + */ + chunk_t ipaded_key; +}; + +/** + * Implementation of hmac_t.get_mac. + */ +static void get_mac(private_hmac_t *this, chunk_t data, u_int8_t *out) +{ + /* H(K XOR opad, H(K XOR ipad, text)) + * + * if out is NULL, we append text to the inner hash. + * else, we complete the inner and do the outer. + * + */ + + u_int8_t buffer[this->h->get_hash_size(this->h)]; + chunk_t inner; + + if (out == NULL) + { + /* append data to inner */ + this->h->get_hash(this->h, data, NULL); + } + else + { + /* append and do outer hash */ + inner.ptr = buffer; + inner.len = this->h->get_hash_size(this->h); + + /* complete inner */ + this->h->get_hash(this->h, data, buffer); + + /* do outer */ + this->h->get_hash(this->h, this->opaded_key, NULL); + this->h->get_hash(this->h, inner, out); + + /* reinit for next call */ + this->h->get_hash(this->h, this->ipaded_key, NULL); + } +} + +/** + * Implementation of hmac_t.allocate_mac. + */ +static void allocate_mac(private_hmac_t *this, chunk_t data, chunk_t *out) +{ + /* allocate space and use get_mac */ + if (out == NULL) + { + /* append mode */ + this->hmac.get_mac(&(this->hmac), data, NULL); + } + else + { + out->len = this->h->get_hash_size(this->h); + out->ptr = malloc(out->len); + this->hmac.get_mac(&(this->hmac), data, out->ptr); + } +} + +/** + * Implementation of hmac_t.get_block_size. + */ +static size_t get_block_size(private_hmac_t *this) +{ + return this->h->get_hash_size(this->h); +} + +/** + * Implementation of hmac_t.set_key. + */ +static void set_key(private_hmac_t *this, chunk_t key) +{ + int i; + u_int8_t buffer[this->b]; + + memset(buffer, 0, this->b); + + if (key.len > this->b) + { + /* if key is too long, it will be hashed */ + this->h->get_hash(this->h, key, buffer); + } + else + { + /* if not, just copy it in our pre-padded k */ + memcpy(buffer, key.ptr, key.len); + } + + /* apply ipad and opad to key */ + for (i = 0; i < this->b; i++) + { + this->ipaded_key.ptr[i] = buffer[i] ^ 0x36; + this->opaded_key.ptr[i] = buffer[i] ^ 0x5C; + } + + /* begin hashing of inner pad */ + this->h->reset(this->h); + this->h->get_hash(this->h, this->ipaded_key, NULL); +} + +/** + * Implementation of hmac_t.destroy. + */ +static void destroy(private_hmac_t *this) +{ + this->h->destroy(this->h); + free(this->opaded_key.ptr); + free(this->ipaded_key.ptr); + free(this); +} + +/* + * Described in header + */ +hmac_t *hmac_create(hash_algorithm_t hash_algorithm) +{ + private_hmac_t *this; + + this = malloc_thing(private_hmac_t); + + /* set hmac_t methods */ + this->hmac.get_mac = (void (*)(hmac_t *,chunk_t,u_int8_t*))get_mac; + this->hmac.allocate_mac = (void (*)(hmac_t *,chunk_t,chunk_t*))allocate_mac; + this->hmac.get_block_size = (size_t (*)(hmac_t *))get_block_size; + this->hmac.set_key = (void (*)(hmac_t *,chunk_t))set_key; + this->hmac.destroy = (void (*)(hmac_t *))destroy; + + /* set b, according to hasher */ + switch (hash_algorithm) + { + case HASH_SHA1: + case HASH_MD5: + case HASH_SHA256: + this->b = 64; + break; + case HASH_SHA384: + case HASH_SHA512: + this->b = 128; + break; + default: + free(this); + return NULL; + } + + /* build the hasher */ + this->h = hasher_create(hash_algorithm); + + /* build ipad and opad */ + this->opaded_key.ptr = malloc(this->b); + this->opaded_key.len = this->b; + + this->ipaded_key.ptr = malloc(this->b); + this->ipaded_key.len = this->b; + + return &(this->hmac); +} diff --git a/src/libstrongswan/crypto/hmac.h b/src/libstrongswan/crypto/hmac.h new file mode 100644 index 000000000..d320bc5aa --- /dev/null +++ b/src/libstrongswan/crypto/hmac.h @@ -0,0 +1,117 @@ +/** + * @file hmac.h + * + * @brief Interface of hmac_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef HMAC_H_ +#define HMAC_H_ + +typedef struct hmac_t hmac_t; + +#include + +/** + * @brief Message authentication using hash functions. + * + * This class implements the message authenticaion algorithm + * described in RFC2104. It uses a hash function, wich must + * be implemented as a hasher_t class. + * + * See http://www.faqs.org/rfcs/rfc2104.html for RFC. + * @see + * - hasher_t + * - prf_hmac_t + * + * @b Constructors: + * - hmac_create() + * + * @ingroup transforms + */ +struct hmac_t { + /** + * @brief Generate message authentication code. + * + * If buffer is NULL, no result is given back. A next call will + * append the data to already supplied data. If buffer is not NULL, + * the mac of all apended data is calculated, returned and the + * state of the hmac_t is reseted. + * + * @param this calling object + * @param data chunk of data to authenticate + * @param[out] buffer pointer where the generated bytes will be written + */ + void (*get_mac) (hmac_t *this, chunk_t data, u_int8_t *buffer); + + /** + * @brief Generates message authentication code and + * allocate space for them. + * + * If chunk is NULL, no result is given back. A next call will + * append the data to already supplied. If chunk is not NULL, + * the mac of all apended data is calculated, returned and the + * state of the hmac_t reset; + * + * @param this calling object + * @param data chunk of data to authenticate + * @param[out] chunk chunk which will hold generated bytes + */ + void (*allocate_mac) (hmac_t *this, chunk_t data, chunk_t *chunk); + + /** + * @brief Get the block size of this hmac_t object. + * + * @param this calling object + * @return block size in bytes + */ + size_t (*get_block_size) (hmac_t *this); + + /** + * @brief Set the key for this hmac_t object. + * + * Any key length is accepted. + * + * @param this calling object + * @param key key to set + */ + void (*set_key) (hmac_t *this, chunk_t key); + + /** + * @brief Destroys a hmac_t object. + * + * @param this calling object + */ + void (*destroy) (hmac_t *this); +}; + +/** + * @brief Creates a new hmac_t object. + * + * Creates a hasher_t object internally. + * + * @param hash_algorithm hash algorithm to use + * @return + * - hmac_t object + * - NULL if hash algorithm is not supported + * + * @ingroup transforms + */ +hmac_t *hmac_create(hash_algorithm_t hash_algorithm); + +#endif /*HMAC_H_*/ diff --git a/src/libstrongswan/crypto/ocsp.c b/src/libstrongswan/crypto/ocsp.c new file mode 100644 index 000000000..471996c8e --- /dev/null +++ b/src/libstrongswan/crypto/ocsp.c @@ -0,0 +1,924 @@ +/** + * @file ocsp.c + * + * @brief Implementation of ocsp_t. + * + */ + +/* Support of the Online Certificate Status Protocol (OCSP) + * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen + * Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hashers/hasher.h" +#include "rsa/rsa_public_key.h" +#include "certinfo.h" +#include "x509.h" +#include "ocsp.h" + +#define NONCE_LENGTH 16 + +typedef struct private_ocsp_t private_ocsp_t; + +/** + * Private data of a ocsp_t object. + */ +struct private_ocsp_t { + /** + * Public interface for this ocsp object. + */ + ocsp_t public; + + /** + * CA certificate. + */ + x509_t *cacert; + + /** + * Requestor certificate + */ + x509_t *requestor_cert; + + /** + * Linked list of ocsp uris + */ + linked_list_t *uris; + + /** + * Linked list of certinfos to be requested + */ + linked_list_t *certinfos; + + /** + * Nonce required for ocsp request and response + */ + chunk_t nonce; + + /** + * SHA-1 hash over issuer distinguished name + */ + chunk_t authNameID; + + /** + * SHA-1 hash over issuer public key + */ + chunk_t authKeyID; +}; + +ENUM(response_status_names, STATUS_SUCCESSFUL, STATUS_UNAUTHORIZED, + "successful", + "malformed request", + "internal error", + "try later", + "signature required", + "unauthorized" +); + +/* response container */ +typedef struct response_t response_t; + +struct response_t { + chunk_t chunk; + chunk_t tbs; + identification_t *responder_id_name; + chunk_t responder_id_key; + time_t produced_at; + chunk_t responses; + chunk_t nonce; + int algorithm; + chunk_t signature; + x509_t *responder_cert; + + /** + * @brief Destroys the response_t object + * + * @param this response_t to destroy + */ + void (*destroy) (response_t *this); +}; + +/** + * Implements response_t.destroy. + */ +static void response_destroy(response_t *this) +{ + DESTROY_IF(this->responder_id_name); + DESTROY_IF(this->responder_cert); + free(this->chunk.ptr); + free(this); +} + +/** + * Creates a response_t object + */ +static response_t* response_create_from_chunk(chunk_t chunk) +{ + response_t *this = malloc_thing(response_t); + + this->chunk = chunk; + this->tbs = chunk_empty; + this->responder_id_name = NULL; + this->responder_id_key = chunk_empty; + this->produced_at = UNDEFINED_TIME; + this->responses = chunk_empty; + this->nonce = chunk_empty; + this->algorithm = OID_UNKNOWN; + this->signature = chunk_empty; + this->responder_cert = NULL; + + this->destroy = (void (*) (response_t*))response_destroy; + + return this; +} + +/* some OCSP specific prefabricated ASN.1 constants */ + +static u_char ASN1_nonce_oid_str[] = { + 0x06, 0x09, + 0x2B, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02 +}; + +static u_char ASN1_response_oid_str[] = { + 0x06, 0x09, + 0x2B, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04 +}; + +static u_char ASN1_response_content_str[] = { + 0x04, 0x0D, + 0x30, 0x0B, + 0x06, 0x09, + 0x2B, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 +}; + +static const chunk_t ASN1_nonce_oid = chunk_from_buf(ASN1_nonce_oid_str); +static const chunk_t ASN1_response_oid = chunk_from_buf(ASN1_response_oid_str); +static const chunk_t ASN1_response_content = chunk_from_buf(ASN1_response_content_str); + +/* asn.1 definitions for parsing */ + +static const asn1Object_t ocspResponseObjects[] = { + { 0, "OCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "responseStatus", ASN1_ENUMERATED, ASN1_BODY }, /* 1 */ + { 1, "responseBytesContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 2 */ + { 2, "responseBytes", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ + { 3, "responseType", ASN1_OID, ASN1_BODY }, /* 4 */ + { 3, "response", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */ +}; + +#define OCSP_RESPONSE_STATUS 1 +#define OCSP_RESPONSE_TYPE 4 +#define OCSP_RESPONSE 5 +#define OCSP_RESPONSE_ROOF 7 + +static const asn1Object_t basicResponseObjects[] = { + { 0, "BasicOCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "tbsResponseData", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE | + ASN1_DEF }, /* 2 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 2, "responderIdContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */ + { 3, "responderIdByName", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ + { 2, "responderIdContext", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 7 */ + { 3, "responderIdByKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 8 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 2, "producedAt", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */ + { 2, "responses", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ + { 2, "responseExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 12 */ + { 3, "responseExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 13 */ + { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 14 */ + { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 15 */ + { 5, "critical", ASN1_BOOLEAN, ASN1_BODY | + ASN1_DEF }, /* 16 */ + { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 17 */ + { 4, "end loop", ASN1_EOC, ASN1_END }, /* 18 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 20 */ + { 1, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 21 */ + { 1, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 22 */ + { 2, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 23 */ + { 3, "certificate", ASN1_SEQUENCE, ASN1_RAW }, /* 24 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 26 */ +}; + +#define BASIC_RESPONSE_TBS_DATA 1 +#define BASIC_RESPONSE_VERSION 3 +#define BASIC_RESPONSE_ID_BY_NAME 5 +#define BASIC_RESPONSE_ID_BY_KEY 8 +#define BASIC_RESPONSE_PRODUCED_AT 10 +#define BASIC_RESPONSE_RESPONSES 11 +#define BASIC_RESPONSE_EXT_ID 15 +#define BASIC_RESPONSE_CRITICAL 16 +#define BASIC_RESPONSE_EXT_VALUE 17 +#define BASIC_RESPONSE_ALGORITHM 20 +#define BASIC_RESPONSE_SIGNATURE 21 +#define BASIC_RESPONSE_CERTIFICATE 24 +#define BASIC_RESPONSE_ROOF 27 + +static const asn1Object_t responsesObjects[] = { + { 0, "responses", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "singleResponse", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */ +}; + +#define RESPONSES_SINGLE_RESPONSE 1 +#define RESPONSES_ROOF 3 + +static const asn1Object_t singleResponseObjects[] = { + { 0, "singleResponse", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ + { 1, "certID", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 2, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ + { 2, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */ + { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "certStatusGood", ASN1_CONTEXT_S_0, ASN1_OPT }, /* 6 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 1, "certStatusRevoked", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 8 */ + { 2, "revocationTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 9 */ + { 2, "revocationReason", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 10 */ + { 3, "crlReason", ASN1_ENUMERATED, ASN1_BODY }, /* 11 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 13 */ + { 1, "certStatusUnknown", ASN1_CONTEXT_S_2, ASN1_OPT }, /* 14 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 15 */ + { 1, "thisUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */ + { 1, "nextUpdateContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 17 */ + { 2, "nextUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ + { 1, "singleExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 20 */ + { 2, "singleExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 21 */ + { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ + { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 23 */ + { 4, "critical", ASN1_BOOLEAN, ASN1_BODY | + ASN1_DEF }, /* 24 */ + { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 25 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 26 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 27 */ +}; + +#define SINGLE_RESPONSE_ALGORITHM 2 +#define SINGLE_RESPONSE_ISSUER_NAME_HASH 3 +#define SINGLE_RESPONSE_ISSUER_KEY_HASH 4 +#define SINGLE_RESPONSE_SERIAL_NUMBER 5 +#define SINGLE_RESPONSE_CERT_STATUS_GOOD 6 +#define SINGLE_RESPONSE_CERT_STATUS_REVOKED 8 +#define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME 9 +#define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON 11 +#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN 14 +#define SINGLE_RESPONSE_THIS_UPDATE 16 +#define SINGLE_RESPONSE_NEXT_UPDATE 18 +#define SINGLE_RESPONSE_EXT_ID 23 +#define SINGLE_RESPONSE_CRITICAL 24 +#define SINGLE_RESPONSE_EXT_VALUE 25 +#define SINGLE_RESPONSE_ROOF 28 + +/** + * build requestorName (into TBSRequest) + */ +static chunk_t build_requestor_name(private_ocsp_t *this) +{ + identification_t *requestor_name = this->requestor_cert->get_subject(this->requestor_cert); + + return asn1_wrap(ASN1_CONTEXT_C_1, "m", + asn1_simple_object(ASN1_CONTEXT_C_4, + requestor_name->get_encoding(requestor_name))); +} + +/** + * build request (into requestList) + * no singleRequestExtensions used + */ +static chunk_t build_request(private_ocsp_t *this, certinfo_t *certinfo) +{ + chunk_t serialNumber = certinfo->get_serialNumber(certinfo); + + chunk_t reqCert = asn1_wrap(ASN1_SEQUENCE, "cmmm", + ASN1_sha1_id, + asn1_simple_object(ASN1_OCTET_STRING, this->authNameID), + asn1_simple_object(ASN1_OCTET_STRING, this->authKeyID), + asn1_simple_object(ASN1_INTEGER, serialNumber)); + + return asn1_wrap(ASN1_SEQUENCE, "m", reqCert); +} + +/** + * build requestList (into TBSRequest) + */ +static chunk_t build_request_list(private_ocsp_t *this) +{ + chunk_t requestList; + size_t datalen = 0; + linked_list_t *request_list = linked_list_create(); + + { + iterator_t *iterator = this->certinfos->create_iterator(this->certinfos, TRUE); + certinfo_t *certinfo; + + while (iterator->iterate(iterator, (void**)&certinfo)) + { + chunk_t *request = malloc_thing(chunk_t); + + *request = build_request(this, certinfo); + request_list->insert_last(request_list, (void*)request); + datalen += request->len; + } + iterator->destroy(iterator); + } + { + iterator_t *iterator = request_list->create_iterator(request_list, TRUE); + chunk_t *request; + + u_char *pos = build_asn1_object(&requestList, ASN1_SEQUENCE, datalen); + + while (iterator->iterate(iterator, (void**)&request)) + { + memcpy(pos, request->ptr, request->len); + pos += request->len; + free(request->ptr); + free(request); + } + iterator->destroy(iterator); + request_list->destroy(request_list); + } + return requestList; +} + +/** + * build nonce extension (into requestExtensions) + */ +static chunk_t build_nonce_extension(private_ocsp_t *this) +{ + randomizer_t *randomizer = randomizer_create(); + + /* generate a random nonce */ + randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LENGTH, &this->nonce); + randomizer->destroy(randomizer); + + return asn1_wrap(ASN1_SEQUENCE, "cm", + ASN1_nonce_oid, + asn1_simple_object(ASN1_OCTET_STRING, this->nonce)); +} + +/** + * build requestExtensions (into TBSRequest) + */ +static chunk_t build_request_ext(private_ocsp_t *this) +{ + return asn1_wrap(ASN1_CONTEXT_C_2, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + build_nonce_extension(this), + asn1_wrap(ASN1_SEQUENCE, "cc", + ASN1_response_oid, + ASN1_response_content + ) + ) + ); +} + +/** + * build TBSRequest (into OCSPRequest) + */ +static chunk_t build_tbs_request(private_ocsp_t *this, bool has_requestor_cert) +{ + /* version is skipped since the default is ok */ + return asn1_wrap(ASN1_SEQUENCE, "mmm", + (has_requestor_cert)? build_requestor_name(this): chunk_empty, + build_request_list(this), + build_request_ext(this)); +} + +/** + * build signature into ocsp request + * gets built only if a request cert with a corresponding private key is found + */ +static chunk_t build_signature(private_ocsp_t *this, chunk_t tbsRequest) +{ + /* TODO */ + return chunk_empty; +} + +/** + * assembles an ocsp request and sets the nonce field in private_ocsp_t to the sent nonce + */ +static chunk_t ocsp_build_request(private_ocsp_t *this) +{ + bool has_requestor_cert; + chunk_t keyid = this->cacert->get_keyid(this->cacert); + chunk_t tbsRequest, signature; + + DBG2("assembling ocsp request"); + DBG2("issuer: '%D'", this->cacert->get_subject(this->cacert)); + DBG2("keyid: %#B", &keyid); + + /* looks for requestor cert and matching private key */ + has_requestor_cert = FALSE; + + /* TODO has_requestor_cert = get_ocsp_requestor_cert(location); */ + + /* build content */ + tbsRequest = build_tbs_request(this, has_requestor_cert); + + /* sign tbsReuqest */ + signature = (has_requestor_cert)? build_signature(this, tbsRequest): chunk_empty; + + return asn1_wrap(ASN1_SEQUENCE, "mm", + tbsRequest, + signature); + + return signature; +} + +/** + * parse a basic OCSP response + */ +static bool ocsp_parse_basic_response(chunk_t blob, int level0, response_t *res) +{ + u_int level, version; + u_int extn_oid = OID_UNKNOWN; + asn1_ctx_t ctx; + bool critical; + chunk_t object; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < BASIC_RESPONSE_ROOF) + { + if (!extract_object(basicResponseObjects, &objectID, &object, &level, &ctx)) + { + return FALSE; + } + + switch (objectID) + { + case BASIC_RESPONSE_TBS_DATA: + res->tbs = object; + break; + case BASIC_RESPONSE_VERSION: + version = (object.len)? (1 + (u_int)*object.ptr) : 1; + if (version != OCSP_BASIC_RESPONSE_VERSION) + { + DBG1("wrong ocsp basic response version (version= %i)", version); + return FALSE; + } + break; + case BASIC_RESPONSE_ID_BY_NAME: + res->responder_id_name = identification_create_from_encoding(ID_DER_ASN1_DN, object); + DBG2(" '%D'", res->responder_id_name); + break; + case BASIC_RESPONSE_ID_BY_KEY: + res->responder_id_key = object; + break; + case BASIC_RESPONSE_PRODUCED_AT: + res->produced_at = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case BASIC_RESPONSE_RESPONSES: + res->responses = object; + break; + case BASIC_RESPONSE_EXT_ID: + extn_oid = known_oid(object); + break; + case BASIC_RESPONSE_CRITICAL: + critical = object.len && *object.ptr; + DBG2(" %s", critical? "TRUE" : "FALSE"); + break; + case BASIC_RESPONSE_EXT_VALUE: + if (extn_oid == OID_NONCE) + res->nonce = object; + break; + case BASIC_RESPONSE_ALGORITHM: + res->algorithm = parse_algorithmIdentifier(object, level+1, NULL); + break; + case BASIC_RESPONSE_SIGNATURE: + res->signature = object; + break; + case BASIC_RESPONSE_CERTIFICATE: + { + chunk_t blob = chunk_clone(object); + + res->responder_cert = x509_create_from_chunk(blob, level+1); + } + break; + } + objectID++; + } + return TRUE; +} + +/** + * parse an ocsp response and return the result as a response_t struct + */ +static response_status ocsp_parse_response(response_t *res) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + response_status rStatus = STATUS_INTERNALERROR; + u_int ocspResponseType = OID_UNKNOWN; + + asn1_init(&ctx, res->chunk, 0, FALSE, FALSE); + + while (objectID < OCSP_RESPONSE_ROOF) + { + if (!extract_object(ocspResponseObjects, &objectID, &object, &level, &ctx)) + { + return STATUS_INTERNALERROR; + } + + switch (objectID) + { + case OCSP_RESPONSE_STATUS: + rStatus = (response_status) *object.ptr; + DBG2(" '%N'", response_status_names, rStatus); + + switch (rStatus) + { + case STATUS_SUCCESSFUL: + break; + case STATUS_MALFORMEDREQUEST: + case STATUS_INTERNALERROR: + case STATUS_TRYLATER: + case STATUS_SIGREQUIRED: + case STATUS_UNAUTHORIZED: + DBG1("unsuccessful ocsp response: server said '%N'", + response_status_names, rStatus); + return rStatus; + default: + return STATUS_INTERNALERROR; + } + break; + case OCSP_RESPONSE_TYPE: + ocspResponseType = known_oid(object); + break; + case OCSP_RESPONSE: + { + switch (ocspResponseType) + { + case OID_BASIC: + if (!ocsp_parse_basic_response(object, level+1, res)) + { + return STATUS_INTERNALERROR; + } + break; + default: + DBG1("ocsp response is not of type BASIC"); + DBG1("ocsp response OID: %#B", &object); + return STATUS_INTERNALERROR; + } + } + break; + } + objectID++; + } + return rStatus; +} + +/** + * Check if the OCSP response has a valid signature + */ +static bool ocsp_valid_response(response_t *res, x509_t *ocsp_cert) +{ + rsa_public_key_t *public_key; + time_t until = UNDEFINED_TIME; + err_t ugh; + + DBG2("verifying ocsp response signature:"); + DBG2("signer: '%D'", ocsp_cert->get_subject(ocsp_cert)); + DBG2("issuer: '%D'", ocsp_cert->get_issuer(ocsp_cert)); + + ugh = ocsp_cert->is_valid(ocsp_cert, &until); + if (ugh != NULL) + { + DBG1("ocsp signer certificate %s", ugh); + return FALSE; + } + public_key = ocsp_cert->get_public_key(ocsp_cert); + + return public_key->verify_emsa_pkcs1_signature(public_key, res->tbs, res->signature) == SUCCESS; +} + +/** + * parse a single OCSP response + */ +static bool ocsp_parse_single_response(private_ocsp_t *this, chunk_t blob, int level0) +{ + u_int level, extn_oid; + asn1_ctx_t ctx; + bool critical; + chunk_t object; + int objectID = 0; + + certinfo_t *certinfo = NULL; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < SINGLE_RESPONSE_ROOF) + { + if (!extract_object(singleResponseObjects, &objectID, &object, &level, &ctx)) + { + return FALSE; + } + + switch (objectID) + { + case SINGLE_RESPONSE_ALGORITHM: + if (parse_algorithmIdentifier(object, level+1, NULL) != OID_SHA1) + { + DBG1("only sha-1 hash supported in ocsp single response"); + return FALSE; + } + break; + case SINGLE_RESPONSE_ISSUER_NAME_HASH: + if (!chunk_equals(object, this->authNameID)) + { + DBG1("ocsp single response has wrong issuer name hash"); + return FALSE; + } + break; + case SINGLE_RESPONSE_ISSUER_KEY_HASH: + if (!chunk_equals(object, this->authKeyID)) + { + DBG1("ocsp single response has wrong issuer key hash"); + return FALSE; + } + break; + case SINGLE_RESPONSE_SERIAL_NUMBER: + { + iterator_t *iterator = this->certinfos->create_iterator(this->certinfos, TRUE); + certinfo_t *current_certinfo; + + while (iterator->iterate(iterator, (void**)¤t_certinfo)) + { + if (chunk_equals(object, current_certinfo->get_serialNumber(current_certinfo))) + { + certinfo = current_certinfo; + } + } + iterator->destroy(iterator); + if (certinfo == NULL) + { + DBG1("unrequested serial number in ocsp single response"); + return FALSE; + } + } + break; + case SINGLE_RESPONSE_CERT_STATUS_GOOD: + certinfo->set_status(certinfo, CERT_GOOD); + break; + case SINGLE_RESPONSE_CERT_STATUS_REVOKED: + certinfo->set_status(certinfo, CERT_REVOKED); + break; + case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME: + certinfo->set_revocationTime(certinfo, + asn1totime(&object, ASN1_GENERALIZEDTIME)); + break; + case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON: + certinfo->set_revocationReason(certinfo, + (object.len == 1) ? *object.ptr : REASON_UNSPECIFIED); + break; + case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN: + certinfo->set_status(certinfo, CERT_UNKNOWN); + break; + case SINGLE_RESPONSE_THIS_UPDATE: + certinfo->set_thisUpdate(certinfo, + asn1totime(&object, ASN1_GENERALIZEDTIME)); + break; + case SINGLE_RESPONSE_NEXT_UPDATE: + certinfo->set_nextUpdate(certinfo, + asn1totime(&object, ASN1_GENERALIZEDTIME)); + break; + case SINGLE_RESPONSE_EXT_ID: + extn_oid = known_oid(object); + break; + case SINGLE_RESPONSE_CRITICAL: + critical = object.len && *object.ptr; + DBG2(" %s", critical ? "TRUE" : "FALSE"); + case SINGLE_RESPONSE_EXT_VALUE: + break; + } + objectID++; + } + return TRUE; +} + +/** + * verify and process ocsp response and update the ocsp cache + */ +static void ocsp_process_response(private_ocsp_t *this, response_t *res, credential_store_t *credentials) +{ + x509_t *ocsp_cert = NULL; + + /* parse the ocsp response without looking at the single responses yet */ + response_status status = ocsp_parse_response(res); + + if (status != STATUS_SUCCESSFUL) + { + DBG1("error in ocsp response"); + return; + } + + /* check if there was a nonce in the request */ + if (this->nonce.ptr != NULL && res->nonce.ptr == NULL) + { + DBG1("ocsp response contains no nonce, replay attack possible"); + } + + /* check if the nonces are identical */ + if (res->nonce.ptr != NULL && !chunk_equals(res->nonce, this->nonce)) + { + DBG1("invalid nonce in ocsp response"); + return; + } + + /* check if we received a trusted responder certificate */ + if (res->responder_cert) + { + if (res->responder_cert->is_ocsp_signer(res->responder_cert)) + { + DBG2("received certificate is ocsp signer"); + if (credentials->is_trusted(credentials, res->responder_cert)) + { + DBG1("received ocsp signer certificate is trusted"); + ocsp_cert = credentials->add_auth_certificate(credentials, + res->responder_cert, AUTH_OCSP); + res->responder_cert = NULL; + } + else + { + DBG1("received ocsp signer certificate is not trusted - rejected"); + } + } + else + { + DBG1("received certificate is no ocsp signer - rejected"); + } + } + + /* if we didn't receive a trusted responder cert, search the credential store */ + if (ocsp_cert == NULL) + { + ocsp_cert = credentials->get_auth_certificate(credentials, + AUTH_OCSP|AUTH_CA, res->responder_id_name); + if (ocsp_cert == NULL) + { + DBG1("no ocsp signer certificate found"); + return; + } + } + + /* check the response signature */ + if (!ocsp_valid_response(res, ocsp_cert)) + { + DBG1("ocsp response signature is invalid"); + return; + } + DBG2("ocsp response signature is valid"); + + /* now parse the single responses one at a time */ + { + u_int level; + asn1_ctx_t ctx; + chunk_t object; + int objectID = 0; + + asn1_init(&ctx, res->responses, 0, FALSE, FALSE); + + while (objectID < RESPONSES_ROOF) + { + if (!extract_object(responsesObjects, &objectID, &object, &level, &ctx)) + { + return; + } + if (objectID == RESPONSES_SINGLE_RESPONSE) + { + ocsp_parse_single_response(this, object, level+1); + } + objectID++; + } + } +} + +/** + * Implements ocsp_t.fetch. + */ +static void fetch(private_ocsp_t *this, certinfo_t *certinfo, credential_store_t *credentials) +{ + chunk_t request; + response_t *response = NULL; + + if (this->uris->get_count(this->uris) == 0) + { + return; + } + this->certinfos->insert_last(this->certinfos, (void*)certinfo); + + request = ocsp_build_request(this); + DBG3("ocsp request: %B", &request); + { + iterator_t *iterator = this->uris->create_iterator(this->uris, TRUE); + identification_t *uri; + + while (iterator->iterate(iterator, (void**)&uri)) + { + fetcher_t *fetcher; + char uri_string[BUF_LEN]; + chunk_t uri_chunk = uri->get_encoding(uri); + chunk_t response_chunk; + + snprintf(uri_string, BUF_LEN, "%.*s", uri_chunk.len, uri_chunk.ptr); + fetcher = fetcher_create(uri_string); + + response_chunk = fetcher->post(fetcher, "application/ocsp-request", request); + fetcher->destroy(fetcher); + if (response_chunk.ptr != NULL) + { + response = response_create_from_chunk(response_chunk); + break; + } + } + iterator->destroy(iterator); + } + free(request.ptr); + + if (response == NULL) + { + return; + } + DBG3("ocsp response: %B", &response->chunk); + ocsp_process_response(this, response, credentials); + response->destroy(response); +} + +/** + * Implements ocsp_t.destroy. + */ +static void destroy(private_ocsp_t *this) +{ + this->certinfos->destroy(this->certinfos); + free(this->authNameID.ptr); + free(this->nonce.ptr); + free(this); +} + +/* + * Described in header. + */ +ocsp_t *ocsp_create(x509_t *cacert, linked_list_t *uris) +{ + private_ocsp_t *this = malloc_thing(private_ocsp_t); + + /* initialize */ + this->cacert = cacert; + this->uris = uris; + this->certinfos = linked_list_create(); + this->nonce = chunk_empty; + this->authKeyID = cacert->get_subjectKeyID(cacert); + { + hasher_t *hasher = hasher_create(HASH_SHA1); + identification_t *issuer = cacert->get_subject(cacert); + + hasher->allocate_hash(hasher, issuer->get_encoding(issuer), + &this->authNameID); + hasher->destroy(hasher); + } + + /* public functions */ + this->public.fetch = (void (*) (ocsp_t*,certinfo_t*,credential_store_t*))fetch; + this->public.destroy = (void (*) (ocsp_t*))destroy; + + return &this->public; +} diff --git a/src/libstrongswan/crypto/ocsp.h b/src/libstrongswan/crypto/ocsp.h new file mode 100644 index 000000000..42059e1c6 --- /dev/null +++ b/src/libstrongswan/crypto/ocsp.h @@ -0,0 +1,86 @@ +/** + * @file ocsp.h + * + * @brief Interface of ocsp_t + * + */ + +/* Support of the Online Certificate Status Protocol (OCSP) Support + * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen + * Copyright (C) 2007 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ + +#ifndef OCSP_H_ +#define OCSP_H_ + +typedef struct ocsp_t ocsp_t; + +#include +#include + +#include "certinfo.h" + +/* constants */ +#define OCSP_BASIC_RESPONSE_VERSION 1 +#define OCSP_DEFAULT_VALID_TIME 120 /* validity of one-time response in seconds */ +#define OCSP_WARNING_INTERVAL 2 /* days */ + +/* OCSP response status */ +typedef enum { + STATUS_SUCCESSFUL = 0, + STATUS_MALFORMEDREQUEST = 1, + STATUS_INTERNALERROR = 2, + STATUS_TRYLATER = 3, + STATUS_SIGREQUIRED = 5, + STATUS_UNAUTHORIZED= 6 +} response_status; + +/** + * @brief Online Certficate Status Protocol (OCSP) + * + * @ingroup transforms + */ +struct ocsp_t { + + /** + * @brief Fetches the actual certificate status via OCSP + * + * @param uris linked list of ocsp uris + * @param certinfo certificate status info to be updated + * @param credentials credential store needed for trust path verification + */ + void (*fetch) (ocsp_t *this, certinfo_t *certinfo, credential_store_t *credentials); + + /** + * @brief Destroys the ocsp_t object. + * + * @param this ocsp object to destroy + */ + void (*destroy) (ocsp_t *this); + +}; + +/** + * @brief Create an ocsp_t object. + * + * @param cacert ca certificate + * @param uris linked list of ocsp uris + * @return created ocsp_t object + * + * @ingroup transforms + */ +ocsp_t *ocsp_create(x509_t *cacert, linked_list_t *uris); + +#endif /* OCSP_H_ */ diff --git a/src/libstrongswan/crypto/prf_plus.c b/src/libstrongswan/crypto/prf_plus.c new file mode 100644 index 000000000..6bd444b1f --- /dev/null +++ b/src/libstrongswan/crypto/prf_plus.c @@ -0,0 +1,156 @@ +/** + * @file prf_plus.c + * + * @brief Implementation of prf_plus_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "prf_plus.h" + +typedef struct private_prf_plus_t private_prf_plus_t; + +/** + * Private data of an prf_plus_t object. + * + */ +struct private_prf_plus_t { + /** + * Public interface of prf_plus_t. + */ + prf_plus_t public; + + /** + * PRF to use. + */ + prf_t *prf; + + /** + * Initial seed. + */ + chunk_t seed; + + /** + * Buffer to store current PRF result. + */ + chunk_t buffer; + + /** + * Already given out bytes in current buffer. + */ + size_t given_out; + + /** + * Octet which will be appended to the seed. + */ + u_int8_t appending_octet; +}; + +/** + * Implementation of prf_plus_t.get_bytes. + */ +static void get_bytes(private_prf_plus_t *this, size_t length, u_int8_t *buffer) +{ + chunk_t appending_chunk; + size_t bytes_in_round; + size_t total_bytes_written = 0; + + appending_chunk.ptr = &(this->appending_octet); + appending_chunk.len = 1; + + while (length > 0) + { /* still more to do... */ + if (this->buffer.len == this->given_out) + { /* no bytes left in buffer, get next*/ + this->prf->get_bytes(this->prf, this->buffer, NULL); + this->prf->get_bytes(this->prf, this->seed, NULL); + this->prf->get_bytes(this->prf, appending_chunk, this->buffer.ptr); + this->given_out = 0; + this->appending_octet++; + } + /* how many bytes can we write in this round ? */ + bytes_in_round = min(length, this->buffer.len - this->given_out); + /* copy bytes from buffer with offset */ + memcpy(buffer + total_bytes_written, this->buffer.ptr + this->given_out, bytes_in_round); + + length -= bytes_in_round; + this->given_out += bytes_in_round; + total_bytes_written += bytes_in_round; + } +} + +/** + * Implementation of prf_plus_t.allocate_bytes. + */ +static void allocate_bytes(private_prf_plus_t *this, size_t length, chunk_t *chunk) +{ + chunk->ptr = malloc(length); + chunk->len = length; + this->public.get_bytes(&(this->public), length, chunk->ptr); +} + +/** + * Implementation of prf_plus_t.destroy. + */ +static void destroy(private_prf_plus_t *this) +{ + free(this->buffer.ptr); + free(this->seed.ptr); + free(this); +} + +/* + * Description in header. + */ +prf_plus_t *prf_plus_create(prf_t *prf, chunk_t seed) +{ + private_prf_plus_t *this; + chunk_t appending_chunk; + + this = malloc_thing(private_prf_plus_t); + + /* set public methods */ + this->public.get_bytes = (void (*)(prf_plus_t *,size_t,u_int8_t*))get_bytes; + this->public.allocate_bytes = (void (*)(prf_plus_t *,size_t,chunk_t*))allocate_bytes; + this->public.destroy = (void (*)(prf_plus_t *))destroy; + + /* take over prf */ + this->prf = prf; + + /* allocate buffer for prf output */ + this->buffer.len = prf->get_block_size(prf); + this->buffer.ptr = malloc(this->buffer.len); + + this->appending_octet = 0x01; + + /* clone seed */ + this->seed.ptr = clalloc(seed.ptr, seed.len); + this->seed.len = seed.len; + + /* do the first run */ + appending_chunk.ptr = &(this->appending_octet); + appending_chunk.len = 1; + this->prf->get_bytes(this->prf, this->seed, NULL); + this->prf->get_bytes(this->prf, appending_chunk, this->buffer.ptr); + this->given_out = 0; + this->appending_octet++; + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/prf_plus.h b/src/libstrongswan/crypto/prf_plus.h new file mode 100644 index 000000000..90f9ce2eb --- /dev/null +++ b/src/libstrongswan/crypto/prf_plus.h @@ -0,0 +1,92 @@ +/** + * @file prf_plus.h + * + * @brief Interface for prf_plus.h. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PRF_PLUS_H_ +#define PRF_PLUS_H_ + +typedef struct prf_plus_t prf_plus_t; + +#include + +/** + * @brief Implementation of the prf+ function described in IKEv2 RFC. + * + * This class implements the prf+ algorithm. Internally it uses a pseudo random + * function, which implements the prf_t interface. + * + * See IKEv2 RFC 2.13. + * + * @b Constructors: + * - prf_plus_create() + * + * @ingroup transforms + */ +struct prf_plus_t { + /** + * @brief Get pseudo random bytes. + * + * Get the next few bytes of the prf+ output. Space + * must be allocated by the caller. + * + * @param this calling object + * @param length number of bytes to get + * @param[out] buffer pointer where the generated bytes will be written + */ + void (*get_bytes) (prf_plus_t *this, size_t length, u_int8_t *buffer); + + /** + * @brief Allocate pseudo random bytes. + * + * Get the next few bytes of the prf+ output. This function + * will allocate the required space. + * + * @param this calling object + * @param length number of bytes to get + * @param[out] chunk chunk which will hold generated bytes + */ + void (*allocate_bytes) (prf_plus_t *this, size_t length, chunk_t *chunk); + + /** + * @brief Destroys a prf_plus_t object. + * + * @param this calling object + */ + void (*destroy) (prf_plus_t *this); +}; + +/** + * @brief Creates a new prf_plus_t object. + * + * Seed will be cloned. prf will + * not be cloned, must be destroyed outside after + * prf_plus_t usage. + * + * @param prf prf object to use + * @param seed input seed for prf + * @return prf_plus_t object + * + * @ingroup transforms + */ +prf_plus_t *prf_plus_create(prf_t *prf, chunk_t seed); + +#endif /*PRF_PLUS_H_*/ diff --git a/src/libstrongswan/crypto/prfs/fips_prf.c b/src/libstrongswan/crypto/prfs/fips_prf.c new file mode 100644 index 000000000..0ab80b089 --- /dev/null +++ b/src/libstrongswan/crypto/prfs/fips_prf.c @@ -0,0 +1,258 @@ +/** + * @file fips_prf.c + * + * @brief Implementation for fips_prf_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "fips_prf.h" + +#include + +#include + +typedef struct private_fips_prf_t private_fips_prf_t; + +/** + * Private data of a fips_prf_t object. + */ +struct private_fips_prf_t { + /** + * Public fips_prf_t interface. + */ + fips_prf_t public; + + /** + * key of prf function, "b" long + */ + u_int8_t *key; + + /** + * size of "b" in bytes + */ + size_t b; + + /** + * G function, either SHA1 or DES + */ + void (*g)(u_int8_t t[], chunk_t c, u_int8_t res[]); +}; + +/** + * t used in G(), equals to initial SHA1 value + */ +static u_int8_t t[] = { + 0x67,0x45,0x23,0x01,0xEF,0xCD,0xAB,0x89,0x98,0xBA, + 0xDC,0xFE,0x10,0x32,0x54,0x76,0xC3,0xD2,0xE1,0xF0, +}; + +/** + * sum = (a + b) mod 2 ^ (length * 8) + */ +static void add_mod(size_t length, u_int8_t a[], u_int8_t b[], u_int8_t sum[]) +{ + int i, c = 0; + + for(i = length - 1; i >= 0; i--) + { + u_int32_t tmp; + + tmp = a[i] + b[i] + c; + sum[i] = 0xff & tmp; + c = tmp >> 8; + } +} + +/** + * calculate "chunk mod 2^(length*8)" and save it into buffer + */ +static void chunk_mod(size_t length, chunk_t chunk, u_int8_t buffer[]) +{ + if (chunk.len < length) + { + /* apply seed as least significant bits, others are zero */ + memset(buffer, 0, length - chunk.len); + memcpy(buffer + length - chunk.len, chunk.ptr, chunk.len); + } + else + { + /* use least significant bytes from seed, as we use mod 2^b */ + memcpy(buffer, chunk.ptr + chunk.len - length, length); + } +} + +/** + * Implementation of prf_t.get_bytes. + * + * Test vector: + * + * key: + * 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + * 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + * 0xeb, 0x5a, 0x38, 0xb6 + * + * seed: + * 0x00 + * + * result: + * 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + * 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + * 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + * 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + * 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + */ +static void get_bytes(private_fips_prf_t *this, chunk_t seed, u_int8_t w[]) +{ + int i; + u_int8_t xval[this->b]; + u_int8_t xseed[this->b]; + u_int8_t sum[this->b]; + u_int8_t *xkey = this->key; + u_int8_t one[this->b]; + chunk_t xval_chunk = chunk_from_buf(xval); + + memset(one, 0, this->b); + one[this->b - 1] = 0x01; + + /* 3.1 */ + chunk_mod(this->b, seed, xseed); + + /* 3.2 */ + for (i = 0; i < 2; i++) /* twice */ + { + /* a. XVAL = (XKEY + XSEED j) mod 2^b */ + add_mod(this->b, xkey, xseed, xval); + DBG3("XVAL %b", xval, this->b); + /* b. wi = G(t, XVAL ) */ + this->g(t, xval_chunk, &w[i * this->b]); + DBG3("w[%d] %b", i, &w[i * this->b], this->b); + /* c. XKEY = (1 + XKEY + wi) mod 2b */ + add_mod(this->b, xkey, &w[i * this->b], sum); + add_mod(this->b, sum, one, xkey); + DBG3("XKEY %b", xkey, this->b); + } + + /* 3.3 done already, mod q not used */ +} + +/** + * Implementation of prf_t.get_block_size. + */ +static size_t get_block_size(private_fips_prf_t *this) +{ + return 2 * this->b; +} +/** + * Implementation of prf_t.allocate_bytes. + */ +static void allocate_bytes(private_fips_prf_t *this, chunk_t seed, chunk_t *chunk) +{ + *chunk = chunk_alloc(get_block_size(this)); + get_bytes(this, seed, chunk->ptr); +} + +/** + * Implementation of prf_t.get_key_size. + */ +static size_t get_key_size(private_fips_prf_t *this) +{ + return this->b; +} + +/** + * Implementation of prf_t.set_key. + */ +static void set_key(private_fips_prf_t *this, chunk_t key) +{ + /* save key as "key mod 2^b" */ + chunk_mod(this->b, key, this->key); +} + +/** + * Implementation of the G() function based on SHA1 + */ +void g_sha1(u_int8_t t[], chunk_t c, u_int8_t res[]) +{ + hasher_t *hasher; + u_int8_t buf[64]; + chunk_t state_chunk; + u_int32_t *state, *iv, *hash; + + if (c.len < sizeof(buf)) + { + /* pad c with zeros */ + memset(buf, 0, sizeof(buf)); + memcpy(buf, c.ptr, c.len); + c.ptr = buf; + c.len = sizeof(buf); + } + else + { + /* not more than 512 bits can be G()-ed */ + c.len = sizeof(buf); + } + + /* our SHA1 hasher's state is 32-Bit integers in host order. We must + * convert them */ + hasher = hasher_create(HASH_SHA1); + state_chunk = hasher->get_state(hasher); + state = (u_int32_t*)state_chunk.ptr; + iv = (u_int32_t*)t; + hash = (u_int32_t*)res; + state[0] = htonl(iv[0]); + state[1] = htonl(iv[1]); + state[2] = htonl(iv[2]); + state[3] = htonl(iv[3]); + hasher->get_hash(hasher, c, NULL); + hash[0] = htonl(state[0]); + hash[1] = htonl(state[1]); + hash[2] = htonl(state[2]); + hash[3] = htonl(state[3]); + hash[4] = htonl(state[4]); + hasher->destroy(hasher); +} + +/** + * Implementation of prf_t.destroy. + */ +static void destroy(private_fips_prf_t *this) +{ + free(this->key); + free(this); +} + +/* + * Described in header. + */ +fips_prf_t *fips_prf_create(size_t b, void(*g)(u_int8_t[],chunk_t,u_int8_t[])) +{ + private_fips_prf_t *this = malloc_thing(private_fips_prf_t); + + this->public.prf_interface.get_bytes = (void (*) (prf_t *,chunk_t,u_int8_t*))get_bytes; + this->public.prf_interface.allocate_bytes = (void (*) (prf_t*,chunk_t,chunk_t*))allocate_bytes; + this->public.prf_interface.get_block_size = (size_t (*) (prf_t*))get_block_size; + this->public.prf_interface.get_key_size = (size_t (*) (prf_t*))get_key_size; + this->public.prf_interface.set_key = (void (*) (prf_t *,chunk_t))set_key; + this->public.prf_interface.destroy = (void (*) (prf_t *))destroy; + + this->g = g; + this->b = b; + this->key = malloc(b); + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/prfs/fips_prf.h b/src/libstrongswan/crypto/prfs/fips_prf.h new file mode 100644 index 000000000..283ee1f61 --- /dev/null +++ b/src/libstrongswan/crypto/prfs/fips_prf.h @@ -0,0 +1,80 @@ +/** + * @file fips_prf.h + * + * @brief Interface of fips_prf_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef FIPS_PRF_H_ +#define FIPS_PRF_H_ + +typedef struct fips_prf_t fips_prf_t; + +#include +#include +#include + +/** + * @brief Implementation of prf_t using the FIPS 186-2-change1 standard. + * + * FIPS defines a "General Purpose Random Number Generator" (Revised + * Algorithm for Computing m values of x (Appendix 3.1 of FIPS 186-2)). This + * implementation is not intended for private key generation and therefore does + * not include the "mod q" operation (see FIPS 186-2-change1 p74). + * The FIPS PRF is stateful; the key changes every time when bytes are acquired. + * + * @b Constructors: + * - fips_prf_create() + * - prf_create() using one of the FIPS algorithms + * + * @ingroup prfs + */ +struct fips_prf_t { + + /** + * Generic prf_t interface for this fips_prf_t class. + */ + prf_t prf_interface; +}; + +/** + * @brief Creates a new fips_prf_t object. + * + * FIPS 186-2 defines G() functions used in the PRF function. It can + * be implemented either based on SHA1 or DES. + * + * @param b size of b (in bytes, not bits) + * @param g G() function to use (e.g. g_sha1) + * @return + * - fips_prf_t object + * - NULL if b invalid not supported + * + * @ingroup prfs + */ +fips_prf_t *fips_prf_create(size_t b, void(*g)(u_int8_t[],chunk_t,u_int8_t[])); + +/** + * @brief Implementation of the G() function based on SHA1. + * + * @param t initialization vector for SHA1 hasher, 20 bytes long + * @param c value to hash, not longer than 512 bit + * @param res result of G(), requries 20 bytes + */ +void g_sha1(u_int8_t t[], chunk_t c, u_int8_t res[]); + +#endif /* FIPS_PRF_H_ */ diff --git a/src/libstrongswan/crypto/prfs/hmac_prf.c b/src/libstrongswan/crypto/prfs/hmac_prf.c new file mode 100644 index 000000000..f315f880d --- /dev/null +++ b/src/libstrongswan/crypto/prfs/hmac_prf.c @@ -0,0 +1,118 @@ +/** + * @file hmac_prf.c + * + * @brief Implementation for hmac_prf_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "hmac_prf.h" + +#include + + +typedef struct private_hmac_prf_t private_hmac_prf_t; + +/** + * Private data of a hma_prf_t object. + */ +struct private_hmac_prf_t { + /** + * Public hmac_prf_t interface. + */ + hmac_prf_t public; + + /** + * Hmac to use for generation. + */ + hmac_t *hmac; +}; + +/** + * Implementation of prf_t.get_bytes. + */ +static void get_bytes(private_hmac_prf_t *this, chunk_t seed, u_int8_t *buffer) +{ + this->hmac->get_mac(this->hmac, seed, buffer); +} + +/** + * Implementation of prf_t.allocate_bytes. + */ +static void allocate_bytes(private_hmac_prf_t *this, chunk_t seed, chunk_t *chunk) +{ + this->hmac->allocate_mac(this->hmac, seed, chunk); +} + +/** + * Implementation of prf_t.get_block_size. + */ +static size_t get_block_size(private_hmac_prf_t *this) +{ + return this->hmac->get_block_size(this->hmac); +} + +/** + * Implementation of prf_t.get_block_size. + */ +static size_t get_key_size(private_hmac_prf_t *this) +{ + /* for HMAC prfs, IKEv2 uses block size as key size */ + return this->hmac->get_block_size(this->hmac); +} + +/** + * Implementation of prf_t.set_key. + */ +static void set_key(private_hmac_prf_t *this, chunk_t key) +{ + this->hmac->set_key(this->hmac, key); +} + +/** + * Implementation of prf_t.destroy. + */ +static void destroy(private_hmac_prf_t *this) +{ + this->hmac->destroy(this->hmac); + free(this); +} + +/* + * Described in header. + */ +hmac_prf_t *hmac_prf_create(hash_algorithm_t hash_algorithm) +{ + private_hmac_prf_t *this = malloc_thing(private_hmac_prf_t); + + this->public.prf_interface.get_bytes = (void (*) (prf_t *,chunk_t,u_int8_t*))get_bytes; + this->public.prf_interface.allocate_bytes = (void (*) (prf_t*,chunk_t,chunk_t*))allocate_bytes; + this->public.prf_interface.get_block_size = (size_t (*) (prf_t*))get_block_size; + this->public.prf_interface.get_key_size = (size_t (*) (prf_t*))get_key_size; + this->public.prf_interface.set_key = (void (*) (prf_t *,chunk_t))set_key; + this->public.prf_interface.destroy = (void (*) (prf_t *))destroy; + + this->hmac = hmac_create(hash_algorithm); + if (this->hmac == NULL) + { + free(this); + return NULL; + } + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/prfs/hmac_prf.h b/src/libstrongswan/crypto/prfs/hmac_prf.h new file mode 100644 index 000000000..9b06ee3a2 --- /dev/null +++ b/src/libstrongswan/crypto/prfs/hmac_prf.h @@ -0,0 +1,65 @@ +/** + * @file hmac_prf.h + * + * @brief Interface of hmac_prf_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PRF_HMAC_H_ +#define PRF_HMAC_H_ + +typedef struct hmac_prf_t hmac_prf_t; + +#include +#include +#include + +/** + * @brief Implementation of prf_t interface using the + * HMAC algorithm. + * + * This simply wraps a hmac_t in a prf_t. More a question of + * interface matching. + * + * @b Constructors: + * - hmac_prf_create() + * + * @ingroup prfs + */ +struct hmac_prf_t { + + /** + * Generic prf_t interface for this hmac_prf_t class. + */ + prf_t prf_interface; +}; + +/** + * @brief Creates a new hmac_prf_t object. + * + * @param hash_algorithm hmac's hash algorithm + * @return + * - hmac_prf_t object + * - NULL if hash not supported + * + * @ingroup prfs + */ +hmac_prf_t *hmac_prf_create(hash_algorithm_t hash_algorithm); + +#endif /*PRF_HMAC_SHA1_H_*/ diff --git a/src/libstrongswan/crypto/prfs/prf.c b/src/libstrongswan/crypto/prfs/prf.c new file mode 100644 index 000000000..f803829af --- /dev/null +++ b/src/libstrongswan/crypto/prfs/prf.c @@ -0,0 +1,70 @@ +/** + * @file prf.c + * + * @brief Generic constructor for all prf_t + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#include "prf.h" + +#include +#include +#include + +ENUM_BEGIN(pseudo_random_function_names, PRF_UNDEFINED, PRF_FIPS_DES, + "PRF_UNDEFINED", + "PRF_FIPS_SHA1_160", + "PRF_FIPS_DES"); +ENUM_NEXT(pseudo_random_function_names, PRF_HMAC_MD5, PRF_HMAC_SHA2_512, PRF_FIPS_DES, + "PRF_HMAC_MD5", + "PRF_HMAC_SHA1", + "PRF_HMAC_TIGER", + "PRF_AES128_CBC", + "PRF_HMAC_SHA2_256", + "PRF_HMAC_SHA2_384", + "PRF_HMAC_SHA2_512"); +ENUM_END(pseudo_random_function_names, PRF_HMAC_SHA2_512); + +/* + * Described in header. + */ +prf_t *prf_create(pseudo_random_function_t pseudo_random_function) +{ + switch (pseudo_random_function) + { + case PRF_HMAC_SHA1: + return (prf_t*)hmac_prf_create(HASH_SHA1); + case PRF_HMAC_MD5: + return (prf_t*)hmac_prf_create(HASH_MD5); + case PRF_HMAC_SHA2_256: + return (prf_t*)hmac_prf_create(HASH_SHA256); + case PRF_HMAC_SHA2_384: + return (prf_t*)hmac_prf_create(HASH_SHA384); + case PRF_HMAC_SHA2_512: + return (prf_t*)hmac_prf_create(HASH_SHA512); + case PRF_FIPS_SHA1_160: + return (prf_t*)fips_prf_create(20, g_sha1); + case PRF_FIPS_DES: + case PRF_HMAC_TIGER: + case PRF_AES128_CBC: + default: + return NULL; + } +} diff --git a/src/libstrongswan/crypto/prfs/prf.h b/src/libstrongswan/crypto/prfs/prf.h new file mode 100644 index 000000000..8560a4a9c --- /dev/null +++ b/src/libstrongswan/crypto/prfs/prf.h @@ -0,0 +1,142 @@ +/** + * @file prf.h + * + * @brief Interface prf_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PRF_H_ +#define PRF_H_ + +typedef enum pseudo_random_function_t pseudo_random_function_t; +typedef struct prf_t prf_t; + +#include + +/** + * @brief Pseudo random function, as in IKEv2 RFC 3.3.2. + * + * PRF algorithms not defined in IKEv2 are allocated in "private use" + * space. + * + * @ingroup prfs + */ +enum pseudo_random_function_t { + PRF_UNDEFINED = 1024, + /** Implemented via hmac_prf_t. */ + PRF_HMAC_MD5 = 1, + /** Implemented via hmac_prf_t. */ + PRF_HMAC_SHA1 = 2, + PRF_HMAC_TIGER = 3, + PRF_AES128_CBC = 4, + /** Implemented via hmac_prf_t. */ + PRF_HMAC_SHA2_256 = 5, + /** Implemented via hmac_prf_t. */ + PRF_HMAC_SHA2_384 = 6, + /** Implemented via hmac_prf_t. */ + PRF_HMAC_SHA2_512 = 7, + /** Implemented via fips_prf_t, other output sizes would be possible */ + PRF_FIPS_SHA1_160 = 1025, + /** Could be implemented via fips_prf_t, uses fixed output size of 160bit */ + PRF_FIPS_DES = 1026, +}; + +/** + * enum name for encryption_algorithm_t. + */ +extern enum_name_t *pseudo_random_function_names; + +/** + * @brief Generic interface for pseudo-random-functions. + * + * @b Constructors: + * - prf_create() + * - hmac_prf_create() + * + * @todo Implement more prf algorithms + * + * @ingroup prfs + */ +struct prf_t { + /** + * @brief Generates pseudo random bytes and writes them in the buffer. + * + * @param this calling object + * @param seed a chunk containing the seed for the next bytes + * @param[out] buffer pointer where the generated bytes will be written + */ + void (*get_bytes) (prf_t *this, chunk_t seed, u_int8_t *buffer); + + /** + * @brief Generates pseudo random bytes and allocate space for them. + * + * @param this calling object + * @param seed a chunk containing the seed for the next bytes + * @param[out] chunk chunk which will hold generated bytes + */ + void (*allocate_bytes) (prf_t *this, chunk_t seed, chunk_t *chunk); + + /** + * @brief Get the block size of this prf_t object. + * + * @param this calling object + * @return block size in bytes + */ + size_t (*get_block_size) (prf_t *this); + + /** + * @brief Get the key size of this prf_t object. + * + * This is a suggestion only, all implemented PRFs accept variable key + * length. + * + * @param this calling object + * @return key size in bytes + */ + size_t (*get_key_size) (prf_t *this); + + /** + * @brief Set the key for this prf_t object. + * + * @param this calling object + * @param key key to set + */ + void (*set_key) (prf_t *this, chunk_t key); + + /** + * @brief Destroys a prf object. + * + * @param this calling object + */ + void (*destroy) (prf_t *this); +}; + +/** + * @brief Generic constructor for a prf_t oject. + * + * @param pseudo_random_function Algorithm to use + * @return + * - prf_t object + * - NULL if prf algorithm not supported + * + * @ingroup prfs + */ +prf_t *prf_create(pseudo_random_function_t pseudo_random_function); + +#endif /*PRF_H_*/ diff --git a/src/libstrongswan/crypto/rsa/rsa_private_key.c b/src/libstrongswan/crypto/rsa/rsa_private_key.c new file mode 100644 index 000000000..5b1647965 --- /dev/null +++ b/src/libstrongswan/crypto/rsa/rsa_private_key.c @@ -0,0 +1,774 @@ +/** + * @file rsa_private_key.c + * + * @brief Implementation of rsa_private_key_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include + +#include "rsa_public_key.h" +#include "rsa_private_key.h" + +#include +#include +#include + +/** + * OIDs for hash algorithms are defined in rsa_public_key.c. + */ +extern u_int8_t md2_oid[18]; +extern u_int8_t md5_oid[18]; +extern u_int8_t sha1_oid[15]; +extern u_int8_t sha256_oid[19]; +extern u_int8_t sha384_oid[19]; +extern u_int8_t sha512_oid[19]; + + +/** + * defined in rsa_public_key.c + */ +extern chunk_t rsa_public_key_info_to_asn1(const mpz_t n, const mpz_t e); + + +/** + * Public exponent to use for key generation. + */ +#define PUBLIC_EXPONENT 0x10001 + + +typedef struct private_rsa_private_key_t private_rsa_private_key_t; + +/** + * Private data of a rsa_private_key_t object. + */ +struct private_rsa_private_key_t { + /** + * Public interface for this signer. + */ + rsa_private_key_t public; + + /** + * Version of key, as encoded in PKCS#1 + */ + u_int version; + + /** + * Public modulus. + */ + mpz_t n; + + /** + * Public exponent. + */ + mpz_t e; + + /** + * Private prime 1. + */ + mpz_t p; + + /** + * Private Prime 2. + */ + mpz_t q; + + /** + * Private exponent. + */ + mpz_t d; + + /** + * Private exponent 1. + */ + mpz_t exp1; + + /** + * Private exponent 2. + */ + mpz_t exp2; + + /** + * Private coefficient. + */ + mpz_t coeff; + + /** + * Keysize in bytes. + */ + size_t k; + + /** + * Keyid formed as a SHA-1 hash of a publicKeyInfo object + */ + chunk_t keyid; + + + /** + * @brief Implements the RSADP algorithm specified in PKCS#1. + * + * @param this calling object + * @param data data to process + * @return processed data + */ + chunk_t (*rsadp) (private_rsa_private_key_t *this, chunk_t data); + + /** + * @brief Implements the RSASP1 algorithm specified in PKCS#1. + * @param this calling object + * @param data data to process + * @return processed data + */ + chunk_t (*rsasp1) (private_rsa_private_key_t *this, chunk_t data); + + /** + * @brief Generate a prime value. + * + * @param this calling object + * @param prime_size size of the prime, in bytes + * @param[out] prime uninitialized mpz + */ + status_t (*compute_prime) (private_rsa_private_key_t *this, size_t prime_size, mpz_t *prime); + +}; + +/* ASN.1 definition of a PKCS#1 RSA private key */ +static const asn1Object_t privkey_objects[] = { + { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */ + { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */ + { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */ + { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 10 */ + { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ + { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */ + { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */ + { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END } /* 15 */ +}; + +#define PRIV_KEY_VERSION 1 +#define PRIV_KEY_MODULUS 2 +#define PRIV_KEY_PUB_EXP 3 +#define PRIV_KEY_PRIV_EXP 4 +#define PRIV_KEY_PRIME1 5 +#define PRIV_KEY_PRIME2 6 +#define PRIV_KEY_EXP1 7 +#define PRIV_KEY_EXP2 8 +#define PRIV_KEY_COEFF 9 +#define PRIV_KEY_ROOF 16 + +static private_rsa_private_key_t *rsa_private_key_create_empty(void); + +/** + * Implementation of private_rsa_private_key_t.compute_prime. + */ +static status_t compute_prime(private_rsa_private_key_t *this, size_t prime_size, mpz_t *prime) +{ + randomizer_t *randomizer; + chunk_t random_bytes; + status_t status; + + randomizer = randomizer_create(); + mpz_init(*prime); + + do + { + status = randomizer->allocate_random_bytes(randomizer, prime_size, &random_bytes); + if (status != SUCCESS) + { + randomizer->destroy(randomizer); + mpz_clear(*prime); + return FAILED; + } + + /* make sure most significant bit is set */ + random_bytes.ptr[0] = random_bytes.ptr[0] | 0x80; + + /* convert chunk to mpz value */ + mpz_import(*prime, random_bytes.len, 1, 1, 1, 0, random_bytes.ptr); + + /* get next prime */ + mpz_nextprime (*prime, *prime); + + free(random_bytes.ptr); + } + /* check if it isnt too large */ + while (((mpz_sizeinbase(*prime, 2) + 7) / 8) > prime_size); + + randomizer->destroy(randomizer); + return SUCCESS; +} + +/** + * Implementation of private_rsa_private_key_t.rsadp and private_rsa_private_key_t.rsasp1. + */ +static chunk_t rsadp(private_rsa_private_key_t *this, chunk_t data) +{ + mpz_t t1, t2; + chunk_t decrypted; + + mpz_init(t1); + mpz_init(t2); + + mpz_import(t1, data.len, 1, 1, 1, 0, data.ptr); + + mpz_powm(t2, t1, this->exp1, this->p); /* m1 = c^dP mod p */ + mpz_powm(t1, t1, this->exp2, this->q); /* m2 = c^dQ mod Q */ + mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */ + mpz_mod(t2, t2, this->p); + mpz_mul(t2, t2, this->coeff); + mpz_mod(t2, t2, this->p); + + mpz_mul(t2, t2, this->q); /* m = m2 + h q */ + mpz_add(t1, t1, t2); + + decrypted.len = this->k; + decrypted.ptr = mpz_export(NULL, NULL, 1, decrypted.len, 1, 0, t1); + + mpz_clear(t1); + mpz_clear(t2); + + return decrypted; +} + +/** + * Implementation of rsa_private_key.build_emsa_signature. + */ +static status_t build_emsa_pkcs1_signature(private_rsa_private_key_t *this, hash_algorithm_t hash_algorithm, chunk_t data, chunk_t *signature) +{ + hasher_t *hasher; + chunk_t hash; + chunk_t em; + chunk_t oid; + + /* get oid string prepended to hash */ + switch (hash_algorithm) + { + case HASH_MD2: + { + oid.ptr = md2_oid; + oid.len = sizeof(md2_oid); + break; + } + case HASH_MD5: + { + oid.ptr = md5_oid; + oid.len = sizeof(md5_oid); + break; + } + case HASH_SHA1: + { + oid.ptr = sha1_oid; + oid.len = sizeof(sha1_oid); + break; + } + case HASH_SHA256: + { + oid.ptr = sha256_oid; + oid.len = sizeof(sha256_oid); + break; + } + case HASH_SHA384: + { + oid.ptr = sha384_oid; + oid.len = sizeof(sha384_oid); + break; + } + case HASH_SHA512: + { + oid.ptr = sha512_oid; + oid.len = sizeof(sha512_oid); + break; + } + default: + { + return NOT_SUPPORTED; + } + } + + /* get hasher */ + hasher = hasher_create(hash_algorithm); + if (hasher == NULL) + { + return NOT_SUPPORTED; + } + + /* build hash */ + hasher->allocate_hash(hasher, data, &hash); + hasher->destroy(hasher); + + /* build chunk to rsa-decrypt: + * EM = 0x00 || 0x01 || PS || 0x00 || T. + * PS = 0xFF padding, with length to fill em + * T = oid || hash + */ + em.len = this->k; + em.ptr = malloc(em.len); + + /* fill em with padding */ + memset(em.ptr, 0xFF, em.len); + /* set magic bytes */ + *(em.ptr) = 0x00; + *(em.ptr+1) = 0x01; + *(em.ptr + em.len - hash.len - oid.len - 1) = 0x00; + /* set hash */ + memcpy(em.ptr + em.len - hash.len, hash.ptr, hash.len); + /* set oid */ + memcpy(em.ptr + em.len - hash.len - oid.len, oid.ptr, oid.len); + + /* build signature */ + *signature = this->rsasp1(this, em); + + free(hash.ptr); + free(em.ptr); + + return SUCCESS; +} + +/** + * Implementation of rsa_private_key.get_key. + */ +static status_t get_key(private_rsa_private_key_t *this, chunk_t *key) +{ + chunk_t n, e, p, q, d, exp1, exp2, coeff; + + n.len = this->k; + n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, this->n); + e.len = this->k; + e.ptr = mpz_export(NULL, NULL, 1, e.len, 1, 0, this->e); + p.len = this->k; + p.ptr = mpz_export(NULL, NULL, 1, p.len, 1, 0, this->p); + q.len = this->k; + q.ptr = mpz_export(NULL, NULL, 1, q.len, 1, 0, this->q); + d.len = this->k; + d.ptr = mpz_export(NULL, NULL, 1, d.len, 1, 0, this->d); + exp1.len = this->k; + exp1.ptr = mpz_export(NULL, NULL, 1, exp1.len, 1, 0, this->exp1); + exp2.len = this->k; + exp2.ptr = mpz_export(NULL, NULL, 1, exp2.len, 1, 0, this->exp2); + coeff.len = this->k; + coeff.ptr = mpz_export(NULL, NULL, 1, coeff.len, 1, 0, this->coeff); + + key->len = this->k * 8; + key->ptr = malloc(key->len); + memcpy(key->ptr + this->k * 0, n.ptr , n.len); + memcpy(key->ptr + this->k * 1, e.ptr, e.len); + memcpy(key->ptr + this->k * 2, p.ptr, p.len); + memcpy(key->ptr + this->k * 3, q.ptr, q.len); + memcpy(key->ptr + this->k * 4, d.ptr, d.len); + memcpy(key->ptr + this->k * 5, exp1.ptr, exp1.len); + memcpy(key->ptr + this->k * 6, exp2.ptr, exp2.len); + memcpy(key->ptr + this->k * 7, coeff.ptr, coeff.len); + + free(n.ptr); + free(e.ptr); + free(p.ptr); + free(q.ptr); + free(d.ptr); + free(exp1.ptr); + free(exp2.ptr); + free(coeff.ptr); + + return SUCCESS; +} + +/** + * Implementation of rsa_private_key.save_key. + */ +static status_t save_key(private_rsa_private_key_t *this, char *file) +{ + return NOT_SUPPORTED; +} + +/** + * Implementation of rsa_private_key.get_public_key. + */ +rsa_public_key_t *get_public_key(private_rsa_private_key_t *this) +{ + return NULL; +} + +/** + * Implementation of rsa_private_key.belongs_to. + */ +static bool belongs_to(private_rsa_private_key_t *this, rsa_public_key_t *public) +{ + return chunk_equals(this->keyid, public->get_keyid(public)); +} + +/** + * Check the loaded key if it is valid and usable + * TODO: Log errors + */ +static status_t check(private_rsa_private_key_t *this) +{ + mpz_t t, u, q1; + status_t status = SUCCESS; + + /* PKCS#1 1.5 section 6 requires modulus to have at least 12 octets. + * We actually require more (for security). + */ + if (this->k < 512/8) + { + return FAILED; + } + + /* we picked a max modulus size to simplify buffer allocation */ + if (this->k > 8192/8) + { + return FAILED; + } + + mpz_init(t); + mpz_init(u); + mpz_init(q1); + + /* check that n == p * q */ + mpz_mul(u, this->p, this->q); + if (mpz_cmp(u, this->n) != 0) + { + status = FAILED; + } + + /* check that e divides neither p-1 nor q-1 */ + mpz_sub_ui(t, this->p, 1); + mpz_mod(t, t, this->e); + if (mpz_cmp_ui(t, 0) == 0) + { + status = FAILED; + } + + mpz_sub_ui(t, this->q, 1); + mpz_mod(t, t, this->e); + if (mpz_cmp_ui(t, 0) == 0) + { + status = FAILED; + } + + /* check that d is e^-1 (mod lcm(p-1, q-1)) */ + /* see PKCS#1v2, aka RFC 2437, for the "lcm" */ + mpz_sub_ui(q1, this->q, 1); + mpz_sub_ui(u, this->p, 1); + mpz_gcd(t, u, q1); /* t := gcd(p-1, q-1) */ + mpz_mul(u, u, q1); /* u := (p-1) * (q-1) */ + mpz_divexact(u, u, t); /* u := lcm(p-1, q-1) */ + + mpz_mul(t, this->d, this->e); + mpz_mod(t, t, u); + if (mpz_cmp_ui(t, 1) != 0) + { + status = FAILED; + } + + /* check that exp1 is d mod (p-1) */ + mpz_sub_ui(u, this->p, 1); + mpz_mod(t, this->d, u); + if (mpz_cmp(t, this->exp1) != 0) + { + status = FAILED; + } + + /* check that exp2 is d mod (q-1) */ + mpz_sub_ui(u, this->q, 1); + mpz_mod(t, this->d, u); + if (mpz_cmp(t, this->exp2) != 0) + { + status = FAILED; + } + + /* check that coeff is (q^-1) mod p */ + mpz_mul(t, this->coeff, this->q); + mpz_mod(t, t, this->p); + if (mpz_cmp_ui(t, 1) != 0) + { + status = FAILED; + } + + mpz_clear(t); + mpz_clear(u); + mpz_clear(q1); + return status; +} + +/** + * Implementation of rsa_private_key.clone. + */ +static rsa_private_key_t* _clone(private_rsa_private_key_t *this) +{ + private_rsa_private_key_t *clone = rsa_private_key_create_empty(); + + mpz_init_set(clone->n, this->n); + mpz_init_set(clone->e, this->e); + mpz_init_set(clone->p, this->p); + mpz_init_set(clone->q, this->q); + mpz_init_set(clone->d, this->d); + mpz_init_set(clone->exp1, this->exp1); + mpz_init_set(clone->exp2, this->exp2); + mpz_init_set(clone->coeff, this->coeff); + clone->keyid = chunk_clone(this->keyid); + clone->k = this->k; + + return &clone->public; +} + +/** + * Implementation of rsa_private_key.destroy. + */ +static void destroy(private_rsa_private_key_t *this) +{ + mpz_clear(this->n); + mpz_clear(this->e); + mpz_clear(this->p); + mpz_clear(this->q); + mpz_clear(this->d); + mpz_clear(this->exp1); + mpz_clear(this->exp2); + mpz_clear(this->coeff); + free(this->keyid.ptr); + free(this); +} + +/** + * Internal generic constructor + */ +static private_rsa_private_key_t *rsa_private_key_create_empty(void) +{ + private_rsa_private_key_t *this = malloc_thing(private_rsa_private_key_t); + + /* public functions */ + this->public.build_emsa_pkcs1_signature = (status_t (*) (rsa_private_key_t*,hash_algorithm_t,chunk_t,chunk_t*))build_emsa_pkcs1_signature; + this->public.get_key = (status_t (*) (rsa_private_key_t*,chunk_t*))get_key; + this->public.save_key = (status_t (*) (rsa_private_key_t*,char*))save_key; + this->public.get_public_key = (rsa_public_key_t *(*) (rsa_private_key_t*))get_public_key; + this->public.belongs_to = (bool (*) (rsa_private_key_t*,rsa_public_key_t*))belongs_to; + this->public.clone = (rsa_private_key_t*(*)(rsa_private_key_t*))_clone; + this->public.destroy = (void (*) (rsa_private_key_t*))destroy; + + /* private functions */ + this->rsadp = rsadp; + this->rsasp1 = rsadp; /* same algorithm */ + this->compute_prime = compute_prime; + + return this; +} + +/* + * See header + */ +rsa_private_key_t *rsa_private_key_create(size_t key_size) +{ + mpz_t p, q, n, e, d, exp1, exp2, coeff; + mpz_t m, q1, t; + private_rsa_private_key_t *this; + + this = rsa_private_key_create_empty(); + key_size = key_size / 8; + + /* Get values of primes p and q */ + if (this->compute_prime(this, key_size/2, &p) != SUCCESS) + { + free(this); + return NULL; + } + if (this->compute_prime(this, key_size/2, &q) != SUCCESS) + { + mpz_clear(p); + free(this); + return NULL; + } + + mpz_init(t); + mpz_init(n); + mpz_init(d); + mpz_init(exp1); + mpz_init(exp2); + mpz_init(coeff); + + /* Swapping Primes so p is larger then q */ + if (mpz_cmp(p, q) < 0) + { + mpz_set(t, p); + mpz_set(p, q); + mpz_set(q, t); + } + + mpz_mul(n, p, q); /* n = p*q */ + mpz_init_set_ui(e, PUBLIC_EXPONENT); /* assign public exponent */ + mpz_init_set(m, p); /* m = p */ + mpz_sub_ui(m, m, 1); /* m = m -1 */ + mpz_init_set(q1, q); /* q1 = q */ + mpz_sub_ui(q1, q1, 1); /* q1 = q1 -1 */ + mpz_gcd(t, m, q1); /* t = gcd(p-1, q-1) */ + mpz_mul(m, m, q1); /* m = (p-1)*(q-1) */ + mpz_divexact(m, m, t); /* m = m / t */ + mpz_gcd(t, m, e); /* t = gcd(m, e) (greatest common divisor) */ + + mpz_invert(d, e, m); /* e has an inverse mod m */ + if (mpz_cmp_ui(d, 0) < 0) /* make sure d is positive */ + { + mpz_add(d, d, m); + } + mpz_sub_ui(t, p, 1); /* t = p-1 */ + mpz_mod(exp1, d, t); /* exp1 = d mod p-1 */ + mpz_sub_ui(t, q, 1); /* t = q-1 */ + mpz_mod(exp2, d, t); /* exp2 = d mod q-1 */ + + mpz_invert(coeff, q, p); /* coeff = q^-1 mod p */ + if (mpz_cmp_ui(coeff, 0) < 0) /* make coeff d is positive */ + { + mpz_add(coeff, coeff, p); + } + + mpz_clear(q1); + mpz_clear(m); + mpz_clear(t); + + /* apply values */ + *(this->p) = *p; + *(this->q) = *q; + *(this->n) = *n; + *(this->e) = *e; + *(this->d) = *d; + *(this->exp1) = *exp1; + *(this->exp2) = *exp2; + *(this->coeff) = *coeff; + + /* set key size in bytes */ + this->k = key_size; + + return &this->public; +} + +/* + * see header + */ +rsa_private_key_t *rsa_private_key_create_from_chunk(chunk_t blob) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + private_rsa_private_key_t *this; + + this = rsa_private_key_create_empty(); + + mpz_init(this->n); + mpz_init(this->e); + mpz_init(this->p); + mpz_init(this->q); + mpz_init(this->d); + mpz_init(this->exp1); + mpz_init(this->exp2); + mpz_init(this->coeff); + + asn1_init(&ctx, blob, 0, FALSE, TRUE); + + while (objectID < PRIV_KEY_ROOF) + { + if (!extract_object(privkey_objects, &objectID, &object, &level, &ctx)) + { + destroy(this); + return FALSE; + } + switch (objectID) + { + case PRIV_KEY_VERSION: + if (object.len > 0 && *object.ptr != 0) + { + destroy(this); + return NULL; + } + break; + case PRIV_KEY_MODULUS: + mpz_import(this->n, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_PUB_EXP: + mpz_import(this->e, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_PRIV_EXP: + mpz_import(this->d, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_PRIME1: + mpz_import(this->p, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_PRIME2: + mpz_import(this->q, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_EXP1: + mpz_import(this->exp1, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_EXP2: + mpz_import(this->exp2, object.len, 1, 1, 1, 0, object.ptr); + break; + case PRIV_KEY_COEFF: + mpz_import(this->coeff, object.len, 1, 1, 1, 0, object.ptr); + break; + } + objectID++; + } + + this->k = (mpz_sizeinbase(this->n, 2) + 7) / 8; + + /* form the keyid as a SHA-1 hash of a publicKeyInfo object */ + { + chunk_t publicKeyInfo = rsa_public_key_info_to_asn1(this->n, this->e); + hasher_t *hasher = hasher_create(HASH_SHA1); + + hasher->allocate_hash(hasher, publicKeyInfo, &this->keyid); + hasher->destroy(hasher); + free(publicKeyInfo.ptr); + } + + if (check(this) != SUCCESS) + { + destroy(this); + return NULL; + } + else + { + return &this->public; + } +} + +/* + * see header + */ +rsa_private_key_t *rsa_private_key_create_from_file(char *filename, chunk_t *passphrase) +{ + bool pgp = FALSE; + chunk_t chunk = chunk_empty; + rsa_private_key_t *key = NULL; + + if (!pem_asn1_load_file(filename, passphrase, "private key", &chunk, &pgp)) + return NULL; + + key = rsa_private_key_create_from_chunk(chunk); + free(chunk.ptr); + return key; +} diff --git a/src/libstrongswan/crypto/rsa/rsa_private_key.h b/src/libstrongswan/crypto/rsa/rsa_private_key.h new file mode 100644 index 000000000..9ec07704e --- /dev/null +++ b/src/libstrongswan/crypto/rsa/rsa_private_key.h @@ -0,0 +1,184 @@ +/** + * @file rsa_private_key.h + * + * @brief Interface of rsa_private_key_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef RSA_PRIVATE_KEY_H_ +#define RSA_PRIVATE_KEY_H_ + +typedef struct rsa_private_key_t rsa_private_key_t; + +#include +#include +#include + +/** + * @brief RSA private key with associated functions. + * + * Currently only supports signing using EMSA encoding. + * + * @b Constructors: + * - rsa_private_key_create() + * - rsa_private_key_create_from_chunk() + * - rsa_private_key_create_from_file() + * + * @see rsa_public_key_t + * + * @todo Implement get_key(), save_key(), get_public_key() + * + * @ingroup rsa + */ +struct rsa_private_key_t { + + /** + * @brief Build a signature over a chunk using EMSA-PKCS1 encoding. + * + * This signature creates a hash using the specified hash algorithm, concatenates + * it with an ASN1-OID of the hash algorithm and runs the RSASP1 function + * on it. + * + * @param this calling object + * @param hash_algorithm hash algorithm to use for hashing + * @param data data to sign + * @param[out] signature allocated signature + * @return + * - SUCCESS + * - INVALID_STATE, if key not set + * - NOT_SUPPORTED, if hash algorithm not supported + */ + status_t (*build_emsa_pkcs1_signature) (rsa_private_key_t *this, hash_algorithm_t hash_algorithm, chunk_t data, chunk_t *signature); + + /** + * @brief Gets the key. + * + * UNIMPLEMENTED! + * + * @param this calling object + * @param key key (in a propriarity format) + * @return + * - SUCCESS + * - INVALID_STATE, if key not set + */ + status_t (*get_key) (rsa_private_key_t *this, chunk_t *key); + + /** + * @brief Saves a key to a file. + * + * Not implemented! + * + * @param this calling object + * @param file file to which the key should be written. + * @return NOT_SUPPORTED + */ + status_t (*save_key) (rsa_private_key_t *this, char *file); + + /** + * @brief Generate a new key. + * + * Generates a new private_key with specified key size + * + * @param this calling object + * @param key_size size of the key in bits + * @return + * - SUCCESS + * - INVALID_ARG if key_size invalid + */ + status_t (*generate_key) (rsa_private_key_t *this, size_t key_size); + + /** + * @brief Create a rsa_public_key_t with the public + * parts of the key. + * + * @param this calling object + * @return public_key + */ + rsa_public_key_t *(*get_public_key) (rsa_private_key_t *this); + + /** + * @brief Check if a private key belongs to a public key. + * + * Compares the public part of the private key with the + * public key, return TRUE if it equals. + * + * @param this private key + * @param public public key + * @return TRUE, if keys belong together + */ + bool (*belongs_to) (rsa_private_key_t *this, rsa_public_key_t *public); + + /** + * @brief Clone the private key. + * + * @param this private key to clone + * @return clone of this + */ + rsa_private_key_t *(*clone) (rsa_private_key_t *this); + + /** + * @brief Destroys the private key. + * + * @param this private key to destroy + */ + void (*destroy) (rsa_private_key_t *this); +}; + +/** + * @brief Generate a new RSA key with specified key length. + * + * @param key_size size of the key in bits + * @return generated rsa_private_key_t. + * + * @ingroup rsa + */ +rsa_private_key_t *rsa_private_key_create(size_t key_size); + +/** + * @brief Load an RSA private key from a chunk. + * + * Load a key from a chunk, encoded as described in PKCS#1 + * (ASN1 DER encoded). + * + * @param chunk chunk containing the DER encoded key + * @return loaded rsa_private_key_t, or NULL + * + * @ingroup rsa + */ +rsa_private_key_t *rsa_private_key_create_from_chunk(chunk_t chunk); + +/** + * @brief Load an RSA private key from a file. + * + * Load a key from a file, which is either in a unencrypted binary + * format (DER), or in a (encrypted) PEM format. The supplied + * passphrase is used to decrypt an ecrypted key. + * + * @param filename filename which holds the key + * @param passphrase optional passphase for decryption, can be NULL + * @return loaded rsa_private_key_t, or NULL + * + * @todo Implement PEM file loading + * @todo Implement key decryption + * + * @ingroup rsa + */ +rsa_private_key_t *rsa_private_key_create_from_file(char *filename, chunk_t *passphrase); + +#endif /*RSA_PRIVATE_KEY_H_*/ diff --git a/src/libstrongswan/crypto/rsa/rsa_public_key.c b/src/libstrongswan/crypto/rsa/rsa_public_key.c new file mode 100644 index 000000000..38899670f --- /dev/null +++ b/src/libstrongswan/crypto/rsa/rsa_public_key.c @@ -0,0 +1,497 @@ +/** + * @file rsa_public_key.c + * + * @brief Implementation of rsa_public_key_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "rsa_public_key.h" + +#include +#include +#include + +/* + * For simplicity, we use these predefined values for hash algorithm OIDs + * These also contain the length of the appended hash + * These values are also used in rsa_private_key.c. + */ + +const u_int8_t md2_oid[] = { + 0x30,0x20, + 0x30,0x0c, + 0x06,0x08, + 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x02, + 0x05,0x00, + 0x04,0x10 +}; + +const u_int8_t md5_oid[] = { + 0x30,0x20, + 0x30,0x0c, + 0x06,0x08, + 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05, + 0x05,0x00, + 0x04,0x10 +}; + +const u_int8_t sha1_oid[] = { + 0x30,0x21, + 0x30,0x09, + 0x06,0x05, + 0x2b,0x0e,0x03,0x02,0x1a, + 0x05,0x00, + 0x04,0x14 +}; + +const u_int8_t sha256_oid[] = { + 0x30,0x31, + 0x30,0x0d, + 0x06,0x09, + 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01, + 0x05,0x00, + 0x04,0x20 +}; + +const u_int8_t sha384_oid[] = { + 0x30,0x41, + 0x30,0x0d, + 0x06,0x09, + 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02, + 0x05,0x00, + 0x04,0x30 +}; + +const u_int8_t sha512_oid[] = { + 0x30,0x51, + 0x30,0x0d, + 0x06,0x09, + 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03, + 0x05,0x00, + 0x04,0x40 +}; + +#define LARGEST_HASH_OID_SIZE sizeof(sha512_oid) + +/* ASN.1 definition public key */ +static const asn1Object_t pubkey_objects[] = { + { 0, "RSAPublicKey", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 2 */ +}; + +#define PUB_KEY_RSA_PUBLIC_KEY 0 +#define PUB_KEY_MODULUS 1 +#define PUB_KEY_EXPONENT 2 +#define PUB_KEY_ROOF 3 + +typedef struct private_rsa_public_key_t private_rsa_public_key_t; + +/** + * Private data structure with signing context. + */ +struct private_rsa_public_key_t { + /** + * Public interface for this signer. + */ + rsa_public_key_t public; + + /** + * Public modulus. + */ + mpz_t n; + + /** + * Public exponent. + */ + mpz_t e; + + /** + * Keysize in bytes. + */ + size_t k; + + /** + * Keyid formed as a SHA-1 hash of a publicKeyInfo object + */ + chunk_t keyid; + + /** + * @brief Implements the RSAEP algorithm specified in PKCS#1. + * + * @param this calling object + * @param data data to process + * @return processed data + */ + chunk_t (*rsaep) (const private_rsa_public_key_t *this, chunk_t data); + + /** + * @brief Implements the RSASVP1 algorithm specified in PKCS#1. + * + * @param this calling object + * @param data data to process + * @return processed data + */ + chunk_t (*rsavp1) (const private_rsa_public_key_t *this, chunk_t data); +}; + +private_rsa_public_key_t *rsa_public_key_create_empty(void); + +/** + * Implementation of private_rsa_public_key_t.rsaep and private_rsa_public_key_t.rsavp1 + */ +static chunk_t rsaep(const private_rsa_public_key_t *this, chunk_t data) +{ + mpz_t m, c; + chunk_t encrypted; + + mpz_init(c); + mpz_init(m); + + mpz_import(m, data.len, 1, 1, 1, 0, data.ptr); + + mpz_powm(c, m, this->e, this->n); + + encrypted.len = this->k; + encrypted.ptr = mpz_export(NULL, NULL, 1, encrypted.len, 1, 0, c); + + mpz_clear(c); + mpz_clear(m); + + return encrypted; +} + +/** + * Implementation of rsa_public_key.verify_emsa_pkcs1_signature. + */ +static status_t verify_emsa_pkcs1_signature(const private_rsa_public_key_t *this, chunk_t data, chunk_t signature) +{ + hasher_t *hasher = NULL; + chunk_t hash; + chunk_t em; + u_int8_t *pos; + status_t res = FAILED; + + /* remove any preceding 0-bytes from signature */ + while (signature.len && *(signature.ptr) == 0x00) + { + signature.len -= 1; + signature.ptr++; + } + + if (signature.len > this->k) + { + return INVALID_ARG; + } + + /* unpack signature */ + em = this->rsavp1(this, signature); + + /* result should look like this: + * EM = 0x00 || 0x01 || PS || 0x00 || T. + * PS = 0xFF padding, with length to fill em + * T = oid || hash + */ + + /* check magic bytes */ + if ((*(em.ptr) != 0x00) || (*(em.ptr+1) != 0x01)) + { + goto end; + } + + /* find magic 0x00 */ + pos = em.ptr + 2; + while (pos <= em.ptr + em.len) + { + if (*pos == 0x00) + { + /* found magic byte, stop */ + pos++; + break; + } + else if (*pos != 0xFF) + { + /* bad padding, decryption failed ?!*/ + goto end; + } + pos++; + } + + if (pos + LARGEST_HASH_OID_SIZE > em.ptr + em.len) + { + /* not enought room for oid compare */ + goto end; + } + + if (memeq(md2_oid, pos, sizeof(md2_oid))) + { + hasher = hasher_create(HASH_MD2); + pos += sizeof(md2_oid); + } + else if (memeq(md5_oid, pos, sizeof(md5_oid))) + { + hasher = hasher_create(HASH_MD5); + pos += sizeof(md5_oid); + } + else if (memeq(sha1_oid, pos, sizeof(sha1_oid))) + { + hasher = hasher_create(HASH_SHA1); + pos += sizeof(sha1_oid); + } + else if (memeq(sha256_oid, pos, sizeof(sha256_oid))) + { + hasher = hasher_create(HASH_SHA256); + pos += sizeof(sha256_oid); + } + else if (memeq(sha384_oid, pos, sizeof(sha384_oid))) + { + hasher = hasher_create(HASH_SHA384); + pos += sizeof(sha384_oid); + } + else if (memeq(sha512_oid, pos, sizeof(sha512_oid))) + { + hasher = hasher_create(HASH_SHA512); + pos += sizeof(sha512_oid); + } + + if (hasher == NULL) + { + /* unsupported hash algorithm */ + res = NOT_SUPPORTED;; + goto end; + } + + if (pos + hasher->get_hash_size(hasher) != em.ptr + em.len) + { + /* bad length */ + hasher->destroy(hasher); + goto end; + } + + /* build our own hash */ + hasher->allocate_hash(hasher, data, &hash); + hasher->destroy(hasher); + + /* compare the hashes */ + res = memeq(hash.ptr, pos, hash.len) ? SUCCESS : FAILED; + free(hash.ptr); + +end: + free(em.ptr); + return res; +} + +/** + * Implementation of rsa_public_key.get_key. + */ +static status_t get_key(const private_rsa_public_key_t *this, chunk_t *key) +{ + chunk_t n, e; + + n.len = this->k; + n.ptr = mpz_export(NULL, NULL, 1, n.len, 1, 0, this->n); + e.len = this->k; + e.ptr = mpz_export(NULL, NULL, 1, e.len, 1, 0, this->e); + + key->len = this->k * 2; + key->ptr = malloc(key->len); + memcpy(key->ptr, n.ptr, n.len); + memcpy(key->ptr + n.len, e.ptr, e.len); + free(n.ptr); + free(e.ptr); + + return SUCCESS; +} + +/** + * Implementation of rsa_public_key.save_key. + */ +static status_t save_key(const private_rsa_public_key_t *this, char *file) +{ + return NOT_SUPPORTED; +} + +/** + * Implementation of rsa_public_key.get_modulus. + */ +static mpz_t *get_modulus(const private_rsa_public_key_t *this) +{ + return (mpz_t*)&this->n; +} + +/** + * Implementation of rsa_public_key.get_keysize. + */ +static size_t get_keysize(const private_rsa_public_key_t *this) +{ + return this->k; +} + +/** + * Implementation of rsa_public_key.get_keyid. + */ +static chunk_t get_keyid(const private_rsa_public_key_t *this) +{ + return this->keyid; +} + +/** + * Implementation of rsa_public_key.clone. + */ +static rsa_public_key_t* _clone(const private_rsa_public_key_t *this) +{ + private_rsa_public_key_t *clone = rsa_public_key_create_empty(); + + mpz_init_set(clone->n, this->n); + mpz_init_set(clone->e, this->e); + clone->keyid = chunk_clone(this->keyid); + clone->k = this->k; + + return &clone->public; +} + +/** + * Implementation of rsa_public_key.destroy. + */ +static void destroy(private_rsa_public_key_t *this) +{ + mpz_clear(this->n); + mpz_clear(this->e); + free(this->keyid.ptr); + free(this); +} + +/** + * Generic private constructor + */ +private_rsa_public_key_t *rsa_public_key_create_empty(void) +{ + private_rsa_public_key_t *this = malloc_thing(private_rsa_public_key_t); + + /* public functions */ + this->public.verify_emsa_pkcs1_signature = (status_t (*) (const rsa_public_key_t*,chunk_t,chunk_t))verify_emsa_pkcs1_signature; + this->public.get_key = (status_t (*) (const rsa_public_key_t*,chunk_t*))get_key; + this->public.save_key = (status_t (*) (const rsa_public_key_t*,char*))save_key; + this->public.get_modulus = (mpz_t *(*) (const rsa_public_key_t*))get_modulus; + this->public.get_keysize = (size_t (*) (const rsa_public_key_t*))get_keysize; + this->public.get_keyid = (chunk_t (*) (const rsa_public_key_t*))get_keyid; + this->public.clone = (rsa_public_key_t* (*) (const rsa_public_key_t*))_clone; + this->public.destroy = (void (*) (rsa_public_key_t*))destroy; + + /* private functions */ + this->rsaep = rsaep; + this->rsavp1 = rsaep; /* same algorithm */ + + return this; +} + +/** + * Build a DER-encoded publicKeyInfo object from an RSA public key. + * Also used in rsa_private_key.c. + */ +chunk_t rsa_public_key_info_to_asn1(const mpz_t n, const mpz_t e) +{ + chunk_t rawKey = asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_integer_from_mpz(n), + asn1_integer_from_mpz(e)); + chunk_t publicKey; + + u_char *pos = build_asn1_object(&publicKey, ASN1_BIT_STRING, 1 + rawKey.len); + + *pos++ = 0x00; + memcpy(pos, rawKey.ptr, rawKey.len); + free(rawKey.ptr); + + return asn1_wrap(ASN1_SEQUENCE, "cm", ASN1_rsaEncryption_id, + publicKey); +} + +/* + * See header + */ +rsa_public_key_t *rsa_public_key_create_from_chunk(chunk_t blob) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + private_rsa_public_key_t *this = rsa_public_key_create_empty(); + + mpz_init(this->n); + mpz_init(this->e); + + asn1_init(&ctx, blob, 0, FALSE, FALSE); + + while (objectID < PUB_KEY_ROOF) + { + if (!extract_object(pubkey_objects, &objectID, &object, &level, &ctx)) + { + destroy(this); + return FALSE; + } + switch (objectID) + { + case PUB_KEY_MODULUS: + mpz_import(this->n, object.len, 1, 1, 1, 0, object.ptr); + break; + case PUB_KEY_EXPONENT: + mpz_import(this->e, object.len, 1, 1, 1, 0, object.ptr); + break; + } + objectID++; + } + + this->k = (mpz_sizeinbase(this->n, 2) + 7) / 8; + + /* form the keyid as a SHA-1 hash of a publicKeyInfo object */ + { + chunk_t publicKeyInfo = rsa_public_key_info_to_asn1(this->n, this->e); + hasher_t *hasher = hasher_create(HASH_SHA1); + + hasher->allocate_hash(hasher, publicKeyInfo, &this->keyid); + hasher->destroy(hasher); + free(publicKeyInfo.ptr); + } + + return &this->public; +} + +/* + * See header + */ +rsa_public_key_t *rsa_public_key_create_from_file(char *filename) +{ + bool pgp = FALSE; + chunk_t chunk = chunk_empty; + rsa_public_key_t *pubkey = NULL; + + if (!pem_asn1_load_file(filename, NULL, "public key", &chunk, &pgp)) + return NULL; + + pubkey = rsa_public_key_create_from_chunk(chunk); + free(chunk.ptr); + return pubkey; +} diff --git a/src/libstrongswan/crypto/rsa/rsa_public_key.h b/src/libstrongswan/crypto/rsa/rsa_public_key.h new file mode 100644 index 000000000..1ee54dcc3 --- /dev/null +++ b/src/libstrongswan/crypto/rsa/rsa_public_key.h @@ -0,0 +1,164 @@ +/** + * @file rsa_public_key.h + * + * @brief Interface of rsa_public_key_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef RSA_PUBLIC_KEY_H_ +#define RSA_PUBLIC_KEY_H_ + +typedef struct rsa_public_key_t rsa_public_key_t; + +#include + +#include + +/** + * @brief RSA public key with associated functions. + * + * Currently only supports signature verification using + * the EMSA encoding (see PKCS1) + * + * @b Constructors: + * - rsa_public_key_create_from_chunk() + * - rsa_public_key_create_from_file() + * - rsa_private_key_t.get_public_key() + * + * @see rsa_private_key_t + * + * @todo Implement getkey() and savekey() + * + * @ingroup rsa + */ +struct rsa_public_key_t { + + /** + * @brief Verify a EMSA-PKCS1 encodined signature. + * + * Processes the supplied signature with the RSAVP1 function, + * selects the hash algorithm form the resultign ASN1-OID and + * verifies the hash against the supplied data. + * + * @param this rsa_public_key to use + * @param data data to sign + * @param signature signature to verify + * @return + * - SUCCESS, if signature ok + * - INVALID_STATE, if key not set + * - NOT_SUPPORTED, if hash algorithm not supported + * - INVALID_ARG, if signature is not a signature + * - FAILED if signature invalid or unable to verify + */ + status_t (*verify_emsa_pkcs1_signature) (const rsa_public_key_t *this, chunk_t data, chunk_t signature); + + /** + * @brief Gets the key. + * + * Currently uses a proprietary format which is only inteded + * for testing. This should be replaced with a proper + * ASN1 encoded key format, when charon gets the ASN1 + * capabilities. + * + * @param this calling object + * @param key key (in a propriarity format) + * @return + * - SUCCESS + * - INVALID_STATE, if key not set + */ + status_t (*get_key) (const rsa_public_key_t *this, chunk_t *key); + + /** + * @brief Saves a key to a file. + * + * Not implemented! + * + * @param this calling object + * @param file file to which the key should be written. + * @return NOT_SUPPORTED + */ + status_t (*save_key) (const rsa_public_key_t *this, char *file); + + /** + * @brief Get the modulus of the key. + * + * @param this calling object + * @return modulus (n) of the key + */ + mpz_t *(*get_modulus) (const rsa_public_key_t *this); + + /** + * @brief Get the size of the modulus in bytes. + * + * @param this calling object + * @return size of the modulus (n) in bytes + */ + size_t (*get_keysize) (const rsa_public_key_t *this); + + /** + * @brief Get the keyid formed as the SHA-1 hash of a publicKeyInfo object. + * + * @param this calling object + * @return keyid in the form of a SHA-1 hash + */ + chunk_t (*get_keyid) (const rsa_public_key_t *this); + + /** + * @brief Clone the public key. + * + * @param this public key to clone + * @return clone of this + */ + rsa_public_key_t *(*clone) (const rsa_public_key_t *this); + + /** + * @brief Destroys the public key. + * + * @param this public key to destroy + */ + void (*destroy) (rsa_public_key_t *this); +}; + +/** + * @brief Load an RSA public key from a chunk. + * + * Load a key from a chunk, encoded in the more frequently + * used publicKeyInfo object (ASN1 DER encoded). + * + * @param chunk chunk containing the DER encoded key + * @return loaded rsa_public_key_t, or NULL + * + * @ingroup rsa + */ +rsa_public_key_t *rsa_public_key_create_from_chunk(chunk_t chunk); + +/** + * @brief Load an RSA public key from a file. + * + * Load a key from a file, which is either in binary + * format (DER), or in PEM format. + * + * @param filename filename which holds the key + * @return loaded rsa_public_key_t, or NULL + * + * @ingroup rsa + */ +rsa_public_key_t *rsa_public_key_create_from_file(char *filename); + +#endif /*RSA_PUBLIC_KEY_H_*/ diff --git a/src/libstrongswan/crypto/signers/hmac_signer.c b/src/libstrongswan/crypto/signers/hmac_signer.c new file mode 100644 index 000000000..76e1ce50e --- /dev/null +++ b/src/libstrongswan/crypto/signers/hmac_signer.c @@ -0,0 +1,174 @@ +/** + * @file hmac_signer.c + * + * @brief Implementation of hmac_signer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "hmac_signer.h" + +#include + +typedef struct private_hmac_signer_t private_hmac_signer_t; + +/** + * Private data structure with signing context. + */ +struct private_hmac_signer_t { + /** + * Public interface of hmac_signer_t. + */ + hmac_signer_t public; + + /** + * Assigned hmac function. + */ + prf_t *hmac_prf; + + /** + * Block size (truncation of HMAC Hash) + */ + size_t block_size; +}; + +/** + * Implementation of signer_t.get_signature. + */ +static void get_signature (private_hmac_signer_t *this, chunk_t data, u_int8_t *buffer) +{ + u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; + + this->hmac_prf->get_bytes(this->hmac_prf, data, full_mac); + + /* copy MAC depending on truncation */ + memcpy(buffer, full_mac, this->block_size); +} + +/** + * Implementation of signer_t.allocate_signature. + */ +static void allocate_signature (private_hmac_signer_t *this, chunk_t data, chunk_t *chunk) +{ + chunk_t signature; + u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; + + this->hmac_prf->get_bytes(this->hmac_prf,data,full_mac); + + signature.ptr = malloc(this->block_size); + signature.len = this->block_size; + + /* copy signature */ + memcpy(signature.ptr, full_mac, this->block_size); + + *chunk = signature; +} + +/** + * Implementation of signer_t.verify_signature. + */ +static bool verify_signature(private_hmac_signer_t *this, chunk_t data, chunk_t signature) +{ + u_int8_t full_mac[this->hmac_prf->get_block_size(this->hmac_prf)]; + + this->hmac_prf->get_bytes(this->hmac_prf, data, full_mac); + + if (signature.len != this->block_size) + { + return FALSE; + } + + /* compare mac aka signature :-) */ + if (memcmp(signature.ptr, full_mac, this->block_size) == 0) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * Implementation of signer_t.get_key_size. + */ +static size_t get_key_size(private_hmac_signer_t *this) +{ + /* for HMAC signer, IKEv2 uses block size as key size */ + return this->hmac_prf->get_block_size(this->hmac_prf); +} + +/** + * Implementation of signer_t.get_block_size. + */ +static size_t get_block_size(private_hmac_signer_t *this) +{ + return this->block_size; +} + +/** + * Implementation of signer_t.set_key. + */ +static void set_key(private_hmac_signer_t *this, chunk_t key) +{ + this->hmac_prf->set_key(this->hmac_prf, key); +} + +/** + * Implementation of signer_t.destroy. + */ +static status_t destroy(private_hmac_signer_t *this) +{ + this->hmac_prf->destroy(this->hmac_prf); + free(this); + return SUCCESS; +} + +/* + * Described in header + */ +hmac_signer_t *hmac_signer_create(hash_algorithm_t hash_algoritm, size_t block_size) +{ + size_t hmac_block_size; + private_hmac_signer_t *this = malloc_thing(private_hmac_signer_t); + + this->hmac_prf = (prf_t *) hmac_prf_create(hash_algoritm); + if (this->hmac_prf == NULL) + { + /* algorithm not supported */ + free(this); + return NULL; + } + + /* prevent invalid truncation */ + hmac_block_size = this->hmac_prf->get_block_size(this->hmac_prf); + this->block_size = min(block_size, hmac_block_size); + + /* interface functions */ + this->public.signer_interface.get_signature = (void (*) (signer_t*, chunk_t, u_int8_t*))get_signature; + this->public.signer_interface.allocate_signature = (void (*) (signer_t*, chunk_t, chunk_t*))allocate_signature; + this->public.signer_interface.verify_signature = (bool (*) (signer_t*, chunk_t, chunk_t))verify_signature; + this->public.signer_interface.get_key_size = (size_t (*) (signer_t*))get_key_size; + this->public.signer_interface.get_block_size = (size_t (*) (signer_t*))get_block_size; + this->public.signer_interface.set_key = (void (*) (signer_t*,chunk_t))set_key; + this->public.signer_interface.destroy = (void (*) (signer_t*))destroy; + + return &(this->public); +} diff --git a/src/libstrongswan/crypto/signers/hmac_signer.h b/src/libstrongswan/crypto/signers/hmac_signer.h new file mode 100644 index 000000000..2449069bd --- /dev/null +++ b/src/libstrongswan/crypto/signers/hmac_signer.h @@ -0,0 +1,68 @@ +/** + * @file hmac_signer.h + * + * @brief Interface of hmac_signer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef HMAC_SIGNER_H_ +#define HMAC_SIGNER_H_ + +typedef struct hmac_signer_t hmac_signer_t; + +#include +#include + +/** + * @brief Implementation of signer_t interface using HMAC. + * + * HMAC uses a standard hash function implemented in a hasher_t to build + * a MAC. + * + * @ingroup signers + */ +struct hmac_signer_t { + + /** + * generic signer_t interface for this signer + */ + signer_t signer_interface; +}; + +/** + * @brief Creates a new hmac_signer_t. + * + * HMAC signatures are often truncated to shorten them to a more usable, but + * still secure enough length. + * Block size must be equal or smaller then the hash algorithms + * hash. + * + * @param hash_algoritm Hash algorithm to use with signer + * @param block_size Size of resulting signature (truncated to block_size) + * @return + * - hmac_signer_t + * - NULL if hash algorithm not supported + * + * @ingroup signers + */ +hmac_signer_t *hmac_signer_create(hash_algorithm_t hash_algoritm, + size_t block_size); + + +#endif /*HMAC_SIGNER_H_*/ diff --git a/src/libstrongswan/crypto/signers/signer.c b/src/libstrongswan/crypto/signers/signer.c new file mode 100644 index 000000000..747bc5efa --- /dev/null +++ b/src/libstrongswan/crypto/signers/signer.c @@ -0,0 +1,65 @@ +/** + * @file signer.c + * + * @brief Implementation of generic signer_t constructor. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "signer.h" + +#include + +ENUM_BEGIN(integrity_algorithm_names, AUTH_UNDEFINED, AUTH_HMAC_SHA1_128, + "UNDEFINED", + "AUTH_HMAC_SHA1_128"); +ENUM_NEXT(integrity_algorithm_names, AUTH_HMAC_MD5_96, AUTH_AES_XCBC_96, AUTH_HMAC_SHA1_128, + "HMAC_MD5_96", + "HMAC_SHA1_96", + "DES_MAC", + "KPDK_MD5", + "AES_XCBC_96"); +ENUM_NEXT(integrity_algorithm_names, AUTH_HMAC_SHA2_256_128, AUTH_HMAC_SHA2_512_256, AUTH_AES_XCBC_96, + "AUTH_HMAC_SHA2_256_128", + "AUTH_HMAC_SHA2_384_192", + "AUTH_HMAC_SHA2_512_256"); +ENUM_END(integrity_algorithm_names, AUTH_HMAC_SHA2_512_256); + +/* + * Described in header. + */ +signer_t *signer_create(integrity_algorithm_t integrity_algorithm) +{ + switch(integrity_algorithm) + { + case AUTH_HMAC_SHA1_96: + return (signer_t *)hmac_signer_create(HASH_SHA1, 12); + case AUTH_HMAC_SHA1_128: + return (signer_t *)hmac_signer_create(HASH_SHA1, 16); + case AUTH_HMAC_MD5_96: + return (signer_t *)hmac_signer_create(HASH_MD5, 12); + case AUTH_HMAC_SHA2_256_128: + return (signer_t *)hmac_signer_create(HASH_SHA256, 16); + case AUTH_HMAC_SHA2_384_192: + return (signer_t *)hmac_signer_create(HASH_SHA384, 24); + case AUTH_HMAC_SHA2_512_256: + return (signer_t *)hmac_signer_create(HASH_SHA512, 32); + default: + return NULL; + } +} diff --git a/src/libstrongswan/crypto/signers/signer.h b/src/libstrongswan/crypto/signers/signer.h new file mode 100644 index 000000000..0f3709712 --- /dev/null +++ b/src/libstrongswan/crypto/signers/signer.h @@ -0,0 +1,147 @@ +/** + * @file signer.h + * + * @brief Interface for signer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef SIGNER_H_ +#define SIGNER_H_ + +typedef enum integrity_algorithm_t integrity_algorithm_t; +typedef struct signer_t signer_t; + +#include + +/** + * @brief Integrity algorithm, as in IKEv2 RFC 3.3.2. + * + * Algorithms not specified in IKEv2 are allocated in private use space. + * + * @ingroup signers + */ +enum integrity_algorithm_t { + AUTH_UNDEFINED = 1024, + /** Implemented via hmac_signer_t */ + AUTH_HMAC_MD5_96 = 1, + /** Implemented via hmac_signer_t */ + AUTH_HMAC_SHA1_96 = 2, + AUTH_DES_MAC = 3, + AUTH_KPDK_MD5 = 4, + AUTH_AES_XCBC_96 = 5, + /** Implemented via hmac_signer_t */ + AUTH_HMAC_SHA2_256_128 = 12, + /** Implemented via hmac_signer_t */ + AUTH_HMAC_SHA2_384_192 = 13, + /** Implemented via hmac_signer_t */ + AUTH_HMAC_SHA2_512_256 = 14, + /** Implemented via hmac_signer_t */ + AUTH_HMAC_SHA1_128 = 1025, +}; + +/** + * enum names for integrity_algorithm_t. + */ +extern enum_name_t *integrity_algorithm_names; + +/** + * @brief Generig interface for a symmetric signature algorithm. + * + * @b Constructors: + * - signer_create() + * - hmac_signer_create() + * + * @todo Implement more integrity algorithms + * + * @ingroup signers + */ +struct signer_t { + /** + * @brief Generate a signature. + * + * @param this calling object + * @param data a chunk containing the data to sign + * @param[out] buffer pointer where the signature will be written + */ + void (*get_signature) (signer_t *this, chunk_t data, u_int8_t *buffer); + + /** + * @brief Generate a signature and allocate space for it. + * + * @param this calling object + * @param data a chunk containing the data to sign + * @param[out] chunk chunk which will hold the allocated signature + */ + void (*allocate_signature) (signer_t *this, chunk_t data, chunk_t *chunk); + + /** + * @brief Verify a signature. + * + * @param this calling object + * @param data a chunk containing the data to verify + * @param signature a chunk containing the signature + * @return TRUE, if signature is valid, FALSE otherwise + */ + bool (*verify_signature) (signer_t *this, chunk_t data, chunk_t signature); + + /** + * @brief Get the block size of this signature algorithm. + * + * @param this calling object + * @return block size in bytes + */ + size_t (*get_block_size) (signer_t *this); + + /** + * @brief Get the key size of the signature algorithm. + * + * @param this calling object + * @return key size in bytes + */ + size_t (*get_key_size) (signer_t *this); + + /** + * @brief Set the key for this object. + * + * @param this calling object + * @param key key to set + */ + void (*set_key) (signer_t *this, chunk_t key); + + /** + * @brief Destroys a signer_t object. + * + * @param this calling object + */ + void (*destroy) (signer_t *this); +}; + +/** + * @brief Creates a new signer_t object. + * + * @param integrity_algorithm Algorithm to use for signing and verifying. + * @return + * - signer_t object + * - NULL if signer not supported + * + * @ingroup signers + */ +signer_t *signer_create(integrity_algorithm_t integrity_algorithm); + +#endif /*SIGNER_H_*/ diff --git a/src/libstrongswan/crypto/x509.c b/src/libstrongswan/crypto/x509.c new file mode 100755 index 000000000..58fcff16d --- /dev/null +++ b/src/libstrongswan/crypto/x509.c @@ -0,0 +1,1354 @@ +/** + * @file x509.c + * + * @brief Implementation of x509_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "x509.h" +#include "hashers/hasher.h" +#include +#include +#include +#include +#include +#include +#include + +#define CERT_WARNING_INTERVAL 30 /* days */ + +/** + * Different kinds of generalNames + */ +typedef enum generalNames_t generalNames_t; + +enum generalNames_t { + GN_OTHER_NAME = 0, + GN_RFC822_NAME = 1, + GN_DNS_NAME = 2, + GN_X400_ADDRESS = 3, + GN_DIRECTORY_NAME = 4, + GN_EDI_PARTY_NAME = 5, + GN_URI = 6, + GN_IP_ADDRESS = 7, + GN_REGISTERED_ID = 8, +}; + +typedef struct private_x509_t private_x509_t; + +/** + * Private data of a x509_t object. + */ +struct private_x509_t { + /** + * Public interface for this certificate. + */ + x509_t public; + + /** + * Time when certificate was installed + */ + time_t installed; + + /** + * Time until certificate can be trusted + */ + time_t until; + + /** + * Certificate status + */ + cert_status_t status; + + /** + * Authority flags + */ + u_int authority_flags; + + /** + * X.509 Certificate in DER format + */ + chunk_t certificate; + + /** + * X.509 certificate body over which signature is computed + */ + chunk_t tbsCertificate; + + /** + * Version of the X.509 certificate + */ + u_int version; + + /** + * Serial number of the X.509 certificate + */ + chunk_t serialNumber; + + /** + * Signature algorithm + */ + int sigAlg; + + /** + * ID representing the certificate issuer + */ + identification_t *issuer; + + /** + * Start time of certificate validity + */ + time_t notBefore; + + /** + * End time of certificate validity + */ + time_t notAfter; + + /** + * ID representing the certificate subject + */ + identification_t *subject; + + /** + * List of identification_t's representing subjectAltNames + */ + linked_list_t *subjectAltNames; + + /** + * List of identification_t's representing crlDistributionPoints + */ + linked_list_t *crlDistributionPoints; + + /** + * List of identification_t's representing ocspAccessLocations + */ + linked_list_t *ocspAccessLocations; + + /** + * Subject public key + */ + chunk_t subjectPublicKey; + + /** + * Subject RSA public key, if subjectPublicKeyAlgorithm == RSA + */ + rsa_public_key_t *public_key; + + /** + * Subject Key Identifier + */ + chunk_t subjectKeyID; + + /** + * Authority Key Identifier + */ + chunk_t authKeyID; + + /** + * Authority Key Serial Number + */ + chunk_t authKeySerialNumber; + + /** + * CA basic constraints flag + */ + bool isCA; + + /** + * OCSPSigner extended key usage flag + */ + bool isOcspSigner; + + /** + * Signature algorithm (must be identical to sigAlg) + */ + int algorithm; + + /** + * Signature + */ + chunk_t signature; + +}; + +/** + * ASN.1 definition of generalName + */ +static const asn1Object_t generalNameObjects[] = { + { 0, "otherName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_BODY }, /* 0 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 1 */ + { 0, "rfc822Name", ASN1_CONTEXT_S_1, ASN1_OPT|ASN1_BODY }, /* 2 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 3 */ + { 0, "dnsName", ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 4 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ + { 0, "x400Address", ASN1_CONTEXT_S_3, ASN1_OPT|ASN1_BODY }, /* 6 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ + { 0, "directoryName", ASN1_CONTEXT_C_4, ASN1_OPT|ASN1_BODY }, /* 8 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 0, "ediPartyName", ASN1_CONTEXT_C_5, ASN1_OPT|ASN1_BODY }, /* 10 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 11 */ + { 0, "URI", ASN1_CONTEXT_S_6, ASN1_OPT|ASN1_BODY }, /* 12 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 13 */ + { 0, "ipAddress", ASN1_CONTEXT_S_7, ASN1_OPT|ASN1_BODY }, /* 14 */ + { 0, "end choice", ASN1_EOC, ASN1_END }, /* 15 */ + { 0, "registeredID", ASN1_CONTEXT_S_8, ASN1_OPT|ASN1_BODY }, /* 16 */ + { 0, "end choice", ASN1_EOC, ASN1_END } /* 17 */ +}; +#define GN_OBJ_OTHER_NAME 0 +#define GN_OBJ_RFC822_NAME 2 +#define GN_OBJ_DNS_NAME 4 +#define GN_OBJ_X400_ADDRESS 6 +#define GN_OBJ_DIRECTORY_NAME 8 +#define GN_OBJ_EDI_PARTY_NAME 10 +#define GN_OBJ_URI 12 +#define GN_OBJ_IP_ADDRESS 14 +#define GN_OBJ_REGISTERED_ID 16 +#define GN_OBJ_ROOF 18 + +/** + * ASN.1 definition of otherName + */ +static const asn1Object_t otherNameObjects[] = { + {0, "type-id", ASN1_OID, ASN1_BODY }, /* 0 */ + {0, "value", ASN1_CONTEXT_C_0, ASN1_BODY } /* 1 */ +}; +#define ON_OBJ_ID_TYPE 0 +#define ON_OBJ_VALUE 1 +#define ON_OBJ_ROOF 2 +/** + * ASN.1 definition of a basicConstraints extension + */ +static const asn1Object_t basicConstraintsObjects[] = { + { 0, "basicConstraints", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "CA", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 1 */ + { 1, "pathLenConstraint", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 2 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 3 */ +}; +#define BASIC_CONSTRAINTS_CA 1 +#define BASIC_CONSTRAINTS_ROOF 4 + +/** + * ASN.1 definition of time + */ +static const asn1Object_t timeObjects[] = { + { 0, "utcTime", ASN1_UTCTIME, ASN1_OPT|ASN1_BODY }, /* 0 */ + { 0, "end opt", ASN1_EOC, ASN1_END }, /* 1 */ + { 0, "generalizeTime",ASN1_GENERALIZEDTIME, ASN1_OPT|ASN1_BODY }, /* 2 */ + { 0, "end opt", ASN1_EOC, ASN1_END } /* 3 */ +}; +#define TIME_UTC 0 +#define TIME_GENERALIZED 2 +#define TIME_ROOF 4 + +/** + * ASN.1 definition of a keyIdentifier + */ +static const asn1Object_t keyIdentifierObjects[] = { + { 0, "keyIdentifier", ASN1_OCTET_STRING, ASN1_BODY } /* 0 */ +}; + +/** + * ASN.1 definition of a authorityKeyIdentifier extension + */ +static const asn1Object_t authorityKeyIdentifierObjects[] = { + { 0, "authorityKeyIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "keyIdentifier", ASN1_CONTEXT_S_0, ASN1_OPT|ASN1_OBJ }, /* 1 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ + { 1, "authorityCertIssuer", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_OBJ }, /* 3 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "authorityCertSerialNumber",ASN1_CONTEXT_S_2, ASN1_OPT|ASN1_BODY }, /* 5 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */ +}; +#define AUTH_KEY_ID_KEY_ID 1 +#define AUTH_KEY_ID_CERT_ISSUER 3 +#define AUTH_KEY_ID_CERT_SERIAL 5 +#define AUTH_KEY_ID_ROOF 7 + +/** + * ASN.1 definition of a authorityInfoAccess extension + */ +static const asn1Object_t authorityInfoAccessObjects[] = { + { 0, "authorityInfoAccess", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "accessDescription", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "accessMethod", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "accessLocation", ASN1_EOC, ASN1_RAW }, /* 3 */ + { 0, "end loop", ASN1_EOC, ASN1_END } /* 4 */ +}; +#define AUTH_INFO_ACCESS_METHOD 2 +#define AUTH_INFO_ACCESS_LOCATION 3 +#define AUTH_INFO_ACCESS_ROOF 5 + +/** + * ASN.1 definition of a extendedKeyUsage extension + */ +static const asn1Object_t extendedKeyUsageObjects[] = { + { 0, "extendedKeyUsage", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "keyPurposeID", ASN1_OID, ASN1_BODY }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */ +}; + +#define EXT_KEY_USAGE_PURPOSE_ID 1 +#define EXT_KEY_USAGE_ROOF 3 + +/** + * ASN.1 definition of generalNames + */ +static const asn1Object_t generalNamesObjects[] = { + { 0, "generalNames", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "generalName", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */ +}; +#define GENERAL_NAMES_GN 1 +#define GENERAL_NAMES_ROOF 3 + + +/** + * ASN.1 definition of crlDistributionPoints + */ +static const asn1Object_t crlDistributionPointsObjects[] = { + { 0, "crlDistributionPoints", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "DistributionPoint", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "distributionPoint", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_LOOP }, /* 2 */ + { 3, "fullName", ASN1_CONTEXT_C_0, ASN1_OPT|ASN1_OBJ }, /* 3 */ + { 3, "end choice", ASN1_EOC, ASN1_END }, /* 4 */ + { 3, "nameRelToCRLIssuer",ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_BODY }, /* 5 */ + { 3, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 2, "reasons", ASN1_CONTEXT_C_1, ASN1_OPT|ASN1_BODY }, /* 8 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 9 */ + { 2, "crlIssuer", ASN1_CONTEXT_C_2, ASN1_OPT|ASN1_BODY }, /* 10 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 11 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 12 */ +}; +#define CRL_DIST_POINTS_FULLNAME 3 +#define CRL_DIST_POINTS_ROOF 13 + +/** + * ASN.1 definition of an X.509v3 x509 + */ +static const asn1Object_t certObjects[] = { + { 0, "x509", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "tbsCertificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "DEFAULT v1", ASN1_CONTEXT_C_0, ASN1_DEF }, /* 2 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 5 */ + { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */ + { 2, "validity", ASN1_SEQUENCE, ASN1_NONE }, /* 7 */ + { 3, "notBefore", ASN1_EOC, ASN1_RAW }, /* 8 */ + { 3, "notAfter", ASN1_EOC, ASN1_RAW }, /* 9 */ + { 2, "subject", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */ + { 2, "subjectPublicKeyInfo",ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ + { 3, "algorithm", ASN1_EOC, ASN1_RAW }, /* 12 */ + { 3, "subjectPublicKey", ASN1_BIT_STRING, ASN1_NONE }, /* 13 */ + { 4, "RSAPublicKey", ASN1_SEQUENCE, ASN1_RAW }, /* 14 */ + { 2, "issuerUniqueID", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 15 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 16 */ + { 2, "subjectUniqueID", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 17 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 18 */ + { 2, "optional extensions", ASN1_CONTEXT_C_3, ASN1_OPT }, /* 19 */ + { 3, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */ + { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */ + { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */ + { 5, "critical", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 23 */ + { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */ + { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */ + { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */ +}; +#define X509_OBJ_CERTIFICATE 0 +#define X509_OBJ_TBS_CERTIFICATE 1 +#define X509_OBJ_VERSION 3 +#define X509_OBJ_SERIAL_NUMBER 4 +#define X509_OBJ_SIG_ALG 5 +#define X509_OBJ_ISSUER 6 +#define X509_OBJ_NOT_BEFORE 8 +#define X509_OBJ_NOT_AFTER 9 +#define X509_OBJ_SUBJECT 10 +#define X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM 12 +#define X509_OBJ_SUBJECT_PUBLIC_KEY 13 +#define X509_OBJ_RSA_PUBLIC_KEY 14 +#define X509_OBJ_EXTN_ID 22 +#define X509_OBJ_CRITICAL 23 +#define X509_OBJ_EXTN_VALUE 24 +#define X509_OBJ_ALGORITHM 27 +#define X509_OBJ_SIGNATURE 28 +#define X509_OBJ_ROOF 29 + + +static u_char ASN1_subjectAltName_oid_str[] = { + 0x06, 0x03, 0x55, 0x1D, 0x11 +}; + +static const chunk_t ASN1_subjectAltName_oid = chunk_from_buf(ASN1_subjectAltName_oid_str); + + +/** + * compare two X.509 x509s by comparing their signatures + */ +static bool equals(const private_x509_t *this, const private_x509_t *other) +{ + return chunk_equals(this->signature, other->signature); +} + +/** + * extracts the basicConstraints extension + */ +static bool parse_basicConstraints(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + bool isCA = FALSE; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < BASIC_CONSTRAINTS_ROOF) { + + if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx)) + { + break; + } + if (objectID == BASIC_CONSTRAINTS_CA) + { + isCA = object.len && *object.ptr; + DBG2(" %s", isCA ? "TRUE" : "FALSE"); + } + objectID++; + } + return isCA; +} + +/* + * extracts an otherName + */ +static bool +parse_otherName(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + int objectID = 0; + u_int level; + int oid = OID_UNKNOWN; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < ON_OBJ_ROOF) + { + if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + switch (objectID) + { + case ON_OBJ_ID_TYPE: + oid = known_oid(object); + break; + case ON_OBJ_VALUE: + if (oid == OID_XMPP_ADDR) + { + if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING, level + 1, "xmppAddr")) + return FALSE; + } + break; + default: + break; + } + objectID++; + } + return TRUE; +} + +/* + * extracts a generalName + */ +static identification_t *parse_generalName(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + int objectID = 0; + u_int level; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < GN_OBJ_ROOF) + { + id_type_t id_type = ID_ANY; + + if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx)) + return NULL; + + switch (objectID) + { + case GN_OBJ_RFC822_NAME: + id_type = ID_RFC822_ADDR; + break; + case GN_OBJ_DNS_NAME: + id_type = ID_FQDN; + break; + case GN_OBJ_URI: + id_type = ID_DER_ASN1_GN_URI; + break; + case GN_OBJ_DIRECTORY_NAME: + id_type = ID_DER_ASN1_DN; + break; + case GN_OBJ_IP_ADDRESS: + id_type = ID_IPV4_ADDR; + break; + case GN_OBJ_OTHER_NAME: + if (!parse_otherName(object, level + 1)) + return NULL; + break; + case GN_OBJ_X400_ADDRESS: + case GN_OBJ_EDI_PARTY_NAME: + case GN_OBJ_REGISTERED_ID: + break; + default: + break; + } + + if (id_type != ID_ANY) + { + identification_t *gn = identification_create_from_encoding(id_type, object); + DBG2(" '%D'", gn); + return gn; + } + objectID++; + } + return NULL; +} + + +/** + * extracts one or several GNs and puts them into a chained list + */ +static void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, implicit, FALSE); + + while (objectID < GENERAL_NAMES_ROOF) + { + if (!extract_object(generalNamesObjects, &objectID, &object, &level, &ctx)) + return; + + if (objectID == GENERAL_NAMES_GN) + { + identification_t *gn = parse_generalName(object, level+1); + + if (gn != NULL) + list->insert_last(list, (void *)gn); + } + objectID++; + } + return; +} + +/** + * extracts and converts a UTCTIME or GENERALIZEDTIME object + */ +time_t parse_time(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + + while (objectID < TIME_ROOF) + { + if (!extract_object(timeObjects, &objectID, &object, &level, &ctx)) + return 0; + + if (objectID == TIME_UTC || objectID == TIME_GENERALIZED) + { + return asn1totime(&object, (objectID == TIME_UTC) + ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME); + } + objectID++; + } + return 0; +} + +/** + * extracts a keyIdentifier + */ +static chunk_t parse_keyIdentifier(chunk_t blob, int level0, bool implicit) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, implicit, FALSE); + + extract_object(keyIdentifierObjects, &objectID, &object, &level, &ctx); + return object; +} + +/** + * extracts an authoritykeyIdentifier + */ +void parse_authorityKeyIdentifier(chunk_t blob, int level0 , chunk_t *authKeyID, chunk_t *authKeySerialNumber) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + while (objectID < AUTH_KEY_ID_ROOF) + { + if (!extract_object(authorityKeyIdentifierObjects, &objectID, &object, &level, &ctx)) + { + return; + } + switch (objectID) + { + case AUTH_KEY_ID_KEY_ID: + *authKeyID = parse_keyIdentifier(object, level+1, TRUE); + break; + case AUTH_KEY_ID_CERT_ISSUER: + { + /* TODO: parse_generalNames(object, level+1, TRUE); */ + break; + } + case AUTH_KEY_ID_CERT_SERIAL: + *authKeySerialNumber = object; + break; + default: + break; + } + objectID++; + } +} + +/** + * extracts an authorityInfoAcess location + */ +static void parse_authorityInfoAccess(chunk_t blob, int level0, linked_list_t *list) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + u_int accessMethod = OID_UNKNOWN; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + while (objectID < AUTH_INFO_ACCESS_ROOF) + { + if (!extract_object(authorityInfoAccessObjects, &objectID, &object, &level, &ctx)) + { + return; + } + switch (objectID) + { + case AUTH_INFO_ACCESS_METHOD: + accessMethod = known_oid(object); + break; + case AUTH_INFO_ACCESS_LOCATION: + { + switch (accessMethod) + { + case OID_OCSP: + if (*object.ptr == ASN1_CONTEXT_S_6) + { + identification_t *accessLocation; + + if (asn1_length(&object) == ASN1_INVALID_LENGTH) + return; + DBG2(" '%.*s'",(int)object.len, object.ptr); + accessLocation = identification_create_from_encoding(ID_DER_ASN1_GN_URI, object); + list->insert_last(list, (void *)accessLocation); + } + break; + default: + /* unkown accessMethod, ignoring */ + break; + } + break; + } + default: + break; + } + objectID++; + } +} + +/** + * extracts extendedKeyUsage OIDs + */ +static bool parse_extendedKeyUsage(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + while (objectID < EXT_KEY_USAGE_ROOF) + { + if (!extract_object(extendedKeyUsageObjects, &objectID, &object, &level, &ctx)) + { + return FALSE; + } + if (objectID == EXT_KEY_USAGE_PURPOSE_ID && + known_oid(object) == OID_OCSP_SIGNING) + { + return TRUE; + } + objectID++; + } + return FALSE; +} + +/** + * extracts one or several crlDistributionPoints and puts them into + * a chained list + */ +static void parse_crlDistributionPoints(chunk_t blob, int level0, linked_list_t *list) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + while (objectID < CRL_DIST_POINTS_ROOF) + { + if (!extract_object(crlDistributionPointsObjects, &objectID, &object, &level, &ctx)) + { + return; + } + if (objectID == CRL_DIST_POINTS_FULLNAME) + { + /* append extracted generalNames to existing chained list */ + parse_generalNames(object, level+1, TRUE, list); + + } + objectID++; + } +} + + +/** + * Parses an X.509v3 certificate + */ +static bool parse_certificate(chunk_t blob, u_int level0, private_x509_t *cert) +{ + asn1_ctx_t ctx; + bool critical; + chunk_t object; + u_int level; + u_int extn_oid = OID_UNKNOWN; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, FALSE); + while (objectID < X509_OBJ_ROOF) + { + if (!extract_object(certObjects, &objectID, &object, &level, &ctx)) + { + return FALSE; + } + /* those objects which will parsed further need the next higher level */ + level++; + switch (objectID) { + case X509_OBJ_CERTIFICATE: + cert->certificate = object; + break; + case X509_OBJ_TBS_CERTIFICATE: + cert->tbsCertificate = object; + break; + case X509_OBJ_VERSION: + cert->version = (object.len) ? (1+(u_int)*object.ptr) : 1; + DBG2(" v%d", cert->version); + break; + case X509_OBJ_SERIAL_NUMBER: + cert->serialNumber = object; + break; + case X509_OBJ_SIG_ALG: + cert->sigAlg = parse_algorithmIdentifier(object, level, NULL); + break; + case X509_OBJ_ISSUER: + cert->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); + DBG2(" '%D'", cert->issuer); + break; + case X509_OBJ_NOT_BEFORE: + cert->notBefore = parse_time(object, level); + break; + case X509_OBJ_NOT_AFTER: + cert->notAfter = parse_time(object, level); + break; + case X509_OBJ_SUBJECT: + cert->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object); + DBG2(" '%D'", cert->subject); + break; + case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM: + if (parse_algorithmIdentifier(object, level, NULL) != OID_RSA_ENCRYPTION) + { + DBG2(" unsupported public key algorithm"); + return FALSE; + } + break; + case X509_OBJ_SUBJECT_PUBLIC_KEY: + if (ctx.blobs[4].len > 0 && *ctx.blobs[4].ptr == 0x00) + { + /* skip initial bit string octet defining 0 unused bits */ + ctx.blobs[4].ptr++; ctx.blobs[4].len--; + } + else + { + DBG2(" invalid RSA public key format"); + return FALSE; + } + break; + case X509_OBJ_RSA_PUBLIC_KEY: + cert->subjectPublicKey = object; + break; + case X509_OBJ_EXTN_ID: + extn_oid = known_oid(object); + break; + case X509_OBJ_CRITICAL: + critical = object.len && *object.ptr; + DBG2(" %s", critical ? "TRUE" : "FALSE"); + break; + case X509_OBJ_EXTN_VALUE: + { + switch (extn_oid) { + case OID_SUBJECT_KEY_ID: + cert->subjectKeyID = chunk_clone(parse_keyIdentifier(object, level, FALSE)); + break; + case OID_SUBJECT_ALT_NAME: + parse_generalNames(object, level, FALSE, cert->subjectAltNames); + break; + case OID_BASIC_CONSTRAINTS: + cert->isCA = parse_basicConstraints(object, level); + break; + case OID_CRL_DISTRIBUTION_POINTS: + parse_crlDistributionPoints(object, level, cert->crlDistributionPoints); + break; + case OID_AUTHORITY_KEY_ID: + parse_authorityKeyIdentifier(object, level , &cert->authKeyID, &cert->authKeySerialNumber); + break; + case OID_AUTHORITY_INFO_ACCESS: + parse_authorityInfoAccess(object, level, cert->ocspAccessLocations); + break; + case OID_EXTENDED_KEY_USAGE: + cert->isOcspSigner = parse_extendedKeyUsage(object, level); + break; + case OID_NS_REVOCATION_URL: + case OID_NS_CA_REVOCATION_URL: + case OID_NS_CA_POLICY_URL: + case OID_NS_COMMENT: + if (!parse_asn1_simple_object(&object, ASN1_IA5STRING , level, oid_names[extn_oid].name)) + return FALSE; + break; + default: + break; + } + break; + } + case X509_OBJ_ALGORITHM: + cert->algorithm = parse_algorithmIdentifier(object, level, NULL); + break; + case X509_OBJ_SIGNATURE: + cert->signature = object; + break; + default: + break; + } + objectID++; + } + + if (cert->subjectKeyID.ptr == NULL) + { + hasher_t *hasher = hasher_create(HASH_SHA1); + + hasher->allocate_hash(hasher, cert->subjectPublicKey, &cert->subjectKeyID); + hasher->destroy(hasher); + } + + time(&cert->installed); + return TRUE; +} + +/** + * Implements x509_t.is_valid + */ +static err_t is_valid(const private_x509_t *this, time_t *until) +{ + time_t current_time = time(NULL); + + DBG2(" not before : %T", &this->notBefore); + DBG2(" current time: %T", ¤t_time); + DBG2(" not after : %T", &this->notAfter); + + if (until != NULL && + (*until == UNDEFINED_TIME || this->notAfter < *until)) + { + *until = this->notAfter; + } + if (current_time < this->notBefore) + { + return "is not valid yet"; + } + if (current_time > this->notAfter) + { + return "has expired"; + } + DBG2(" certificate is valid"); + return NULL; +} + +/** + * Implements x509_t.is_ca + */ +static bool is_ca(const private_x509_t *this) +{ + return this->isCA; +} + +/** + * Implements x509_t.is_ocsp_signer + */ +static bool is_ocsp_signer(const private_x509_t *this) +{ + return this->isOcspSigner; +} + +/** + * Implements x509_t.is_self_signed + */ +static bool is_self_signed(const private_x509_t *this) +{ + return this->subject->equals(this->subject, this->issuer); +} + +/** + * Implements x509_t.equals_subjectAltName + */ +static bool equals_subjectAltName(const private_x509_t *this, identification_t *id) +{ + bool found = FALSE; + identification_t *subjectAltName; + iterator_t *iterator; + + iterator = this->subjectAltNames->create_iterator(this->subjectAltNames, TRUE); + while (iterator->iterate(iterator, (void**)&subjectAltName)) + { + if (id->equals(id, subjectAltName)) + { + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implements x509_t.is_issuer + */ +static bool is_issuer(const private_x509_t *this, const private_x509_t *issuer) +{ + return (this->authKeyID.ptr) + ? chunk_equals(this->authKeyID, issuer->subjectKeyID) + : (this->issuer->equals(this->issuer, issuer->subject) + && chunk_equals_or_null(this->authKeySerialNumber, issuer->serialNumber)); +} + +/** + * Implements x509_t.get_certificate + */ +static chunk_t get_certificate(const private_x509_t *this) +{ + return this->certificate; +} + +/** + * Implements x509_t.get_public_key + */ +static rsa_public_key_t *get_public_key(const private_x509_t *this) +{ + return this->public_key; +} + +/** + * Implements x509_t.get_serialNumber + */ +static chunk_t get_serialNumber(const private_x509_t *this) +{ + return this->serialNumber; +} + +/** + * Implements x509_t.get_subjectKeyID + */ +static chunk_t get_subjectKeyID(const private_x509_t *this) +{ + return this->subjectKeyID; +} + +/** + * Implements x509_t.get_keyid + */ +static chunk_t get_keyid(const private_x509_t *this) +{ + return this->public_key->get_keyid(this->public_key); +} + +/** + * Implements x509_t.get_issuer + */ +static identification_t *get_issuer(const private_x509_t *this) +{ + return this->issuer; +} + +/** + * Implements x509_t.get_subject + */ +static identification_t *get_subject(const private_x509_t *this) +{ + return this->subject; +} + +/** + * Implements x509_t.set_until + */ +static void set_until(private_x509_t *this, time_t until) +{ + this->until = until; +} + +/** + * Implements x509_t.get_until + */ +static time_t get_until(const private_x509_t *this) +{ + return this->until; +} + +/** + * Implements x509_t.set_status + */ +static void set_status(private_x509_t *this, cert_status_t status) +{ + this->status = status; +} + +/** + * Implements x509_t.get_status + */ +static cert_status_t get_status(const private_x509_t *this) +{ + return this->status; +} + +/** + * Implements x509_t.add_authority_flags + */ +static void add_authority_flags(private_x509_t *this, u_int flags) +{ + this->authority_flags |= flags; +} + +/** + * Implements x509_t.add_authority_flags + */ +static u_int get_authority_flags(private_x509_t *this) +{ + return this->authority_flags; +} + +/** + * Implements x509_t.has_authority_flag + */ +static bool has_authority_flag(private_x509_t *this, u_int flags) +{ + return (this->authority_flags & flags) != AUTH_NONE; +} + +/** + * Implements x509_t.create_crluri_iterator + */ +static iterator_t *create_crluri_iterator(const private_x509_t *this) +{ + return this->crlDistributionPoints->create_iterator(this->crlDistributionPoints, TRUE); +} + +/** + * Implements x509_t.create_crluri_iterator + */ +static iterator_t *create_ocspuri_iterator(const private_x509_t *this) +{ + return this->ocspAccessLocations->create_iterator(this->ocspAccessLocations, TRUE); +} + +/** + * Implements x509_t.verify + */ +static bool verify(const private_x509_t *this, const rsa_public_key_t *signer) +{ + return signer->verify_emsa_pkcs1_signature(signer, this->tbsCertificate, this->signature) == SUCCESS; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_x509_t *this = *((private_x509_t**)(args[0])); + iterator_t *iterator; + bool utc = TRUE; + int written = 0; + + if (info->alt) + { + utc = *((bool*)(args[1])); + } + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + /* determine the current time */ + time_t now = time(NULL); + + written += fprintf(stream, "%#T\n", &this->installed, utc); + + if (this->subjectAltNames->get_count(this->subjectAltNames)) + { + identification_t *subjectAltName; + bool first = TRUE; + + written += fprintf(stream, " altNames: "); + iterator = this->subjectAltNames->create_iterator(this->subjectAltNames, TRUE); + while (iterator->iterate(iterator, (void**)&subjectAltName)) + { + if (first) + { + first = FALSE; + } + else + { + written += fprintf(stream, ", "); + } + written += fprintf(stream, "'%D'", subjectAltName); + } + iterator->destroy(iterator); + written += fprintf(stream, "\n"); + } + written += fprintf(stream, " subject: '%D'\n", this->subject); + written += fprintf(stream, " issuer: '%D'\n", this->issuer); + written += fprintf(stream, " serial: %#B\n", &this->serialNumber); + written += fprintf(stream, " validity: not before %#T, ", &this->notBefore, utc); + if (now < this->notBefore) + { + written += fprintf(stream, "not valid yet (valid in %V)\n", &now, &this->notBefore); + } + else + { + written += fprintf(stream, "ok\n"); + } + + written += fprintf(stream, " not after %#T, ", &this->notAfter, utc); + if (now > this->notAfter) + { + written += fprintf(stream, "expired (%V ago)\n", &now, &this->notAfter); + } + else + { + written += fprintf(stream, "ok"); + if (now > this->notAfter - CERT_WARNING_INTERVAL * 60 * 60 * 24) + { + written += fprintf(stream, " (expires in %V)", &now, &this->notAfter); + } + written += fprintf(stream, " \n"); + } + + { + chunk_t keyid = this->public_key->get_keyid(this->public_key); + written += fprintf(stream, " keyid: %#B\n", &keyid); + } + + if (this->subjectKeyID.ptr) + { + written += fprintf(stream, " subjkey: %#B\n", &this->subjectKeyID); + } + if (this->authKeyID.ptr) + { + written += fprintf(stream, " authkey: %#B\n", &this->authKeyID); + } + if (this->authKeySerialNumber.ptr) + { + written += fprintf(stream, " aserial: %#B\n", &this->authKeySerialNumber); + } + + written += fprintf(stream, " pubkey: RSA %d bits", BITS_PER_BYTE * + this->public_key->get_keysize(this->public_key)); + written += fprintf(stream, ", status %N", + cert_status_names, this->status); + + switch (this->status) + { + case CERT_GOOD: + written += fprintf(stream, " until %#T", &this->until, utc); + break; + case CERT_REVOKED: + written += fprintf(stream, " on %#T", &this->until, utc); + break; + case CERT_UNKNOWN: + case CERT_UNDEFINED: + case CERT_UNTRUSTED: + default: + break; + } + return written; +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_X509, print, arginfo_ptr_alt_ptr_int); +} + +/** + * Implements x509_t.destroy + */ +static void destroy(private_x509_t *this) +{ + this->subjectAltNames->destroy_offset(this->subjectAltNames, + offsetof(identification_t, destroy)); + this->crlDistributionPoints->destroy_offset(this->crlDistributionPoints, + offsetof(identification_t, destroy)); + this->ocspAccessLocations->destroy_offset(this->ocspAccessLocations, + offsetof(identification_t, destroy)); + DESTROY_IF(this->issuer); + DESTROY_IF(this->subject); + DESTROY_IF(this->public_key); + free(this->subjectKeyID.ptr); + free(this->certificate.ptr); + free(this); +} + +/* + * Described in header. + */ +x509_t *x509_create_from_chunk(chunk_t chunk, u_int level) +{ + private_x509_t *this = malloc_thing(private_x509_t); + + /* initialize */ + this->subjectPublicKey = chunk_empty; + this->public_key = NULL; + this->subject = NULL; + this->issuer = NULL; + this->subjectAltNames = linked_list_create(); + this->crlDistributionPoints = linked_list_create(); + this->ocspAccessLocations = linked_list_create(); + this->subjectKeyID = chunk_empty; + this->authKeyID = chunk_empty; + this->authKeySerialNumber = chunk_empty; + this->authority_flags = AUTH_NONE; + + /* public functions */ + this->public.equals = (bool (*) (const x509_t*,const x509_t*))equals; + this->public.equals_subjectAltName = (bool (*) (const x509_t*,identification_t*))equals_subjectAltName; + this->public.is_issuer = (bool (*) (const x509_t*,const x509_t*))is_issuer; + this->public.is_valid = (err_t (*) (const x509_t*,time_t*))is_valid; + this->public.is_ca = (bool (*) (const x509_t*))is_ca; + this->public.is_self_signed = (bool (*) (const x509_t*))is_self_signed; + this->public.is_ocsp_signer = (bool (*) (const x509_t*))is_ocsp_signer; + this->public.get_certificate = (chunk_t (*) (const x509_t*))get_certificate; + this->public.get_public_key = (rsa_public_key_t* (*) (const x509_t*))get_public_key; + this->public.get_serialNumber = (chunk_t (*) (const x509_t*))get_serialNumber; + this->public.get_subjectKeyID = (chunk_t (*) (const x509_t*))get_subjectKeyID; + this->public.get_keyid = (chunk_t (*) (const x509_t*))get_keyid; + this->public.get_issuer = (identification_t* (*) (const x509_t*))get_issuer; + this->public.get_subject = (identification_t* (*) (const x509_t*))get_subject; + this->public.set_until = (void (*) (x509_t*,time_t))set_until; + this->public.get_until = (time_t (*) (const x509_t*))get_until; + this->public.set_status = (void (*) (x509_t*,cert_status_t))set_status; + this->public.get_status = (cert_status_t (*) (const x509_t*))get_status; + this->public.add_authority_flags = (void (*) (x509_t*,u_int))add_authority_flags; + this->public.get_authority_flags = (u_int (*) (x509_t*))get_authority_flags; + this->public.has_authority_flag = (bool (*) (x509_t*,u_int))has_authority_flag; + this->public.create_crluri_iterator = (iterator_t* (*) (const x509_t*))create_crluri_iterator; + this->public.create_ocspuri_iterator = (iterator_t* (*) (const x509_t*))create_ocspuri_iterator; + this->public.verify = (bool (*) (const x509_t*,const rsa_public_key_t*))verify; + this->public.destroy = (void (*) (x509_t*))destroy; + + if (!parse_certificate(chunk, level, this)) + { + destroy(this); + return NULL; + } + + /* extract public key from certificate */ + this->public_key = rsa_public_key_create_from_chunk(this->subjectPublicKey); + if (this->public_key == NULL) + { + destroy(this); + return NULL; + } + /* set trusted lifetime of public key to notAfter */ + this->status = is_self_signed(this)? CERT_GOOD:CERT_UNDEFINED; + this->until = this->notAfter; + return &this->public; +} + +/* + * Described in header. + */ +x509_t *x509_create_from_file(const char *filename, const char *label) +{ + bool pgp = FALSE; + chunk_t chunk = chunk_empty; + x509_t *cert = NULL; + char cert_label[BUF_LEN]; + + snprintf(cert_label, BUF_LEN, "%s certificate", label); + + if (!pem_asn1_load_file(filename, NULL, cert_label, &chunk, &pgp)) + return NULL; + + cert = x509_create_from_chunk(chunk, 0); + + if (cert == NULL) + free(chunk.ptr); + return cert; +} diff --git a/src/libstrongswan/crypto/x509.h b/src/libstrongswan/crypto/x509.h new file mode 100755 index 000000000..a949d99d2 --- /dev/null +++ b/src/libstrongswan/crypto/x509.h @@ -0,0 +1,290 @@ +/** + * @file x509.h + * + * @brief Interface of x509_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi, Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef X509_H_ +#define X509_H_ + +typedef struct x509_t x509_t; + +#include +#include +#include +#include +#include + +/* authority flags */ + +#define AUTH_NONE 0x00 /* no authorities */ +#define AUTH_CA 0x01 /* certification authority */ +#define AUTH_AA 0x02 /* authorization authority */ +#define AUTH_OCSP 0x04 /* ocsp signing authority */ + +/** + * @brief X.509 certificate. + * + * @b Constructors: + * - x509_create_from_chunk() + * - x509_create_from_file() + * + * @todo more code cleanup needed! + * @todo fix unimplemented functions... + * @todo handle memory management + * + * @ingroup transforms + */ +struct x509_t { + + /** + * @brief Set trusted public key life. + * + * @param this calling object + * @param until time until public key is trusted + */ + void (*set_until) (x509_t *this, time_t until); + + /** + * @brief Get trusted public key life. + * + * @param this calling object + * @return time until public key is trusted + */ + time_t (*get_until) (const x509_t *this); + + /** + * @brief Set the certificate status + * + * @param this calling object + * @param status certificate status + */ + void (*set_status) (x509_t *this, cert_status_t status); + + /** + * @brief Get the certificate status + * + * @param this calling object + * @return certificate status + */ + cert_status_t (*get_status) (const x509_t *this); + + /** + * @brief Add authority flags + * + * @param this calling object + * @param flag flags to be added + */ + void (*add_authority_flags) (x509_t *this, u_int flags); + + /** + * @brief Get authority flags + * + * @param this calling object + * @return authority flags + */ + u_int (*get_authority_flags) (x509_t *this); + + /** + * @brief Check a specific authority flag + * + * @param this calling object + * @param flag flag to be checked + * @return TRUE if flag is present + */ + bool (*has_authority_flag) (x509_t *this, u_int flag); + + /** + * @brief Get the DER-encoded X.509 certificate body + * + * @param this calling object + * @return DER-encoded X.509 certificate + */ + chunk_t (*get_certificate) (const x509_t *this); + + /** + * @brief Get the RSA public key from the certificate. + * + * @param this calling object + * @return public_key + */ + rsa_public_key_t *(*get_public_key) (const x509_t *this); + + /** + * @brief Get serial number from the certificate. + * + * @param this calling object + * @return serialNumber + */ + chunk_t (*get_serialNumber) (const x509_t *this); + + /** + * @brief Get subjectKeyID from the certificate. + * + * @param this calling object + * @return subjectKeyID + */ + chunk_t (*get_subjectKeyID) (const x509_t *this); + + /** + * @brief Get keyid from the certificate's public key. + * + * @param this calling object + * @return keyid + */ + chunk_t (*get_keyid) (const x509_t *this); + + /** + * @brief Get the certificate issuer's ID. + * + * The resulting ID is always a identification_t + * of type ID_DER_ASN1_DN. + * + * @param this calling object + * @return issuers ID + */ + identification_t *(*get_issuer) (const x509_t *this); + + /** + * @brief Get the subjectDistinguisheName. + * + * The resulting ID is always a identification_t + * of type ID_DER_ASN1_DN. + * + * @param this calling object + * @return subjects ID + */ + identification_t *(*get_subject) (const x509_t *this); + + /** + * @brief Create an iterator for the crlDistributionPoints. + * + * @param this calling object + * @return iterator for crlDistributionPoints + */ + iterator_t *(*create_crluri_iterator) (const x509_t *this); + + /** + * @brief Create an iterator for the ocspAccessLocations. + * + * @param this calling object + * @return iterator for ocspAccessLocations + */ + iterator_t *(*create_ocspuri_iterator) (const x509_t *this); + + /** + * @brief Check if a certificate is trustworthy + * + * @param this calling object + * @param signer signer's RSA public key + */ + bool (*verify) (const x509_t *this, const rsa_public_key_t *signer); + + /** + * @brief Compare two certificates. + * + * Comparison is done via the certificates signature. + * + * @param this first cert for compare + * @param other second cert for compare + * @return TRUE if signature is equal + */ + bool (*equals) (const x509_t *this, const x509_t *that); + + /** + * @brief Checks if the certificate contains a subjectAltName equal to id. + * + * @param this certificate being examined + * @param id id which is being compared to the subjectAltNames + * @return TRUE if a match is found + */ + bool (*equals_subjectAltName) (const x509_t *this, identification_t *id); + + /** + * @brief Checks if the subject of the other cert is the issuer of this cert. + * + * @param this certificate + * @param issuer potential issuer certificate + * @return TRUE if issuer is found + */ + bool (*is_issuer) (const x509_t *this, const x509_t *issuer); + + /** + * @brief Checks the validity interval of the certificate + * + * @param this certificate being examined + * @param until until = min(until, notAfter) + * @return NULL if the certificate is valid + */ + err_t (*is_valid) (const x509_t *this, time_t *until); + + /** + * @brief Returns the CA basic constraints flag + * + * @param this certificate being examined + * @return TRUE if the CA flag is set + */ + bool (*is_ca) (const x509_t *this); + + /** + * @brief Returns the OCSPSigner extended key usage flag + * + * @param this certificate being examined + * @return TRUE if the OCSPSigner flag is set + */ + bool (*is_ocsp_signer) (const x509_t *this); + + /** + * @brief Checks if the certificate is self-signed (subject equals issuer) + * + * @param this certificate being examined + * @return TRUE if self-signed + */ + bool (*is_self_signed) (const x509_t *this); + + /** + * @brief Destroys the certificate. + * + * @param this certificate to destroy + */ + void (*destroy) (x509_t *this); +}; + +/** + * @brief Read a x509 certificate from a DER encoded blob. + * + * @param chunk chunk containing DER encoded data + * @return created x509_t certificate, or NULL if invlid. + * + * @ingroup transforms + */ +x509_t *x509_create_from_chunk(chunk_t chunk, u_int level); + +/** + * @brief Read a x509 certificate from a DER encoded file. + * + * @param filename file containing DER encoded data + * @param label label describing kind of certificate + * @return created x509_t certificate, or NULL if invalid. + * + * @ingroup transforms + */ +x509_t *x509_create_from_file(const char *filename, const char *label); + +#endif /* X509_H_ */ diff --git a/src/libstrongswan/debug.c b/src/libstrongswan/debug.c new file mode 100644 index 000000000..996cae502 --- /dev/null +++ b/src/libstrongswan/debug.c @@ -0,0 +1,41 @@ +/** + * @file library.c + * + * @brief Logging functions for the library. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "debug.h" + +/** + * default dbg function which printf all to stderr + */ +static void dbg_stderr(int level, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); +} + +void (*dbg) (int level, char *fmt, ...) = dbg_stderr; diff --git a/src/libstrongswan/debug.h b/src/libstrongswan/debug.h new file mode 100644 index 000000000..c424a1c11 --- /dev/null +++ b/src/libstrongswan/debug.h @@ -0,0 +1,60 @@ +/** + * @file log.h + * + * @brief Logging functions for the library. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 4 +#endif /* DEBUG_LEVEL */ + +/** debug macros, they call the dbg function hook */ +#if DEBUG_LEVEL >= 1 +# define DBG1(fmt, ...) dbg(1, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 2 +# define DBG2(fmt, ...) dbg(2, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 3 +# define DBG3(fmt, ...) dbg(3, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 4 +# define DBG4(fmt, ...) dbg(4, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ + +#ifndef DBG1 +# define DBG1(...) {} +#endif +#ifndef DBG2 +# define DBG2(...) {} +#endif +#ifndef DBG3 +# define DBG3(...) {} +#endif +#ifndef DBG4 +# define DBG4(...) {} +#endif + +/** dbg function hook, uses stderr logger by default */ +extern void (*dbg) (int level, char *fmt, ...); + +#endif /* DEBUG_H_ */ diff --git a/src/libstrongswan/enum.c b/src/libstrongswan/enum.c new file mode 100644 index 000000000..ade7c16a1 --- /dev/null +++ b/src/libstrongswan/enum.c @@ -0,0 +1,73 @@ +/** + * @file library.c + * + * @brief enum value to string conversion functions. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "enum.h" + +#include + +/** + * get the name of an enum value in a enum_name_t list + */ +static char *enum_name(enum_name_t *e, int val) +{ + do + { + if (val >= e->first && val <= e->last) + { + return e->names[val - e->first]; + } + } + while ((e = e->next)); + return NULL; +} + +/** + * output handler in printf() for enum names + */ +static int print_enum(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + enum_name_t *ed = *((enum_name_t**)(args[0])); + int val = *((int*)(args[1])); + + char *name = enum_name(ed, val); + + if (name == NULL) + { + return fprintf(stream, "(%d)", val); + } + else + { + return fprintf(stream, "%s", name); + } +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_ENUM, print_enum, arginfo_ptr_int); +} diff --git a/src/libstrongswan/enum.h b/src/libstrongswan/enum.h new file mode 100644 index 000000000..cd06e424b --- /dev/null +++ b/src/libstrongswan/enum.h @@ -0,0 +1,106 @@ +/** + * @file enum.h + * + * @brief enum value to string conversion functions. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef ENUM_H_ +#define ENUM_H_ + +typedef struct enum_name_t enum_name_t; + +/** + * @brief Struct to store names for enums. + * + * To print the string representation of enumeration values, the strings + * are stored in these structures. Every enum_name contains a range + * of strings, multiple ranges are linked together. + * Use the convenience macros to define these linked ranges. + * + * For a single range, use: + * ENUM(name, first, last, string1, string2, ...) + * + * For multiple linked ranges, use: + * ENUM_BEGIN(name, first, last, string1, string2, ...) + * ENUM_NEXT(name, first, last, last_from_previous, string3, ...) + * ENUM_NEXT(name, first, last, last_from_previous, string4, ...) + * ENUM_END(name, last_from_previous) + * + * The ENUM and the ENUM_END define a enum_name_t pointer with the name supplied + * in "name". + * + * Resolving of enum names is done using a printf hook. A printf fromat + * character %N is replaced by the enum string. Printf needs two arguments to + * resolve a %N, the enum_name_t* (the defined name in ENUM_BEGIN) followed + * by the numerical enum value. + */ +struct enum_name_t { + /** value of the first enum string */ + int first; + /** value of the last enum string */ + int last; + /** next enum_name_t in list */ + enum_name_t *next; + /** array of strings containing names from first to last */ + char *names[]; +}; + +/** + * @brief Begin a new enum_name list. + * + * @param name name of the enum_name list + * @param first enum value of the first enum string + * @param last enum value of the last enum string + * @param ... a list of strings + */ +#define ENUM_BEGIN(name, first, last, ...) static enum_name_t name##last = {first, last, NULL, { __VA_ARGS__ }} + +/** + * @brief Continue a enum name list startetd with ENUM_BEGIN. + * + * @param name name of the enum_name list + * @param first enum value of the first enum string + * @param last enum value of the last enum string + * @param prev enum value of the "last" defined in ENUM_BEGIN/previous ENUM_NEXT + * @param ... a list of strings + */ +#define ENUM_NEXT(name, first, last, prev, ...) static enum_name_t name##last = {first, last, &name##prev, { __VA_ARGS__ }} + +/** + * @brief Complete enum name list started with ENUM_BEGIN. + * + * @param name name of the enum_name list + * @param prev enum value of the "last" defined in ENUM_BEGIN/previous ENUM_NEXT + */ +#define ENUM_END(name, prev) enum_name_t *name = &name##prev; + +/** + * @brief Define a enum name with only one range. + * + * This is a convenience macro to use when a enum_name list contains only + * one range, and is equal as defining ENUM_BEGIN followed by ENUM_END. + * + * @param name name of the enum_name list + * @param first enum value of the first enum string + * @param last enum value of the last enum string + * @param ... a list of strings + */ +#define ENUM(name, first, last, ...) ENUM_BEGIN(name, first, last, __VA_ARGS__); ENUM_END(name, last) + +#endif /* ENUM_H_ */ diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c new file mode 100644 index 000000000..9f96d119c --- /dev/null +++ b/src/libstrongswan/library.c @@ -0,0 +1,184 @@ +/** + * @file library.c + * + * @brief Helper functions and definitions. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "library.h" + +#include + +ENUM(status_names, SUCCESS, DESTROY_ME, + "SUCCESS", + "FAILED", + "OUT_OF_RES", + "ALREADY_DONE", + "NOT_SUPPORTED", + "INVALID_ARG", + "NOT_FOUND", + "PARSE_ERROR", + "VERIFY_ERROR", + "INVALID_STATE", + "DESTROY_ME", + "NEED_MORE", +); + +/** + * Described in header. + */ +void *clalloc(void * pointer, size_t size) +{ + void *data; + data = malloc(size); + + memcpy(data, pointer,size); + + return (data); +} + +/** + * Described in header. + */ +void memxor(u_int8_t dest[], u_int8_t src[], size_t n) +{ + size_t i; + for (i = 0; i < n; i++) + { + dest[i] ^= src[i]; + } +} + +/** + * We use a single mutex for all refcount variables. This + * is not optimal for performance, but the critical section + * is not that long... + * TODO: Consider to include a mutex in each refcount_t variable. + */ +static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Described in header. + * + * TODO: May be implemented with atomic CPU instructions + * instead of a mutex. + */ +void ref_get(refcount_t *ref) +{ + pthread_mutex_lock(&ref_mutex); + (*ref)++; + pthread_mutex_unlock(&ref_mutex); +} + +/** + * Described in header. + * + * TODO: May be implemented with atomic CPU instructions + * instead of a mutex. + */ +bool ref_put(refcount_t *ref) +{ + bool more_refs; + + pthread_mutex_lock(&ref_mutex); + more_refs = --(*ref); + pthread_mutex_unlock(&ref_mutex); + return !more_refs; +} + +/** + * output handler in printf() for time_t + */ +static int print_time(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + static const char* months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + time_t *time = *((time_t**)(args[0])); + bool utc = TRUE; + struct tm t; + + if (info->alt) + { + utc = *((bool*)(args[1])); + } + if (time == UNDEFINED_TIME) + { + return fprintf(stream, "--- -- --:--:--%s----", + info->alt ? " UTC " : " "); + } + if (utc) + { + gmtime_r(time, &t); + } + else + { + localtime_r(time, &t); + } + return fprintf(stream, "%s %02d %02d:%02d:%02d%s%04d", + months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, utc ? " UTC " : " ", t.tm_year + 1900); +} + +/** + * output handler in printf() for time deltas + */ +static int print_time_delta(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + time_t *start = *((time_t**)(args[0])); + time_t *end = *((time_t**)(args[1])); + u_int delta = abs(*end - *start); + + char* unit = "second"; + + if (delta > 2 * 60 * 60 * 24) + { + delta /= 60 * 60 * 24; + unit = "day"; + } + else if (delta > 2 * 60 * 60) + { + delta /= 60 * 60; + unit = "hour"; + } + else if (delta > 2 * 60) + { + delta /= 60; + unit = "minute"; + } + return fprintf(stream, "%d %s%s", delta, unit, (delta == 1)? "":"s"); +} + +/** + * register printf() handlers for time_t + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_TIME, print_time, arginfo_ptr_alt_ptr_int); + register_printf_function(PRINTF_TIME_DELTA, print_time_delta, arginfo_ptr_ptr); +} diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h new file mode 100644 index 000000000..7c7f087f0 --- /dev/null +++ b/src/libstrongswan/library.h @@ -0,0 +1,301 @@ +/** + * @file library.h + * + * @brief Helper functions and definitions. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef LIBRARY_H_ +#define LIBRARY_H_ + +/** + * @defgroup libstrongswan libstrongswan + * + * libstrongswan: library with various crypto related things. + */ + +/** + * @defgroup asn1 asn1 + * + * ASN1 definitions, parser and generator functions. + * + * @ingroup libstrongswan + */ + +/** + * @defgroup crypto crypto + * + * Crypto algorithms of different kind. + * + * @ingroup libstrongswan + */ + +/** + * @defgroup crypters crypters + * + * Symmetric encryption algorithms, used for + * encryption and decryption. + * + * @ingroup crypto + */ + +/** + * @defgroup hashers hashers + * + * Hashing algorithms, such as MD5 or SHA1 + * + * @ingroup crypto + */ + +/** + * @defgroup prfs prfs + * + * Pseudo random functions, used to generate + * pseude random byte sequences. + * + * @ingroup crypto + */ + +/** + * @defgroup rsa rsa + * + * RSA private/public key algorithm. + * + * @ingroup crypto + */ + +/** + * @defgroup signers signers + * + * Symmetric signing algorithms, + * used to ensure message integrity. + * + * @ingroup crypto + */ + +/** + * @defgroup utils utils + * + * Generic helper classes. + * + * @ingroup libstrongswan + */ + +#include +#include +#include +#include +#include + +#include + +/** + * Number of bits in a byte + */ +#define BITS_PER_BYTE 8 + +/** + * Default length for various auxiliary text buffers + */ +#define BUF_LEN 512 + +/** + * Macro compares two strings for equality + */ +#define streq(x,y) (strcmp(x, y) == 0) + +/** + * Macro compares two binary blobs for equality + */ +#define memeq(x,y,len) (memcmp(x, y, len) == 0) + +/** + * Macro gives back larger of two values. + */ +#define max(x,y) ((x) > (y) ? (x):(y)) + +/** + * Macro gives back smaller of two values. + */ +#define min(x,y) ((x) < (y) ? (x):(y)) + +/** + * Call destructor of a object if object != NULL + */ +#define DESTROY_IF(obj) if (obj) obj->destroy(obj) + +/** + * Debug macro to follow control flow + */ +#define POS printf("%s, line %d\n", __FILE__, __LINE__) + +/** + * Macro to allocate a sized type. + */ +#define malloc_thing(thing) ((thing*)malloc(sizeof(thing))) + +/** + * Assign a function as a class method + */ +#define ASSIGN(method, function) (method = (typeof(method))function) + +/** + * time_t not defined + */ +#define UNDEFINED_TIME 0 + +/** + * General purpose boolean type. + */ +typedef int bool; +#define FALSE 0 +#define TRUE 1 + +typedef enum status_t status_t; + +/** + * Return values of function calls. + */ +enum status_t { + /** + * Call succeeded. + */ + SUCCESS, + + /** + * Call failed. + */ + FAILED, + + /** + * Out of resources. + */ + OUT_OF_RES, + + /** + * The suggested operation is already done + */ + ALREADY_DONE, + + /** + * Not supported. + */ + NOT_SUPPORTED, + + /** + * One of the arguments is invalid. + */ + INVALID_ARG, + + /** + * Something could not be found. + */ + NOT_FOUND, + + /** + * Error while parsing. + */ + PARSE_ERROR, + + /** + * Error while verifying. + */ + VERIFY_ERROR, + + /** + * Object in invalid state. + */ + INVALID_STATE, + + /** + * Destroy object which called method belongs to. + */ + DESTROY_ME, + + /** + * Another call to the method is required. + */ + NEED_MORE, +}; + +/** + * enum_names for type status_t. + */ +extern enum_name_t *status_names; + +/** + * deprecated pluto style return value: + * error message, NULL for success + */ +typedef const char *err_t; + +/** + * Handle struct timeval like an own type. + */ +typedef struct timeval timeval_t; + +/** + * Handle struct timespec like an own type. + */ +typedef struct timespec timespec_t; + +/** + * Handle struct chunk_t like an own type. + */ +typedef struct sockaddr sockaddr_t; + +/** + * Clone a data to a newly allocated buffer + */ +void *clalloc(void *pointer, size_t size); + +/** + * Same as memcpy, but XORs src into dst instead of copy + */ +void memxor(u_int8_t dest[], u_int8_t src[], size_t n); + +/** + * Special type to count references + */ +typedef volatile u_int refcount_t; + +/** + * @brief Get a new reference. + * + * Increments the reference counter atomic. + * + * @param ref pointer to ref counter + */ +void ref_get(refcount_t *ref); + +/** + * @brief Put back a unused reference. + * + * Decrements the reference counter atomic and + * says if more references available. + * + * @param ref pointer to ref counter + * @return TRUE if no more references counted + */ +bool ref_put(refcount_t *ref); + + +#include +#include + +#endif /* LIBRARY_H_ */ diff --git a/src/libstrongswan/printf_hook.c b/src/libstrongswan/printf_hook.c new file mode 100644 index 000000000..0407e8c82 --- /dev/null +++ b/src/libstrongswan/printf_hook.c @@ -0,0 +1,118 @@ +/** + * @file printf_hook.c + * + * @brief Printf hook definitions and arginfo functions. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include "printf_hook.h" + +/** + * arginfo handler in printf() pointer + */ +int arginfo_ptr(const struct printf_info *info, size_t n, int *argtypes) +{ + if (n > 0) + { + argtypes[0] = PA_POINTER; + } + return 1; +} + +/** + * arginfo handler for two prt arguments + */ +int arginfo_ptr_ptr(const struct printf_info *info, size_t n, int *argtypes) +{ + if (n > 1) + { + argtypes[0] = PA_POINTER; + argtypes[1] = PA_POINTER; + } + return 2; +} + +/** + * arginfo handler for one ptr, one int + */ +int arginfo_ptr_int(const struct printf_info *info, size_t n, int *argtypes) +{ + if (n > 1) + { + argtypes[0] = PA_POINTER; + argtypes[1] = PA_INT; + } + return 2; +} + +/** + * arginfo handler for two int arguments + */ +int arginfo_int_int(const struct printf_info *info, size_t n, int *argtypes) +{ + if (n > 1) + { + argtypes[0] = PA_INT; + argtypes[1] = PA_INT; + } + return 2; +} + +/** + * special arginfo handler respecting alt flag + */ +int arginfo_int_alt_int_int(const struct printf_info *info, size_t n, int *argtypes) +{ + if (info->alt) + { + if (n > 1) + { + argtypes[0] = PA_INT; + argtypes[1] = PA_INT; + } + return 2; + } + + if (n > 0) + { + argtypes[0] = PA_INT; + } + return 1; +} + +/** + * special arginfo handler respecting alt flag + */ +int arginfo_ptr_alt_ptr_int(const struct printf_info *info, size_t n, int *argtypes) +{ + if (info->alt) + { + if (n > 1) + { + argtypes[0] = PA_POINTER; + argtypes[1] = PA_INT; + } + return 2; + } + + if (n > 0) + { + argtypes[0] = PA_POINTER; + } + return 1; +} diff --git a/src/libstrongswan/printf_hook.h b/src/libstrongswan/printf_hook.h new file mode 100644 index 000000000..45184a8f0 --- /dev/null +++ b/src/libstrongswan/printf_hook.h @@ -0,0 +1,76 @@ +/** + * @file printf_hook.h + * + * @brief Printf hook definitions and arginfo functions. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef PRINTF_HOOK_H_ +#define PRINTF_HOOK_H_ + +#include + +/** + * Printf() hook characters. + * We define all characters here to have them on a central place. + */ + +/** 2 arguments: u_char *buffer, int size */ +#define PRINTF_BYTES 'b' +/** 1 argument: chunk_t *chunk; use #-modifier to print inline */ +#define PRINTF_CHUNK 'B' +/** 1 argument: identification_t *id */ +#define PRINTF_IDENTIFICATION 'D' +/** 1 argumnet: host_t *host; use #-modifier to include port number */ +#define PRINTF_HOST 'H' +/** 1 argument: ike_sa_id_t *id */ +#define PRINTF_IKE_SA_ID 'J' +/** 1 argument: ike_sa_t *ike_sa */ +#define PRINTF_IKE_SA 'K' +/** 1 argument: message_t *message */ +#define PRINTF_MESSAGE 'M' +/** 2 arguments: enum_name_t *name, long value */ +#define PRINTF_ENUM 'N' +/** 1 argument: child_sa_t *child_sa */ +#define PRINTF_CHILD_SA 'P' +/** 1 argument: traffic_selector_t *ts */ +#define PRINTF_TRAFFIC_SELECTOR 'R' +/** 1 argument: time_t *time; with #-modifier 2 arguments: time_t *time, bool utc */ +#define PRINTF_TIME 'T' +/** 1 argument: x509_t *cert; with #-modifier 2 arguments: x509_t *cert, bool utc */ +#define PRINTF_X509 'Q' +/** 1 argument: crl_t *crl; with #-modifier 2 arguments: crl_t *crl, bool utc */ +#define PRINTF_CRL 'U' +/** 2 arguments: time_t *begin, time_t *end */ +#define PRINTF_TIME_DELTA 'V' +/** 1 argument: ca_info_t *ca_info; with #-modifier 2 arguments: ca_info_t *ca_info, bool utc */ +#define PRINTF_CAINFO 'W' +/** 1 argument: certinfo_t *certinfo; with #-modifier 2 arguments: certinfo_t *certinfo, bool utc */ +#define PRINTF_CERTINFO 'Y' + +/** + * Generic arginfo handlers for printf() hooks + */ +int arginfo_ptr(const struct printf_info *info, size_t n, int *argtypes); +int arginfo_ptr_ptr(const struct printf_info *info, size_t n, int *argtypes); +int arginfo_ptr_int(const struct printf_info *info, size_t n, int *argtypes); +int arginfo_int_int(const struct printf_info *info, size_t n, int *argtypes); +int arginfo_ptr_alt_ptr_int(const struct printf_info *info, size_t n, int *argtypes); +int arginfo_int_alt_int_int(const struct printf_info *info, size_t n, int *argtypes); + +#endif /* PRINTF_HOOK_H_ */ diff --git a/src/libstrongswan/utils/fetcher.c b/src/libstrongswan/utils/fetcher.c new file mode 100644 index 000000000..6165cc1e1 --- /dev/null +++ b/src/libstrongswan/utils/fetcher.c @@ -0,0 +1,421 @@ +/** + * @file fetcher.c + * + * @brief Implementation of fetcher_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifdef LIBCURL +#include +#endif /* LIBCURL */ + +#ifdef LIBLDAP +#include +#endif /* LIBLDAP */ + +#include +#include + +#include "fetcher.h" + +typedef struct private_fetcher_t private_fetcher_t; + +/** + * @brief Private Data of a fetcher_t object. + */ +struct private_fetcher_t { + /** + * Public data + */ + fetcher_t public; + + /** + * URI of the information source + */ + const char *uri; + +#ifdef LIBCURL + /** + * we use libcurl from http://curl.haxx.se/ as a fetcher + */ + CURL* curl; +#endif /* LIBCURL */ + +#ifdef LIBLDAP + /** + * we use libldap from http://www.openssl.org/ as a fetcher + */ + LDAP *ldap; + LDAPURLDesc *lurl; +#endif /* LIBLDAP */ +}; + +/** + * writes data into a dynamically resizeable chunk_t + * needed for libcurl responses + */ +static size_t curl_write_buffer(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + chunk_t *mem = (chunk_t*)data; + + mem->ptr = (u_char *)realloc(mem->ptr, mem->len + realsize); + if (mem->ptr) { + memcpy(&(mem->ptr[mem->len]), ptr, realsize); + mem->len += realsize; + } + return realsize; +} + +/** + * Implements fetcher_t.get for curl methods + */ +static chunk_t curl_get(private_fetcher_t *this) +{ + chunk_t response = chunk_empty; + +#ifdef LIBCURL + if (this->curl) + { + CURLcode res; + chunk_t curl_response = chunk_empty; + char curl_error_buffer[CURL_ERROR_SIZE]; + + curl_easy_setopt(this->curl, CURLOPT_URL, this->uri); + curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, curl_write_buffer); + curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)&curl_response); + curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, &curl_error_buffer); + curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE); + curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, FETCHER_TIMEOUT); + curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE); + + DBG1("sending curl request to '%s'...", this->uri); + res = curl_easy_perform(this->curl); + + if (res == CURLE_OK) + { + DBG1("received valid curl response"); + response = chunk_clone(curl_response); + } + else + { + DBG1("curl request failed: %s", curl_error_buffer); + } + curl_free(curl_response.ptr); + } +#else + DBG1("warning: libcurl fetching not compiled in"); +#endif /* LIBCURL */ + return response; +} + +/** + * Implements fetcher_t.post. + */ +static chunk_t http_post(private_fetcher_t *this, const char *request_type, chunk_t request) +{ + chunk_t response = chunk_empty; + +#ifdef LIBCURL + if (this->curl) + { + CURLcode res; + struct curl_slist *headers = NULL; + chunk_t curl_response = chunk_empty; + char curl_error_buffer[CURL_ERROR_SIZE]; + char content_type[BUF_LEN]; + + /* set content type header */ + snprintf(content_type, BUF_LEN, "Content-Type: %s", request_type); + headers = curl_slist_append(headers, content_type); + + /* set options */ + curl_easy_setopt(this->curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(this->curl, CURLOPT_URL, this->uri); + curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, curl_write_buffer); + curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)&curl_response); + curl_easy_setopt(this->curl, CURLOPT_POSTFIELDS, request.ptr); + curl_easy_setopt(this->curl, CURLOPT_POSTFIELDSIZE, request.len); + curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, &curl_error_buffer); + curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE); + curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, FETCHER_TIMEOUT); + curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE); + + DBG1("sending http post request to '%s'...", this->uri); + res = curl_easy_perform(this->curl); + + if (res == CURLE_OK) + { + DBG1("received valid http response"); + response = chunk_clone(curl_response); + } + else + { + DBG1("http post request using libcurl failed: %s", curl_error_buffer); + } + curl_slist_free_all(headers); + curl_free(curl_response.ptr); + } +#else + DBG1("warning: libcurl fetching not compiled in"); +#endif /* LIBCURL */ + return response; +} + +#ifdef LIBLDAP +/** + * Parses the result returned by an ldap query + */ +static chunk_t ldap_parse(LDAP *ldap, LDAPMessage *result) +{ + chunk_t response = chunk_empty; + err_t ugh = NULL; + + LDAPMessage *entry = ldap_first_entry(ldap, result); + + if (entry != NULL) + { + BerElement *ber = NULL; + char *attr; + + attr = ldap_first_attribute(ldap, entry, &ber); + + if (attr != NULL) + { + struct berval **values = ldap_get_values_len(ldap, entry, attr); + + if (values != NULL) + { + if (values[0] != NULL) + { + response.len = values[0]->bv_len; + response.ptr = malloc(response.len); + memcpy(response.ptr, values[0]->bv_val, response.len); + + if (values[1] != NULL) + { + ugh = "more than one value was fetched - first selected"; + } + } + else + { + ugh = "no values in attribute"; + } + ldap_value_free_len(values); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, entry, 0)); + } + ldap_memfree(attr); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, entry, 0)); + } + ber_free(ber, 0); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, result, 0)); + } + if (ugh) + { + DBG1("ldap request failed: %s", ugh); + } + return response; +} +#endif /* LIBLDAP */ + +/** + * Implements fetcher_t.get for curl methods + */ +static chunk_t ldap_get(private_fetcher_t *this) +{ + chunk_t response = chunk_empty; + +#ifdef LIBLDAP + if (this->ldap) + { + err_t ugh = NULL; + int rc; + int ldap_version = LDAP_VERSION3; + + struct timeval timeout; + + timeout.tv_sec = FETCHER_TIMEOUT; + timeout.tv_usec = 0; + + ldap_set_option(this->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); + ldap_set_option(this->ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout); + + DBG1("sending ldap request to '%s'...", this->uri); + + rc = ldap_simple_bind_s(this->ldap, NULL, NULL); + if (rc == LDAP_SUCCESS) + { + LDAPMessage *result; + + timeout.tv_sec = FETCHER_TIMEOUT; + timeout.tv_usec = 0; + + rc = ldap_search_st(this->ldap, this->lurl->lud_dn, + this->lurl->lud_scope, + this->lurl->lud_filter, + this->lurl->lud_attrs, + 0, &timeout, &result); + + if (rc == LDAP_SUCCESS) + { + response = ldap_parse(this->ldap, result); + if (response.ptr) + { + DBG1("received valid ldap response"); + } + ldap_msgfree(result); + } + else + { + ugh = ldap_err2string(rc); + } + } + else + { + ugh = ldap_err2string(rc); + } + ldap_unbind_s(this->ldap); + + if (ugh) + { + DBG1("ldap request failed: %s", ugh); + } + } +#else /* !LIBLDAP */ + DBG1("warning: libldap fetching not compiled in"); +#endif /* !LIBLDAP */ + return response; +} + +/** + * Implements fetcher_t.destroy + */ +static void destroy(private_fetcher_t *this) +{ +#ifdef LIBCURL + if (this->curl) + { + curl_easy_cleanup(this->curl); + } +#endif /* LIBCURL */ + +#ifdef LIBLDAP + if (this->lurl) + { + ldap_free_urldesc(this->lurl); + } +#endif /* LIBLDAP */ + + free(this); +} + +/* + * Described in header. + */ +fetcher_t *fetcher_create(const char *uri) +{ + private_fetcher_t *this = malloc_thing(private_fetcher_t); + + /* initialize */ + this->uri = uri; + +#ifdef LIBCURL + this->curl = NULL; +#endif /* LIBCURL */ + +#ifdef LIBLDAP + this->lurl = NULL; + this->ldap = NULL; +#endif /* LIBLDAP */ + + if (strlen(uri) >= 4 && strncasecmp(uri, "ldap", 4) == 0) + { +#ifdef LIBLDAP + int rc = ldap_url_parse(uri, &this->lurl); + + if (rc == LDAP_SUCCESS) + { + this->ldap = ldap_init(this->lurl->lud_host, + this->lurl->lud_port); + } + else + { + DBG1("ldap: %s", ldap_err2string(rc)); + this->ldap = NULL; + } +#endif /* LIBLDAP */ + this->public.get = (chunk_t (*) (fetcher_t*))ldap_get; + } + else + { +#ifdef LIBCURL + this->curl = curl_easy_init(); + if (this->curl == NULL) + { + DBG1("curl_easy_init_failed()"); + } +#endif /* LIBCURL */ + this->public.get = (chunk_t (*) (fetcher_t*))curl_get; + } + + /* public functions */ + this->public.post = (chunk_t (*) (fetcher_t*,const char*,chunk_t))http_post; + this->public.destroy = (void (*) (fetcher_t*))destroy; + + return &this->public; +} + +/** + * Described in header. + */ +void fetcher_initialize(void) +{ +#ifdef LIBCURL + CURLcode res; + + /* initialize libcurl */ + DBG1("initializing libcurl"); + res = curl_global_init(CURL_GLOBAL_NOTHING); + if (res != CURLE_OK) + { + DBG1("libcurl could not be initialized: %s", curl_easy_strerror(res)); + } +#endif /* LIBCURL */ +} + +/** + * Described in header. + */ +void fetcher_finalize(void) +{ +#ifdef LIBCURL + /* finalize libcurl */ + DBG1("finalizing libcurl"); + curl_global_cleanup(); +#endif /* LIBCURL */ +} + diff --git a/src/libstrongswan/utils/fetcher.h b/src/libstrongswan/utils/fetcher.h new file mode 100644 index 000000000..47b43a0b7 --- /dev/null +++ b/src/libstrongswan/utils/fetcher.h @@ -0,0 +1,95 @@ +/** + * @file fetcher.h + * + * @brief Interface of fetcher_t. + * + */ + +/* + * Copyright (C) 2007 Andreas Steffen + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef FETCHER_H_ +#define FETCHER_H_ + +typedef struct fetcher_t fetcher_t; + +#include + +#define FETCHER_TIMEOUT 10 /* seconds */ + +/** + * @brief Fetches information from an URI (http, file, ftp, etc.) + * + * @ingroup utils + */ +struct fetcher_t { + + /** + * @brief Get information via a get request. + * + * @param this calling object + * @param uri uri specifying the information source + * @return chunk_t containing the information + */ + chunk_t (*get) (fetcher_t *this); + + /** + * @brief Get information via a get request. + * + * @param this calling object + * @param uri uri specifying the information source + * @param type content type of http post request + * @param request binary data for http post request + * @return chunk_t containing the information + */ + chunk_t (*post) (fetcher_t *this, const char *type, chunk_t request); + + /** + * @brief Destroys the fetcher_t object. + * + * @param this fetcher_t to destroy + */ + void (*destroy) (fetcher_t *this); + +}; + +/** + * @brief Create a fetcher_t object. + * + * @return created fetcher_t object + * + * @ingroup utils + */ +fetcher_t* fetcher_create(const char *uri); + +/** + * @brief Initializes the fetcher_t class + * + * call this function only once in the main program + * + * @ingroup utils + */ +void fetcher_initialize(void); + +/** + * @brief Finalizes the fetcher_t class + * + * call this function only once befor exiting the main program + * + * @ingroup utils + */ +void fetcher_finalize(void); + +#endif /*FETCHER_H_*/ diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c new file mode 100644 index 000000000..8cbfd6ab8 --- /dev/null +++ b/src/libstrongswan/utils/host.c @@ -0,0 +1,526 @@ +/** + * @file host.c + * + * @brief Implementation of host_t. + * + */ + +/* + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include + +#include "host.h" + + +typedef struct private_host_t private_host_t; + +/** + * @brief Private Data of a host object. + */ +struct private_host_t { + /** + * Public data + */ + host_t public; + + /** + * low-lewel structure, wich stores the address + */ + union { + /** generic type */ + struct sockaddr address; + /** maximum sockaddr size */ + struct sockaddr_storage address_max; + /** IPv4 address */ + struct sockaddr_in address4; + /** IPv6 address */ + struct sockaddr_in6 address6; + }; + /** + * length of address structure + */ + socklen_t socklen; +}; + + +/** + * implements host_t.get_sockaddr + */ +static sockaddr_t *get_sockaddr(private_host_t *this) +{ + return &(this->address); +} + +/** + * implements host_t.get_sockaddr_len + */ +static socklen_t *get_sockaddr_len(private_host_t *this) +{ + return &(this->socklen); +} + +/** + * Implementation of host_t.is_anyaddr. + */ +static bool is_anyaddr(private_host_t *this) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + u_int8_t default_route[4]; + memset(default_route, 0, sizeof(default_route)); + return memeq(default_route, &(this->address4.sin_addr.s_addr), + sizeof(default_route)); + } + case AF_INET6: + { + u_int8_t default_route[16]; + memset(default_route, 0, sizeof(default_route)); + return memeq(default_route, &(this->address6.sin6_addr.s6_addr), + sizeof(default_route)); + } + default: + { + return FALSE; + } + } +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_host_t *this = *((private_host_t**)(args[0])); + char buffer[INET6_ADDRSTRLEN]; + void *address; + u_int16_t port; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + if (is_anyaddr(this)) + { + return fprintf(stream, "%%any"); + } + + switch (this->address.sa_family) + { + case AF_INET: + address = &this->address4.sin_addr; + port = this->address4.sin_port; + break; + case AF_INET6: + address = &this->address6.sin6_addr; + port = this->address6.sin6_port; + break; + default: + return fprintf(stream, "(family not supported)"); + } + + if (inet_ntop(this->address.sa_family, address, + buffer, sizeof(buffer)) == NULL) + { + return fprintf(stream, "(address conversion failed)"); + } + + if (info->alt) + { + return fprintf(stream, "%s[%d]", buffer, ntohs(port)); + } + else + { + return fprintf(stream, "%s", buffer); + } +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_HOST, print, arginfo_ptr); +} + +/** + * Implementation of host_t.get_address. + */ +static chunk_t get_address(private_host_t *this) +{ + chunk_t address = chunk_empty; + + switch (this->address.sa_family) + { + case AF_INET: + { + address.ptr = (char*)&(this->address4.sin_addr.s_addr); + address.len = 4; + return address; + } + case AF_INET6: + { + address.ptr = (char*)&(this->address6.sin6_addr.s6_addr); + address.len = 16; + return address; + } + default: + { + /* return empty chunk */ + return address; + } + } +} + +/** + * implements host_t.get_family + */ +static int get_family(private_host_t *this) +{ + return this->address.sa_family; +} + +/** + * implements host_t.get_port + */ +static u_int16_t get_port(private_host_t *this) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + return ntohs(this->address4.sin_port); + } + case AF_INET6: + { + return ntohs(this->address6.sin6_port); + } + default: + { + return 0; + } + } +} + +/** + * implements host_t.set_port + */ +static void set_port(private_host_t *this, u_int16_t port) +{ + switch (this->address.sa_family) + { + case AF_INET: + { + this->address4.sin_port = htons(port); + break; + } + case AF_INET6: + { + this->address6.sin6_port = htons(port); + break; + } + default: + { + break; + } + } +} + +/** + * Implements host_t.clone. + */ +static private_host_t *clone_(private_host_t *this) +{ + private_host_t *new = malloc_thing(private_host_t); + + memcpy(new, this, sizeof(private_host_t)); + return new; +} + +/** + * Impelements host_t.ip_equals + */ +static bool ip_equals(private_host_t *this, private_host_t *other) +{ + if (this->address.sa_family != other->address.sa_family) + { + /* 0.0.0.0 and ::0 are equal */ + if (is_anyaddr(this) && is_anyaddr(other)) + { + return TRUE; + } + + return FALSE; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + if (memeq(&this->address4.sin_addr, &other->address4.sin_addr, + sizeof(this->address4.sin_addr))) + { + return TRUE; + } + break; + } + case AF_INET6: + { + if (memeq(&this->address6.sin6_addr, &other->address6.sin6_addr, + sizeof(this->address6.sin6_addr))) + { + return TRUE; + } + } + default: + break; + } + return FALSE; +} + +/** + * Implements host_t.get_differences + */ +static host_diff_t get_differences(host_t *this, host_t *other) +{ + host_diff_t ret = HOST_DIFF_NONE; + + if (!this->ip_equals(this, other)) + { + ret |= HOST_DIFF_ADDR; + } + + if (this->get_port(this) != other->get_port(other)) + { + ret |= HOST_DIFF_PORT; + } + + return ret; +} + +/** + * Impelements host_t.equals + */ +static bool equals(private_host_t *this, private_host_t *other) +{ + if (!ip_equals(this, other)) + { + return FAILED; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + if (this->address4.sin_port == other->address4.sin_port) + { + return TRUE; + } + break; + } + case AF_INET6: + { + if (this->address6.sin6_port == other->address6.sin6_port) + { + return TRUE; + } + break; + } + default: + break; + } + return FALSE; +} + +/** + * Implements host_t.destroy + */ +static void destroy(private_host_t *this) +{ + free(this); +} + +/** + * Creates an empty host_t object + */ +static private_host_t *host_create_empty(void) +{ + private_host_t *this = malloc_thing(private_host_t); + + this->public.get_sockaddr = (sockaddr_t* (*) (host_t*))get_sockaddr; + this->public.get_sockaddr_len = (socklen_t*(*) (host_t*))get_sockaddr_len; + this->public.clone = (host_t* (*) (host_t*))clone_; + this->public.get_family = (int (*) (host_t*))get_family; + this->public.get_address = (chunk_t (*) (host_t *)) get_address; + this->public.get_port = (u_int16_t (*) (host_t *))get_port; + this->public.set_port = (void (*) (host_t *,u_int16_t))set_port; + this->public.get_differences = get_differences; + this->public.ip_equals = (bool (*) (host_t *,host_t *)) ip_equals; + this->public.equals = (bool (*) (host_t *,host_t *)) equals; + this->public.is_anyaddr = (bool (*) (host_t *)) is_anyaddr; + this->public.destroy = (void (*) (host_t*))destroy; + + return this; +} + +/* + * Described in header. + */ +host_t *host_create_from_string(char *string, u_int16_t port) +{ + private_host_t *this = host_create_empty(); + + if (strchr(string, '.')) + { + this->address.sa_family = AF_INET; + } + else + { + this->address.sa_family = AF_INET6; + } + + switch (this->address.sa_family) + { + case AF_INET: + { + if (inet_pton(AF_INET, string, &this->address4.sin_addr) <=0) + { + break; + } + this->address4.sin_port = htons(port); + this->socklen = sizeof(struct sockaddr_in); + return &this->public; + } + case AF_INET6: + { + if (inet_pton(AF_INET6, string, &this->address6.sin6_addr) <=0) + { + break; + } + this->address6.sin6_port = htons(port); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + { + break; + } + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port) +{ + private_host_t *this = host_create_empty(); + + this->address.sa_family = family; + switch (family) + { + case AF_INET: + { + if (address.len != 4) + { + break; + } + memcpy(&(this->address4.sin_addr.s_addr), address.ptr,4); + this->address4.sin_port = htons(port); + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + case AF_INET6: + { + if (address.len != 16) + { + break; + } + memcpy(&(this->address6.sin6_addr.s6_addr), address.ptr, 16); + this->address6.sin6_port = htons(port); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) +{ + private_host_t *this = host_create_empty(); + + switch (sockaddr->sa_family) + { + case AF_INET: + { + memcpy(&this->address4, sockaddr, sizeof(struct sockaddr_in)); + this->socklen = sizeof(struct sockaddr_in); + return &this->public; + } + case AF_INET6: + { + memcpy(&this->address6, sockaddr, sizeof(struct sockaddr_in6)); + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_any(int family) +{ + private_host_t *this = host_create_empty(); + + memset(&this->address_max, 0, sizeof(struct sockaddr_storage)); + this->address.sa_family = family; + + switch (family) + { + case AF_INET: + { + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + case AF_INET6: + { + this->socklen = sizeof(struct sockaddr_in6); + return &this->public; + } + default: + break; + } + return NULL; +} diff --git a/src/libstrongswan/utils/host.h b/src/libstrongswan/utils/host.h new file mode 100644 index 000000000..ee9aa457f --- /dev/null +++ b/src/libstrongswan/utils/host.h @@ -0,0 +1,231 @@ +/** + * @file host.h + * + * @brief Interface of host_t. + * + */ + +/* + * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef HOST_H_ +#define HOST_H_ + +typedef enum host_diff_t host_diff_t; +typedef struct host_t host_t; + +#include +#include +#include +#include +#include +#include + +#include + +/** + * Differences between two hosts. They differ in + * address, port, or both. + */ +enum host_diff_t { + HOST_DIFF_NONE = 0, + HOST_DIFF_ADDR = 1, + HOST_DIFF_PORT = 2, +}; + +/** + * @brief Representates a Host + * + * Host object, identifies a address:port pair and defines some + * useful functions on it. + * + * @b Constructors: + * - host_create() + * - host_create_from_chunk() + * - host_create_from_sockaddr() + * + * @todo Add IPv6 support + * + * @ingroup utils + */ +struct host_t { + + /** + * @brief Build a clone of this host object. + * + * @param this object to clone + * @return cloned host + */ + host_t *(*clone) (host_t *this); + + /** + * @brief Get a pointer to the internal sockaddr struct. + * + * This is used for sending and receiving via sockets. + * + * @param this object to clone + * @return pointer to the internal sockaddr structure + */ + sockaddr_t *(*get_sockaddr) (host_t *this); + + /** + * @brief Get the length of the sockaddr struct. + * + * Depending on the family, the length of the sockaddr struct + * is different. Use this function to get the length of the sockaddr + * struct returned by get_sock_addr. + * + * This is used for sending and receiving via sockets. + * + * @param this object to clone + * @return length of the sockaddr struct + */ + socklen_t *(*get_sockaddr_len) (host_t *this); + + /** + * @brief Gets the family of the address + * + * @param this calling object + * @return family + */ + int (*get_family) (host_t *this); + + /** + * @brief Checks if the ip address of host is set to default route. + * + * @param this calling object + * @return + * - TRUE if host has IP 0.0.0.0 for default route + * - FALSE otherwise + */ + bool (*is_anyaddr) (host_t *this); + + /** + * @brief get the address of this host as chunk_t + * + * Returned chunk points to internal data. + * + * @param this object + * @return address string, + */ + chunk_t (*get_address) (host_t *this); + + /** + * @brief get the port of this host + * + * @param this object to clone + * @return port number + */ + u_int16_t (*get_port) (host_t *this); + + /** + * @brief set the port of this host + * + * @param this object to clone + * @param port port numer + */ + void (*set_port) (host_t *this, u_int16_t port); + + /** + * @brief Compare the ips of two hosts hosts. + * + * @param this object to compare + * @param other the other to compare + * @return TRUE if addresses are equal. + */ + bool (*ip_equals) (host_t *this, host_t *other); + + /** + * @brief Compare two hosts, with port. + * + * @param this object to compare + * @param other the other to compare + * @return TRUE if addresses and ports are equal. + */ + bool (*equals) (host_t *this, host_t *other); + + /** + * @brief Compare two hosts and return the differences. + * + * @param this object to compare + * @param other the other to compare + * @return differences in a combination of host_diff_t's + */ + host_diff_t (*get_differences) (host_t *this, host_t *other); + + /** + * @brief Destroy this host object + * + * @param this calling + * @return SUCCESS in any case + */ + void (*destroy) (host_t *this); +}; + +/** + * @brief Constructor to create a host_t object from an address string. + * + * @param string string of an address, such as "152.96.193.130" + * @param port port number + * @return + * - host_t object + * - NULL, if string not an address. + * + * @ingroup network + */ +host_t *host_create_from_string(char *string, u_int16_t port); + +/** + * @brief Constructor to create a host_t object from an address chunk + * + * @param family Address family to use for this object, such as AF_INET or AF_INET6 + * @param address address as 4 byte chunk_t in networ order + * @param port port number + * @return + * - host_t object + * - NULL, if family not supported or chunk_t length not 4 bytes. + * + * @ingroup network + */ +host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port); + +/** + * @brief Constructor to create a host_t object from a sockaddr struct + * + * @param sockaddr sockaddr struct which contains family, address and port + * @return + * - host_t object + * - NULL, if family not supported. + * + * @ingroup network + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr); + +/** + * @brief Create a host without an address, a "any" host. + * + * @param family family of the any host + * @return + * - host_t object + * - NULL, if family not supported. + * + * @ingroup network + */ +host_t *host_create_any(int family); + +#endif /*HOST_H_*/ diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c new file mode 100644 index 000000000..341af39c0 --- /dev/null +++ b/src/libstrongswan/utils/identification.c @@ -0,0 +1,1144 @@ +/** + * @file identification.c + * + * @brief Implementation of identification_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "identification.h" + +#include + +ENUM_BEGIN(id_type_names, ID_ANY, ID_KEY_ID, + "ID_ANY", + "ID_IPV4_ADDR", + "ID_FQDN", + "ID_RFC822_ADDR", + "ID_IPV4_ADDR_SUBNET", + "ID_IPV6_ADDR", + "ID_IPV6_ADDR_SUBNET", + "ID_IPV4_ADDR_RANGE", + "ID_IPV6_ADDR_RANGE", + "ID_DER_ASN1_DN", + "ID_DER_ASN1_GN", + "ID_KEY_ID"); +ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_DER_ASN1_GN_URI, ID_KEY_ID, + "ID_DER_ASN1_GN_URI"); +ENUM_END(id_type_names, ID_DER_ASN1_GN_URI); + + +/** + * X.501 acronyms for well known object identifiers (OIDs) + */ +static u_char oid_ND[] = { + 0x02, 0x82, 0x06, 0x01, 0x0A, 0x07, 0x14 +}; +static u_char oid_UID[] = { + 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 +}; +static u_char oid_DC[] = { + 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 +}; +static u_char oid_CN[] = { + 0x55, 0x04, 0x03 +}; +static u_char oid_S[] = { + 0x55, 0x04, 0x04 +}; +static u_char oid_SN[] = { + 0x55, 0x04, 0x05 +}; +static u_char oid_C[] = { + 0x55, 0x04, 0x06 +}; +static u_char oid_L[] = { + 0x55, 0x04, 0x07 +}; +static u_char oid_ST[] = { + 0x55, 0x04, 0x08 +}; +static u_char oid_O[] = { + 0x55, 0x04, 0x0A +}; +static u_char oid_OU[] = { + 0x55, 0x04, 0x0B +}; +static u_char oid_T[] = { + 0x55, 0x04, 0x0C +}; +static u_char oid_D[] = { + 0x55, 0x04, 0x0D +}; +static u_char oid_N[] = { + 0x55, 0x04, 0x29 +}; +static u_char oid_G[] = { + 0x55, 0x04, 0x2A +}; +static u_char oid_I[] = { + 0x55, 0x04, 0x2B +}; +static u_char oid_ID[] = { + 0x55, 0x04, 0x2D +}; +static u_char oid_EN[] = { + 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x03, 0x01, 0x03 +}; +static u_char oid_E[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 +}; +static u_char oid_UN[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x02 +}; +static u_char oid_TCGID[] = { + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x89, 0x31, 0x01, 0x01, 0x02, 0x02, 0x4B +}; + +/** + * coding of X.501 distinguished name + */ +typedef struct { + const u_char *name; + chunk_t oid; + u_char type; +} x501rdn_t; + +static const x501rdn_t x501rdns[] = { + {"ND", {oid_ND, 7}, ASN1_PRINTABLESTRING}, + {"UID", {oid_UID, 10}, ASN1_PRINTABLESTRING}, + {"DC", {oid_DC, 10}, ASN1_PRINTABLESTRING}, + {"CN", {oid_CN, 3}, ASN1_PRINTABLESTRING}, + {"S", {oid_S, 3}, ASN1_PRINTABLESTRING}, + {"SN", {oid_SN, 3}, ASN1_PRINTABLESTRING}, + {"serialNumber", {oid_SN, 3}, ASN1_PRINTABLESTRING}, + {"C", {oid_C, 3}, ASN1_PRINTABLESTRING}, + {"L", {oid_L, 3}, ASN1_PRINTABLESTRING}, + {"ST", {oid_ST, 3}, ASN1_PRINTABLESTRING}, + {"O", {oid_O, 3}, ASN1_PRINTABLESTRING}, + {"OU", {oid_OU, 3}, ASN1_PRINTABLESTRING}, + {"T", {oid_T, 3}, ASN1_PRINTABLESTRING}, + {"D", {oid_D, 3}, ASN1_PRINTABLESTRING}, + {"N", {oid_N, 3}, ASN1_PRINTABLESTRING}, + {"G", {oid_G, 3}, ASN1_PRINTABLESTRING}, + {"I", {oid_I, 3}, ASN1_PRINTABLESTRING}, + {"ID", {oid_ID, 3}, ASN1_PRINTABLESTRING}, + {"EN", {oid_EN, 10}, ASN1_PRINTABLESTRING}, + {"employeeNumber", {oid_EN, 10}, ASN1_PRINTABLESTRING}, + {"E", {oid_E, 9}, ASN1_IA5STRING}, + {"Email", {oid_E, 9}, ASN1_IA5STRING}, + {"emailAddress", {oid_E, 9}, ASN1_IA5STRING}, + {"UN", {oid_UN, 9}, ASN1_IA5STRING}, + {"unstructuredName",{oid_UN, 9}, ASN1_IA5STRING}, + {"TCGID", {oid_TCGID, 12}, ASN1_PRINTABLESTRING} +}; +#define X501_RDN_ROOF 26 + +/** + * maximum number of RDNs in atodn() + */ +#define RDN_MAX 20 + + +typedef struct private_identification_t private_identification_t; + +/** + * Private data of an identification_t object. + */ +struct private_identification_t { + /** + * Public interface. + */ + identification_t public; + + /** + * Encoded representation of this ID. + */ + chunk_t encoded; + + /** + * Type of this ID. + */ + id_type_t type; +}; + +static private_identification_t *identification_create(void); + +/** + * updates a chunk (!????) + * TODO: We should reconsider this stuff, its not really clear + */ +static void update_chunk(chunk_t *ch, int n) +{ + n = (n > -1 && n < (int)ch->len)? n : (int)ch->len-1; + ch->ptr += n; ch->len -= n; +} + +/** + * Prints a binary string in hexadecimal form + */ +void hex_str(chunk_t bin, chunk_t *str) +{ + u_int i; + update_chunk(str, snprintf(str->ptr,str->len,"0x")); + for (i = 0; i < bin.len; i++) + { + update_chunk(str, snprintf(str->ptr,str->len,"%02X",*bin.ptr++)); + } +} + +/** + * Remove any malicious characters from a chunk. We are very restrictive, but + * whe use these strings only to present it to the user. + */ +static chunk_t sanitize_chunk(chunk_t chunk) +{ + char *pos; + chunk_t clone = chunk_clone(chunk); + + for (pos = clone.ptr; pos < (char*)(clone.ptr + clone.len); pos++) + { + switch (*pos) + { + case '\0': + case ' ': + case '*': + case '-': + case '.': + case '/': + case '0' ... '9': + case ':': + case '=': + case '@': + case 'A' ... 'Z': + case '_': + case 'a' ... 'z': + break; + default: + *pos = '?'; + } + } + return clone; +} + +/** + * Pointer is set to the first RDN in a DN + */ +static status_t init_rdn(chunk_t dn, chunk_t *rdn, chunk_t *attribute, bool *next) +{ + *rdn = chunk_empty; + *attribute = chunk_empty; + + /* a DN is a SEQUENCE OF RDNs */ + if (*dn.ptr != ASN1_SEQUENCE) + { + /* DN is not a SEQUENCE */ + return FAILED; + } + + rdn->len = asn1_length(&dn); + + if (rdn->len == ASN1_INVALID_LENGTH) + { + /* Invalid RDN length */ + return FAILED; + } + + rdn->ptr = dn.ptr; + + /* are there any RDNs ? */ + *next = rdn->len > 0; + + return SUCCESS; +} + +/** + * Fetches the next RDN in a DN + */ +static status_t get_next_rdn(chunk_t *rdn, chunk_t * attribute, chunk_t *oid, chunk_t *value, asn1_t *type, bool *next) +{ + chunk_t body; + + /* initialize return values */ + *oid = chunk_empty; + *value = chunk_empty; + + /* if all attributes have been parsed, get next rdn */ + if (attribute->len <= 0) + { + /* an RDN is a SET OF attributeTypeAndValue */ + if (*rdn->ptr != ASN1_SET) + { + /* RDN is not a SET */ + return FAILED; + } + attribute->len = asn1_length(rdn); + if (attribute->len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute length */ + return FAILED; + } + attribute->ptr = rdn->ptr; + /* advance to start of next RDN */ + rdn->ptr += attribute->len; + rdn->len -= attribute->len; + } + + /* an attributeTypeAndValue is a SEQUENCE */ + if (*attribute->ptr != ASN1_SEQUENCE) + { + /* attributeTypeAndValue is not a SEQUENCE */ + return FAILED; + } + + /* extract the attribute body */ + body.len = asn1_length(attribute); + + if (body.len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute body length */ + return FAILED; + } + + body.ptr = attribute->ptr; + + /* advance to start of next attribute */ + attribute->ptr += body.len; + attribute->len -= body.len; + + /* attribute type is an OID */ + if (*body.ptr != ASN1_OID) + { + /* attributeType is not an OID */ + return FAILED; + } + /* extract OID */ + oid->len = asn1_length(&body); + + if (oid->len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute OID length */ + return FAILED; + } + oid->ptr = body.ptr; + + /* advance to the attribute value */ + body.ptr += oid->len; + body.len -= oid->len; + + /* extract string type */ + *type = *body.ptr; + + /* extract string value */ + value->len = asn1_length(&body); + + if (value->len == ASN1_INVALID_LENGTH) + { + /* Invalid attribute string length */ + return FAILED; + } + value->ptr = body.ptr; + + /* are there any RDNs left? */ + *next = rdn->len > 0 || attribute->len > 0; + return SUCCESS; +} + +/** + * Parses an ASN.1 distinguished name int its OID/value pairs + */ +static status_t dntoa(chunk_t dn, chunk_t *str) +{ + chunk_t rdn, oid, attribute, value, proper; + asn1_t type; + int oid_code; + bool next; + bool first = TRUE; + + status_t status = init_rdn(dn, &rdn, &attribute, &next); + + if (status != SUCCESS) + return status; + + while (next) + { + status = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next); + + if (status != SUCCESS) + return status; + + if (first) + { /* first OID/value pair */ + first = FALSE; + } + else + { /* separate OID/value pair by a comma */ + update_chunk(str, snprintf(str->ptr,str->len,", ")); + } + + /* print OID */ + oid_code = known_oid(oid); + if (oid_code == OID_UNKNOWN) + { /* OID not found in list */ + hex_str(oid, str); + } + else + { + update_chunk(str, snprintf(str->ptr,str->len,"%s", oid_names[oid_code].name)); + } + /* print value */ + proper = sanitize_chunk(value); + update_chunk(str, snprintf(str->ptr,str->len,"=%.*s", (int)proper.len, proper.ptr)); + chunk_free(&proper); + } + return SUCCESS; +} + +/** + * compare two distinguished names by + * comparing the individual RDNs + */ +static bool same_dn(chunk_t a, chunk_t b) +{ + chunk_t rdn_a, rdn_b, attribute_a, attribute_b; + chunk_t oid_a, oid_b, value_a, value_b; + asn1_t type_a, type_b; + bool next_a, next_b; + + /* same lengths for the DNs */ + if (a.len != b.len) + return FALSE; + + /* try a binary comparison first */ + if (memeq(a.ptr, b.ptr, b.len)) + return TRUE; + + /* initialize DN parsing */ + if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS + || init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS) + { + return FALSE; + } + + /* fetch next RDN pair */ + while (next_a && next_b) + { + /* parse next RDNs and check for errors */ + if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS + || get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS) + { + return FALSE; + } + + /* OIDs must agree */ + if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0) + return FALSE; + + /* same lengths for values */ + if (value_a.len != value_b.len) + return FALSE; + + /* printableStrings and email RDNs require uppercase comparison */ + if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING + || (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL))) + { + if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + else + { + if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + } + /* both DNs must have same number of RDNs */ + if (next_a || next_b) + return FALSE; + + /* the two DNs are equal! */ + return TRUE; +} + + +/** + * compare two distinguished names by comparing the individual RDNs. + * A single'*' character designates a wildcard RDN in DN b. + * TODO: Add support for different RDN order in DN !! + */ +bool match_dn(chunk_t a, chunk_t b, int *wildcards) +{ + chunk_t rdn_a, rdn_b, attribute_a, attribute_b; + chunk_t oid_a, oid_b, value_a, value_b; + asn1_t type_a, type_b; + bool next_a, next_b; + + /* initialize wildcard counter */ + if (wildcards) + { + *wildcards = 0; + } + + /* initialize DN parsing */ + if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS + || init_rdn(b, &rdn_b, &attribute_b, &next_b) != SUCCESS) + { + return FALSE; + } + + /* fetch next RDN pair */ + while (next_a && next_b) + { + /* parse next RDNs and check for errors */ + if (get_next_rdn(&rdn_a, &attribute_a, &oid_a, &value_a, &type_a, &next_a) != SUCCESS + || get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != SUCCESS) + { + return FALSE; + } + /* OIDs must agree */ + if (oid_a.len != oid_b.len || memcmp(oid_a.ptr, oid_b.ptr, oid_b.len) != 0) + return FALSE; + + /* does rdn_b contain a wildcard? */ + if (value_b.len == 1 && *value_b.ptr == '*') + { + if (wildcards) + { + (*wildcards)++; + } + continue; + } + /* same lengths for values */ + if (value_a.len != value_b.len) + return FALSE; + + /* printableStrings and email RDNs require uppercase comparison */ + if (type_a == type_b && (type_a == ASN1_PRINTABLESTRING + || (type_a == ASN1_IA5STRING && known_oid(oid_a) == OID_PKCS9_EMAIL))) + { + if (strncasecmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + else + { + if (strncmp(value_a.ptr, value_b.ptr, value_b.len) != 0) + return FALSE; + } + } + /* both DNs must have same number of RDNs */ + if (next_a || next_b) + { + return FALSE; + } + + /* the two DNs match! */ + if (wildcards) + { + *wildcards = min(*wildcards, MAX_WILDCARDS); + } + return TRUE; +} + +/** + * Converts an LDAP-style human-readable ASCII-encoded + * ASN.1 distinguished name into binary DER-encoded format + */ +static status_t atodn(char *src, chunk_t *dn) +{ + /* finite state machine for atodn */ + typedef enum { + SEARCH_OID = 0, + READ_OID = 1, + SEARCH_NAME = 2, + READ_NAME = 3, + UNKNOWN_OID = 4 + } state_t; + + chunk_t oid = chunk_empty; + chunk_t name = chunk_empty; + chunk_t rdns[RDN_MAX]; + int rdn_count = 0; + int dn_len = 0; + int whitespace = 0; + int i = 0; + asn1_t rdn_type; + state_t state = SEARCH_OID; + status_t status = SUCCESS; + + do + { + switch (state) + { + case SEARCH_OID: + if (*src != ' ' && *src != '/' && *src != ',') + { + oid.ptr = src; + oid.len = 1; + state = READ_OID; + } + break; + case READ_OID: + if (*src != ' ' && *src != '=') + { + oid.len++; + } + else + { + for (i = 0; i < X501_RDN_ROOF; i++) + { + if (strlen(x501rdns[i].name) == oid.len + && strncasecmp(x501rdns[i].name, oid.ptr, oid.len) == 0) + { + break; /* found a valid OID */ + } + } + if (i == X501_RDN_ROOF) + { + status = NOT_SUPPORTED; + state = UNKNOWN_OID; + break; + } + /* reset oid and change state */ + oid = chunk_empty; + state = SEARCH_NAME; + } + break; + case SEARCH_NAME: + if (*src != ' ' && *src != '=') + { + name.ptr = src; + name.len = 1; + whitespace = 0; + state = READ_NAME; + } + break; + case READ_NAME: + if (*src != ',' && *src != '/' && *src != '\0') + { + name.len++; + if (*src == ' ') + whitespace++; + else + whitespace = 0; + } + else + { + name.len -= whitespace; + rdn_type = (x501rdns[i].type == ASN1_PRINTABLESTRING + && !is_printablestring(name))? ASN1_T61STRING : x501rdns[i].type; + + if (rdn_count < RDN_MAX) + { + rdns[rdn_count] = + asn1_wrap(ASN1_SET, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_OID, "c", x501rdns[i].oid), + asn1_wrap(rdn_type, "c", name) + ) + ); + dn_len += rdns[rdn_count++].len; + } + else + { + status = OUT_OF_RES; + } + /* reset name and change state */ + name = chunk_empty; + state = SEARCH_OID; + } + break; + case UNKNOWN_OID: + break; + } + } while (*src++ != '\0'); + + /* build the distinguished name sequence */ + { + int i; + u_char *pos = build_asn1_object(dn, ASN1_SEQUENCE, dn_len); + + for (i = 0; i < rdn_count; i++) + { + memcpy(pos, rdns[i].ptr, rdns[i].len); + pos += rdns[i].len; + free(rdns[i].ptr); + } + } + + if (status != SUCCESS) + { + free(dn->ptr); + *dn = chunk_empty; + } + return status; +} + +/** + * Implementation of identification_t.get_encoding. + */ +static chunk_t get_encoding(private_identification_t *this) +{ + return this->encoded; +} + +/** + * Implementation of identification_t.get_type. + */ +static id_type_t get_type(private_identification_t *this) +{ + return this->type; +} + +/** + * Implementation of identification_t.contains_wildcards. + */ +static bool contains_wildcards(private_identification_t *this) +{ + switch (this->type) + { + case ID_ANY: + return TRUE; + case ID_FQDN: + case ID_RFC822_ADDR: + return memchr(this->encoded.ptr, '*', this->encoded.len) != NULL; + case ID_DER_ASN1_DN: + /* TODO */ + default: + return FALSE; + + } +} + +/** + * Default implementation of identification_t.equals. + * compares encoded chunk for equality. + */ +static bool equals_binary(private_identification_t *this, private_identification_t *other) +{ + return this->type == other->type && + chunk_equals(this->encoded, other->encoded); +} + +/** + * Special implementation of identification_t.equals for ID_DER_ASN1_DN. + */ +static bool equals_dn(private_identification_t *this, + private_identification_t *other) +{ + return same_dn(this->encoded, other->encoded); +} + +/** + * Default implementation of identification_t.matches. + */ +static bool matches_binary(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + if (other->type == ID_ANY) + { + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } + return TRUE; + } + if (wildcards) + { + *wildcards = 0; + } + return this->type == other->type && + chunk_equals(this->encoded, other->encoded); +} + +/** + * Special implementation of identification_t.matches for ID_RFC822_ADDR/ID_FQDN. + * Checks for a wildcard in other-string, and compares it against this-string. + */ +static bool matches_string(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + u_int len = other->encoded.len; + + if (other->type == ID_ANY) + { + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } + return TRUE; + } + + if (this->type != other->type) + return FALSE; + + /* try a binary comparison first */ + if (equals_binary(this, other)) + { + if (wildcards) + { + *wildcards = 0; + } + return TRUE; + } + + if (len == 0 || this->encoded.len < len) + return FALSE; + + /* check for single wildcard at the head of the string */ + if (*other->encoded.ptr == '*') + { + if (wildcards) + { + *wildcards = 1; + } + + /* single asterisk matches any string */ + if (len-- == 1) + return TRUE; + + if (memeq(this->encoded.ptr + this->encoded.len - len, other->encoded.ptr + 1, len)) + return TRUE; + } + + return FALSE; +} + +/** + * Special implementation of identification_t.matches for ID_ANY. + * ANY matches only another ANY, but nothing other + */ +static bool matches_any(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + if (wildcards) + { + *wildcards = 0; + } + return other->type == ID_ANY; +} + +/** + * Special implementation of identification_t.matches for ID_DER_ASN1_DN. + * ANY matches any, even ANY, thats why its there... + */ +static bool matches_dn(private_identification_t *this, + private_identification_t *other, int *wildcards) +{ + if (other->type == ID_ANY) + { + if (wildcards) + { + *wildcards = MAX_WILDCARDS; + } + return TRUE; + } + + if (this->type == other->type) + { + return match_dn(this->encoded, other->encoded, wildcards); + } + return FALSE; +} + +/** + * output handler in printf() + */ +static int print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + private_identification_t *this = *((private_identification_t**)(args[0])); + char buf[BUF_LEN]; + chunk_t proper, buf_chunk = chunk_from_buf(buf); + int written; + + if (this == NULL) + { + return fprintf(stream, "(null)"); + } + + switch (this->type) + { + case ID_ANY: + return fprintf(stream, "%%any"); + case ID_IPV4_ADDR: + if (this->encoded.len < sizeof(struct in_addr) || + inet_ntop(AF_INET, this->encoded.ptr, buf, sizeof(buf)) == NULL) + { + return fprintf(stream, "(invalid ID_IPV4_ADDR)"); + } + else + { + return fprintf(stream, "%s", buf); + } + case ID_IPV6_ADDR: + if (this->encoded.len < sizeof(struct in6_addr) || + inet_ntop(AF_INET6, this->encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL) + { + return fprintf(stream, "(invalid ID_IPV6_ADDR)"); + } + else + { + return fprintf(stream, "%s", buf); + } + case ID_FQDN: + { + proper = sanitize_chunk(this->encoded); + written = fprintf(stream, "@%.*s", proper.len, proper.ptr); + chunk_free(&proper); + return written; + } + case ID_RFC822_ADDR: + { + proper = sanitize_chunk(this->encoded); + written = fprintf(stream, "%.*s", proper.len, proper.ptr); + chunk_free(&proper); + return written; + } + case ID_DER_ASN1_DN: + { + snprintf(buf, sizeof(buf), "%.*s", this->encoded.len, this->encoded.ptr); + /* TODO: whats returned on failure?*/ + dntoa(this->encoded, &buf_chunk); + return fprintf(stream, "%s", buf); + } + case ID_DER_ASN1_GN: + return fprintf(stream, "(ASN.1 general Name"); + case ID_KEY_ID: + return fprintf(stream, "(KEY_ID)"); + case ID_DER_ASN1_GN_URI: + { + proper = sanitize_chunk(this->encoded); + written = fprintf(stream, "%.*s", proper.len, proper.ptr); + chunk_free(&proper); + return written; + } + default: + return fprintf(stream, "(unknown ID type: %d)", this->type); + } +} + +/** + * register printf() handlers + */ +static void __attribute__ ((constructor))print_register() +{ + register_printf_function(PRINTF_IDENTIFICATION, print, arginfo_ptr); +} + +/** + * Implementation of identification_t.clone. + */ +static identification_t *clone_(private_identification_t *this) +{ + private_identification_t *clone = identification_create(); + + clone->type = this->type; + clone->encoded = chunk_clone(this->encoded); + clone->public.equals = this->public.equals; + clone->public.matches = this->public.matches; + + return &clone->public; +} + +/** + * Implementation of identification_t.destroy. + */ +static void destroy(private_identification_t *this) +{ + chunk_free(&this->encoded); + free(this); +} + +/** + * Generic constructor used for the other constructors. + */ +static private_identification_t *identification_create(void) +{ + private_identification_t *this = malloc_thing(private_identification_t); + + this->public.get_encoding = (chunk_t (*) (identification_t*))get_encoding; + this->public.get_type = (id_type_t (*) (identification_t*))get_type; + this->public.contains_wildcards = (bool (*) (identification_t *this))contains_wildcards; + this->public.clone = (identification_t* (*) (identification_t*))clone_; + this->public.destroy = (void (*) (identification_t*))destroy; + /* we use these as defaults, the may be overloaded for special ID types */ + this->public.equals = (bool (*) (identification_t*,identification_t*))equals_binary; + this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_binary; + + this->encoded = chunk_empty; + + return this; +} + +/* + * Described in header. + */ +identification_t *identification_create_from_string(char *string) +{ + private_identification_t *this = identification_create(); + + if (string == NULL) + { + string = "%any"; + } + if (strchr(string, '=') != NULL) + { + /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN. + * convert from LDAP style or openssl x509 -subject style to ASN.1 DN + */ + if (atodn(string, &this->encoded) != SUCCESS) + { + free(this); + return NULL; + } + this->type = ID_DER_ASN1_DN; + this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn; + this->public.matches = (bool (*) (identification_t*,identification_t*,int*))matches_dn; + return &this->public; + } + else if (strchr(string, '@') == NULL) + { + if (streq(string, "%any") + || streq(string, "0.0.0.0") + || streq(string, "*") + || streq(string, "::") + || streq(string, "0::0")) + { + /* any ID will be accepted */ + this->type = ID_ANY; + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_any; + return &this->public; + } + else + { + if (strchr(string, ':') == NULL) + { + /* try IPv4 */ + struct in_addr address; + chunk_t chunk = {(void*)&address, sizeof(address)}; + + if (inet_pton(AF_INET, string, &address) <= 0) + { + free(this); + return NULL; + } + this->encoded = chunk_clone(chunk); + this->type = ID_IPV4_ADDR; + return &(this->public); + } + else + { + /* try IPv6 */ + struct in6_addr address; + chunk_t chunk = {(void*)&address, sizeof(address)}; + + if (inet_pton(AF_INET6, string, &address) <= 0) + { + free(this); + return NULL; + } + this->encoded = chunk_clone(chunk); + this->type = ID_IPV6_ADDR; + return &(this->public); + } + } + } + else + { + if (*string == '@') + { + if (*(string + 1) == '#') + { + /* TODO: Pluto handles '#' as hex encoded ID_KEY_ID. */ + free(this); + return NULL; + } + else + { + this->type = ID_FQDN; + this->encoded.ptr = strdup(string + 1); + this->encoded.len = strlen(string + 1); + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + return &(this->public); + } + } + else + { + this->type = ID_RFC822_ADDR; + this->encoded.ptr = strdup(string); + this->encoded.len = strlen(string); + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + return &(this->public); + } + } +} + +/* + * Described in header. + */ +identification_t *identification_create_from_encoding(id_type_t type, chunk_t encoded) +{ + private_identification_t *this = identification_create(); + this->type = type; + switch (type) + { + case ID_ANY: + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_any; + break; + case ID_FQDN: + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + break; + case ID_RFC822_ADDR: + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_string; + break; + case ID_DER_ASN1_DN: + this->public.equals = (bool (*) + (identification_t*,identification_t*))equals_dn; + this->public.matches = (bool (*) + (identification_t*,identification_t*,int*))matches_dn; + break; + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + case ID_DER_ASN1_GN: + case ID_KEY_ID: + case ID_DER_ASN1_GN_URI: + default: + break; + } + + /* apply encoded chunk */ + if (type != ID_ANY) + { + this->encoded = chunk_clone(encoded); + } + return &(this->public); +} diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h new file mode 100644 index 000000000..59c568eaf --- /dev/null +++ b/src/libstrongswan/utils/identification.h @@ -0,0 +1,261 @@ +/** + * @file identification.h + * + * @brief Interface of identification_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + + +#ifndef IDENTIFICATION_H_ +#define IDENTIFICATION_H_ + +typedef enum id_type_t id_type_t; +typedef struct identification_t identification_t; + +#include + +#define MAX_WILDCARDS 14 + +/** + * @brief ID Types in a ID payload. + * + * @ingroup utils + */ +enum id_type_t { + + /** + * private type which matches any other id. + */ + ID_ANY = 0, + + /** + * ID data is a single four (4) octet IPv4 address. + */ + ID_IPV4_ADDR = 1, + + /** + * ID data is a fully-qualified domain name string. + * An example of a ID_FQDN is "example.com". + * The string MUST not contain any terminators (e.g., NULL, CR, etc.). + */ + ID_FQDN = 2, + + /** + * ID data is a fully-qualified RFC822 email address string. + * An example of an ID_RFC822_ADDR is "jsmith@example.com". + * The string MUST NOT contain any terminators. + */ + ID_RFC822_ADDR = 3, + + /** + * ID data is an IPv4 subnet (IKEv1 only) + */ + ID_IPV4_ADDR_SUBNET = 4, + + /** + * ID data is a single sixteen (16) octet IPv6 address. + */ + ID_IPV6_ADDR = 5, + + /** + * ID data is an IPv6 subnet (IKEv1 only) + */ + ID_IPV6_ADDR_SUBNET = 6, + + /** + * ID data is an IPv4 address range (IKEv1 only) + */ + ID_IPV4_ADDR_RANGE = 7, + + /** + * ID data is an IPv6 address range (IKEv1 only) + */ + ID_IPV6_ADDR_RANGE = 8, + + /** + * ID data is the binary DER encoding of an ASN.1 X.501 Distinguished Name + */ + ID_DER_ASN1_DN = 9, + + /** + * ID data is the binary DER encoding of an ASN.1 X.509 GeneralName + */ + ID_DER_ASN1_GN = 10, + + /** + * ID data is an opaque octet stream which may be used to pass vendor- + * specific information necessary to do certain proprietary + * types of identification. + */ + ID_KEY_ID = 11, + + /** + * private type which represents a GeneralName of type URI + */ + ID_DER_ASN1_GN_URI = 201, + +}; + +/** + * enum names for id_type_t. + */ +extern enum_name_t *id_type_names; + +/** + * @brief Generic identification, such as used in ID payload. + * + * The following types are possible: + * - ID_IPV4_ADDR + * - ID_FQDN + * - ID_RFC822_ADDR + * - ID_IPV6_ADDR + * - ID_DER_ASN1_DN + * - ID_DER_ASN1_GN + * - ID_KEY_ID + * - ID_DER_ASN1_GN_URI + * + * @b Constructors: + * - identification_create_from_string() + * - identification_create_from_encoding() + * + * @todo Support for ID_DER_ASN1_GN is minimal right now. Comparison + * between them and ID_IPV4_ADDR/RFC822_ADDR would be nice. + * + * @ingroup utils + */ +struct identification_t { + + /** + * @brief Get the encoding of this id, to send over + * the network. + * + * @warning Result points to internal data, do NOT free! + * + * @param this the identification_t object + * @return a chunk containing the encoded bytes + */ + chunk_t (*get_encoding) (identification_t *this); + + /** + * @brief Get the type of this identification. + * + * @param this the identification_t object + * @return id_type_t + */ + id_type_t (*get_type) (identification_t *this); + + /** + * @brief Check if two identification_t objects are equal. + * + * @param this the identification_t object + * @param other other identification_t object + * @return TRUE if the IDs are equal + */ + bool (*equals) (identification_t *this, identification_t *other); + + /** + * @brief Check if an ID matches a wildcard ID. + * + * An identification_t may contain wildcards, such as + * *@strongswan.org. This call checks if a given ID + * (e.g. tester@strongswan.org) belongs to a such wildcard + * ID. Returns TRUE if + * - IDs are identical + * - other is of type ID_ANY + * - other contains a wildcard and matches this + * + * @param this the ID without wildcard + * @param other the ID containing a wildcard + * @param wildcards returns the number of wildcards, may be NULL + * @return TRUE if match is found + */ + bool (*matches) (identification_t *this, identification_t *other, int *wildcards); + + /** + * @brief Check if an ID is a wildcard ID. + * + * If the ID represents multiple IDs (with wildcards, or + * as the type ID_ANY), TRUE is returned. If it is unique, + * FALSE is returned. + * + * @param this identification_t object + * @return TRUE if ID contains wildcards + */ + bool (*contains_wildcards) (identification_t *this); + + /** + * @brief Clone a identification_t instance. + * + * @param this the identification_t object to clone + * @return clone of this + */ + identification_t *(*clone) (identification_t *this); + + /** + * @brief Destroys a identification_t object. + * + * @param this identification_t object + */ + void (*destroy) (identification_t *this); +}; + +/** + * @brief Creates an identification_t object from a string. + * + * @param string input string, which will be converted + * @return + * - created identification_t object, or + * - NULL if unsupported string supplied. + * + * The input string may be e.g. one of the following: + * - ID_IPV4_ADDR: 192.168.0.1 + * - ID_IPV6_ADDR: 2001:0db8:85a3:08d3:1319:8a2e:0370:7345 + * - ID_FQDN: @www.strongswan.org (@indicates FQDN) + * - ID_RFC822_ADDR: alice@wonderland.org + * - ID_DER_ASN1_DN: C=CH, O=Linux strongSwan, CN=bob + * + * In favour of pluto, domainnames are prepended with an @, since + * pluto resolves domainnames without an @ to IPv4 addresses. Since + * we use a seperate host_t class for addresses, this doesn't + * make sense for us. + * + * A distinguished name may contain one or more of the following RDNs: + * ND, UID, DC, CN, S, SN, serialNumber, C, L, ST, O, OU, T, D, + * N, G, I, ID, EN, EmployeeNumber, E, Email, emailAddress, UN, + * unstructuredName, TCGID. + * + * @ingroup utils + */ +identification_t * identification_create_from_string(char *string); + +/** + * @brief Creates an identification_t object from an encoded chunk. + * + * @param type type of this id, such as ID_IPV4_ADDR + * @param encoded encoded bytes, such as from identification_t.get_encoding + * @return identification_t object + * + * In contrast to identification_create_from_string(), this constructor never + * returns NULL, even when the conversion to a string representation fails. + * + * @ingroup utils + */ +identification_t * identification_create_from_encoding(id_type_t type, chunk_t encoded); + +#endif /* IDENTIFICATION_H_ */ diff --git a/src/libstrongswan/utils/iterator.h b/src/libstrongswan/utils/iterator.h new file mode 100644 index 000000000..02a15c534 --- /dev/null +++ b/src/libstrongswan/utils/iterator.h @@ -0,0 +1,166 @@ +/** + * @file iterator.h + * + * @brief Interface iterator_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef ITERATOR_H_ +#define ITERATOR_H_ + +#include + +/** + * @brief Iterator hook function prototype. + * + * @param param user supplied parameter + * @param in the value the hook receives from the iterator + * @param out the value supplied as a result to the iterator + * @return TRUE to return "out", FALSE to skip this value + */ +typedef bool (iterator_hook_t)(void *param, void *in, void **out); + + +typedef struct iterator_t iterator_t; + +/** + * @brief Iterator interface, allows iteration over collections. + * + * iterator_t defines an interface for iterating over collections. + * It allows searching, deleting, updating and inserting. + * + * Thanks to JMP for iterator lessons :-) + * + * @b Constructors: + * - via linked_list_t.create_iterator, or + * - any other class which supports the iterator_t interface + * + * @see linked_list_t + * + * @ingroup utils + */ +struct iterator_t { + + /** + * @brief Return number of list items. + * + * @param this calling object + * @return number of list items + */ + int (*get_count) (iterator_t *this); + + /** + * @brief Iterate over all items. + * + * The easy way to iterate over items. + * + * @param this calling object + * @param[out] value item + * @return + * - TRUE, if there was an element available, + * - FALSE otherwise + */ + bool (*iterate) (iterator_t *this, void** value); + + /** + * @brief Hook a function into the iterator. + * + * Sometimes it is useful to hook in an iterator. The hook function is + * called before any successful return of iterate(). It takes the + * iterator value, may manipulate it (or the references object), and returns + * the value that the iterate() function returns. + * A value of NULL deactivates the iterator hook. + * + * @param this calling object + * @param hook iterator hook which manipulates the iterated value + * @param param user supplied parameter to pass back to the hook + */ + void (*set_iterator_hook) (iterator_t *this, iterator_hook_t *hook, + void *param); + + /** + * @brief Inserts a new item before the given iterator position. + * + * The iterator position is not changed after inserting + * + * @param this calling iterator + * @param[in] item value to insert in list + */ + void (*insert_before) (iterator_t *this, void *item); + + /** + * @brief Inserts a new item after the given iterator position. + * + * The iterator position is not changed after inserting. + * + * @param this calling iterator + * @param[in] item value to insert in list + */ + void (*insert_after) (iterator_t *this, void *item); + + /** + * @brief Replace the current item at current iterator position. + * + * The iterator position is not changed after replacing. + * + * @param this calling iterator + * @param[out] old_item old value will be written here(can be NULL) + * @param[in] new_item new value + * + * @return + * - SUCCESS + * - FAILED if iterator is on an invalid position + */ + status_t (*replace) (iterator_t *this, void **old_item, void *new_item); + + /** + * @brief Removes an element from list at the given iterator position. + * + * The iterator is set the the following position: + * - to the item before, if available + * - it gets reseted, otherwise + * + * @param this calling object + * @return + * - SUCCESS + * - FAILED if iterator is on an invalid position + */ + status_t (*remove) (iterator_t *this); + + /** + * @brief Resets the iterator position. + * + * After reset, the iterator_t objects doesn't point to an element. + * A call to iterator_t.has_next is necessary to do any other operations + * with the resetted iterator. + * + * @param this calling object + */ + void (*reset) (iterator_t *this); + + /** + * @brief Destroys an iterator. + * + * @param this iterator to destroy + * + */ + void (*destroy) (iterator_t *this); +}; + +#endif /*ITERATOR_H_*/ diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c new file mode 100644 index 000000000..b8a023270 --- /dev/null +++ b/src/libstrongswan/utils/leak_detective.c @@ -0,0 +1,459 @@ +/** + * @file leak_detective.c + * + * @brief Allocation hooks to find memory leaks. + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_BACKTRACE +# include +#endif /* HAVE_BACKTRACE */ + +#include "leak_detective.h" + +#include +#include + +#ifdef LEAK_DETECTIVE + +/** + * Magic value which helps to detect memory corruption. Yummy! + */ +#define MEMORY_HEADER_MAGIC 0x7ac0be11 + +/** + * Pattern which is filled in memory before freeing it + */ +#define MEMORY_FREE_PATTERN 0xFF + +/** + * Pattern which is filled in newly allocated memory + */ +#define MEMORY_ALLOC_PATTERN 0xEE + + +static void install_hooks(void); +static void uninstall_hooks(void); +static void *malloc_hook(size_t, const void *); +static void *realloc_hook(void *, size_t, const void *); +static void free_hook(void*, const void *); + +static u_int count_malloc = 0; +static u_int count_free = 0; +static u_int count_realloc = 0; + +typedef struct memory_header_t memory_header_t; + +/** + * Header which is prepended to each allocated memory block + */ +struct memory_header_t { + /** + * Magci byte which must(!) hold MEMORY_HEADER_MAGIC + */ + u_int32_t magic; + + /** + * Number of bytes following after the header + */ + size_t bytes; + + /** + * Stack frames at the time of allocation + */ + void *stack_frames[STACK_FRAMES_COUNT]; + + /** + * Number of stacks frames obtained in stack_frames + */ + int stack_frame_count; + + /** + * Pointer to previous entry in linked list + */ + memory_header_t *previous; + + /** + * Pointer to next entry in linked list + */ + memory_header_t *next; +}; + +/** + * first mem header is just a dummy to chain + * the others on it... + */ +static memory_header_t first_header = { + magic: MEMORY_HEADER_MAGIC, + bytes: 0, + stack_frame_count: 0, + previous: NULL, + next: NULL +}; + +/** + * standard hooks, used to temparily remove hooking + */ +static void *old_malloc_hook, *old_realloc_hook, *old_free_hook; + +/** + * are the hooks currently installed? + */ +static bool installed = FALSE; + +/** + * Mutex to exclusivly uninstall hooks, access heap list + */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + +/** + * log stack frames queried by backtrace() + * TODO: Dump symbols of static functions. This could be done with + * the addr2line utility or the GNU BFD Library... + */ +static void log_stack_frames(void **stack_frames, int stack_frame_count) +{ +#ifdef HAVE_BACKTRACE + char **strings; + size_t i; + + strings = backtrace_symbols (stack_frames, stack_frame_count); + + DBG1(" dumping %d stack frame addresses", stack_frame_count); + + for (i = 0; i < stack_frame_count; i++) + { + DBG1(" %s", strings[i]); + } + free (strings); +#endif /* HAVE_BACKTRACE */ +} + +/** + * Whitelist, which contains address ranges in stack frames ignored when leaking. + * + * This is necessary, as some function use allocation hacks (static buffers) + * and so on, which we want to suppress on leak reports. + * + * The range_size is calculated using the readelf utility, e.g.: + * readelf -s /lib/glibc.so.6 + * The values are for glibc-2.4 and may or may not be correct on other systems. + */ +typedef struct whitelist_t whitelist_t; + +struct whitelist_t { + void* range_start; + size_t range_size; +}; + +#ifdef LIBCURL +/* dummy declaration for whitelisting */ +void *Curl_getaddrinfo(void); +#endif /* LIBCURL */ + +whitelist_t whitelist[] = { + {pthread_create, 2542}, + {pthread_setspecific, 217}, + {mktime, 60}, + {tzset, 123}, + {inet_ntoa, 249}, + {strerror, 180}, + {getprotobynumber, 291}, + {getservbyport, 311}, + {register_printf_function, 159}, + {syslog, 45}, + {dlopen, 109}, +# ifdef LIBCURL + /* from /usr/lib/libcurl.so.3 */ + {Curl_getaddrinfo, 480}, +# endif /* LIBCURL */ +}; + +/** + * Check if this stack frame is whitelisted. + */ +static bool is_whitelisted(void **stack_frames, int stack_frame_count) +{ + int i, j; + + for (i=0; i< stack_frame_count; i++) + { + for (j=0; j= whitelist[j].range_start && + stack_frames[i] <= (whitelist[j].range_start + whitelist[j].range_size)) + { + return TRUE; + } + } + } + return FALSE; +} + +/** + * Report leaks at library destruction + */ +void report_leaks() +{ + memory_header_t *hdr; + int leaks = 0; + + for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) + { + if (!is_whitelisted(hdr->stack_frames, hdr->stack_frame_count)) + { + DBG1("Leak (%d bytes at %p):", hdr->bytes, hdr + 1); + log_stack_frames(hdr->stack_frames, hdr->stack_frame_count); + leaks++; + } + } + + switch (leaks) + { + case 0: + DBG1("No leaks detected"); + break; + case 1: + DBG1("One leak detected"); + break; + default: + DBG1("%d leaks detected", leaks); + break; + } +} + +/** + * Installs the malloc hooks, enables leak detection + */ +static void install_hooks() +{ + if (!installed) + { + old_malloc_hook = __malloc_hook; + old_realloc_hook = __realloc_hook; + old_free_hook = __free_hook; + __malloc_hook = malloc_hook; + __realloc_hook = realloc_hook; + __free_hook = free_hook; + installed = TRUE; + } +} + +/** + * Uninstalls the malloc hooks, disables leak detection + */ +static void uninstall_hooks() +{ + if (installed) + { + __malloc_hook = old_malloc_hook; + __free_hook = old_free_hook; + __realloc_hook = old_realloc_hook; + installed = FALSE; + } +} + +/** + * Hook function for malloc() + */ +void *malloc_hook(size_t bytes, const void *caller) +{ + memory_header_t *hdr; + + pthread_mutex_lock(&mutex); + count_malloc++; + uninstall_hooks(); + hdr = malloc(bytes + sizeof(memory_header_t)); + /* set to something which causes crashes */ + memset(hdr, MEMORY_ALLOC_PATTERN, bytes + sizeof(memory_header_t)); + + hdr->magic = MEMORY_HEADER_MAGIC; + hdr->bytes = bytes; + hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + install_hooks(); + + /* insert at the beginning of the list */ + hdr->next = first_header.next; + if (hdr->next) + { + hdr->next->previous = hdr; + } + hdr->previous = &first_header; + first_header.next = hdr; + pthread_mutex_unlock(&mutex); + return hdr + 1; +} + +/** + * Hook function for free() + */ +void free_hook(void *ptr, const void *caller) +{ + void *stack_frames[STACK_FRAMES_COUNT]; + int stack_frame_count; + memory_header_t *hdr = ptr - sizeof(memory_header_t); + + /* allow freeing of NULL */ + if (ptr == NULL) + { + return; + } + + pthread_mutex_lock(&mutex); + count_free++; + uninstall_hooks(); + if (hdr->magic != MEMORY_HEADER_MAGIC) + { + DBG1("freeing of invalid memory (%p, MAGIC 0x%x != 0x%x):", + ptr, hdr->magic, MEMORY_HEADER_MAGIC); + stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); + log_stack_frames(stack_frames, stack_frame_count); + install_hooks(); + pthread_mutex_unlock(&mutex); + return; + } + + /* remove item from list */ + if (hdr->next) + { + hdr->next->previous = hdr->previous; + } + hdr->previous->next = hdr->next; + + /* clear MAGIC, set mem to something remarkable */ + memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t)); + + free(hdr); + install_hooks(); + pthread_mutex_unlock(&mutex); +} + +/** + * Hook function for realloc() + */ +void *realloc_hook(void *old, size_t bytes, const void *caller) +{ + memory_header_t *hdr; + void *stack_frames[STACK_FRAMES_COUNT]; + int stack_frame_count; + + /* allow reallocation of NULL */ + if (old == NULL) + { + return malloc_hook(bytes, caller); + } + + hdr = old - sizeof(memory_header_t); + + pthread_mutex_lock(&mutex); + count_realloc++; + uninstall_hooks(); + if (hdr->magic != MEMORY_HEADER_MAGIC) + { + DBG1("reallocation of invalid memory (%p):", old); + stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); + log_stack_frames(stack_frames, stack_frame_count); + install_hooks(); + pthread_mutex_unlock(&mutex); + raise(SIGKILL); + return NULL; + } + + hdr = realloc(hdr, bytes + sizeof(memory_header_t)); + + /* update statistics */ + hdr->bytes = bytes; + hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + + /* update header of linked list neighbours */ + if (hdr->next) + { + hdr->next->previous = hdr; + } + hdr->previous->next = hdr; + install_hooks(); + pthread_mutex_unlock(&mutex); + return hdr + 1; +} + +/** + * Setup leak detective + */ +void __attribute__ ((constructor)) leak_detective_init() +{ + install_hooks(); +} + +/** + * Clean up leak detective + */ +void __attribute__ ((destructor)) leak_detective_cleanup() +{ + uninstall_hooks(); + report_leaks(); +} + +/** + * Log memory allocation statistics + */ +void leak_detective_status(FILE *stream) +{ + u_int blocks = 0; + size_t bytes = 0; + memory_header_t *hdr = &first_header; + + pthread_mutex_lock(&mutex); + while ((hdr = hdr->next)) + { + blocks++; + bytes += hdr->bytes; + } + pthread_mutex_unlock(&mutex); + + fprintf(stream, "allocation statistics:\n"); + fprintf(stream, " call stats: malloc: %d, free: %d, realloc: %d\n", + count_malloc, count_free, count_realloc); + fprintf(stream, " allocated %d blocks, total size %d bytes (avg. %d bytes)\n", + blocks, bytes, bytes/blocks); +} + +#else /* !LEAK_DETECTION */ + +/** + * Dummy when !using LEAK_DETECTIVE + */ +void leak_detective_status(FILE *stream) +{ + +} + +#endif /* LEAK_DETECTION */ diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h new file mode 100644 index 000000000..d4016b06e --- /dev/null +++ b/src/libstrongswan/utils/leak_detective.h @@ -0,0 +1,35 @@ +/** + * @file leak_detective.h + * + * @brief malloc/free hooks to detect leaks. + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef LEAK_DETECTIVE_H_ +#define LEAK_DETECTIVE_H_ + +/** + * Log status information about allocation + */ +void leak_detective_status(FILE *stream); + +/** + * Max number of stack frames to include in a backtrace. + */ +#define STACK_FRAMES_COUNT 30 + +#endif /* LEAK_DETECTIVE_H_ */ diff --git a/src/libstrongswan/utils/lexparser.c b/src/libstrongswan/utils/lexparser.c new file mode 100644 index 000000000..9d3f06593 --- /dev/null +++ b/src/libstrongswan/utils/lexparser.c @@ -0,0 +1,137 @@ +/** + * @file lexparser.c + * + * @brief lexical parser for text-based configuration files + * + */ + +/* + * Copyright (C) 2001-2006 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "lexparser.h" + + +/** + * eat whitespace + */ +bool eat_whitespace(chunk_t *src) +{ + while (src->len > 0 && (*src->ptr == ' ' || *src->ptr == '\t')) + { + src->ptr++; src->len--; + } + return src->len > 0 && *src->ptr != '#'; +} + +/** + * compare string with chunk + */ +bool match(const char *pattern, const chunk_t *ch) +{ + return ch->len == strlen(pattern) && strncmp(pattern, ch->ptr, ch->len) == 0; +} + +/** + * extracts a token ending with a given termination symbol + */ +bool extract_token(chunk_t *token, const char termination, chunk_t *src) +{ + u_char *eot = memchr(src->ptr, termination, src->len); + + /* initialize empty token */ + *token = chunk_empty; + + if (eot == NULL) /* termination symbol not found */ + { + return FALSE; + } + + /* extract token */ + token->ptr = src->ptr; + token->len = (u_int)(eot - src->ptr); + + /* advance src pointer after termination symbol */ + src->ptr = eot + 1; + src->len -= (token->len + 1); + + return TRUE; +} + +/** + * fetches a new line terminated by \n or \r\n + */ +bool fetchline(chunk_t *src, chunk_t *line) +{ + if (src->len == 0) /* end of src reached */ + return FALSE; + + if (extract_token(line, '\n', src)) + { + if (line->len > 0 && *(line->ptr + line->len -1) == '\r') + line->len--; /* remove optional \r */ + } + else /*last line ends without newline */ + { + *line = *src; + src->ptr += src->len; + src->len = 0; + } + return TRUE; +} + +err_t extract_value(chunk_t *value, chunk_t *line) +{ + char delimiter = ' '; + + if (!eat_whitespace(line)) + { + *value = chunk_empty; + return NULL; + } + if (*line->ptr == '\'' || *line->ptr == '"') + { + delimiter = *line->ptr; + line->ptr++; line->len--; + } + if (!extract_token(value, delimiter, line)) + { + if (delimiter == ' ') + { + *value = *line; + line->len = 0; + } + else + { + return "missing second delimiter"; + } + } + return NULL; +} + +/** + * extracts a parameter: value pair + */ +err_t extract_parameter_value(chunk_t *name, chunk_t *value, chunk_t *line) +{ + /* extract name */ + if (!extract_token(name,':', line)) + { + return "missing ':'"; + } + + /* extract value */ + return extract_value(value, line); +} diff --git a/src/libstrongswan/utils/lexparser.h b/src/libstrongswan/utils/lexparser.h new file mode 100644 index 000000000..e3c2c4c70 --- /dev/null +++ b/src/libstrongswan/utils/lexparser.h @@ -0,0 +1,57 @@ +/** + * @file lexparser.h + * + * @brief lexical parser for text-based configuration files + * + */ + +/* + * Copyright (C) 2001-2006 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +/** + * @brief Eats whitespace + */ +bool eat_whitespace(chunk_t *src); + +/** + * @brief Compare null-terminated pattern with chunk + */ +bool match(const char *pattern, const chunk_t *ch); + +/** + * @brief Extracts a token ending with a given termination symbol + */ +bool extract_token(chunk_t *token, const char termination, chunk_t *src); + +/** + * @brief Fetches a new text line terminated by \n or \r\n + */ +bool fetchline(chunk_t *src, chunk_t *line); + +/** + * @brief Extracts a value that might be single or double quoted + */ +err_t extract_value(chunk_t *value, chunk_t *line); + +/** + * @brief extracts a name: value pair from a text line + */ +err_t extract_name_value(chunk_t *name, chunk_t *value, chunk_t *line); + +/** + * @brief extracts a parameter: value from a text line + */ +err_t extract_parameter_value(chunk_t *name, chunk_t *value, chunk_t *line); diff --git a/src/libstrongswan/utils/linked_list.c b/src/libstrongswan/utils/linked_list.c new file mode 100644 index 000000000..de043a02e --- /dev/null +++ b/src/libstrongswan/utils/linked_list.c @@ -0,0 +1,763 @@ +/** + * @file linked_list.c + * + * @brief Implementation of linked_list_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include + +#include "linked_list.h" + +typedef struct element_t element_t; + +/** + * This element holds a pointer to the value it represents. + */ +struct element_t { + + /** + * Value of a list item. + */ + void *value; + + /** + * Previous list element. + * + * NULL if first element in list. + */ + element_t *previous; + + /** + * Next list element. + * + * NULL if last element in list. + */ + element_t *next; +}; + +/** + * Creates an empty linked list object. + */ +element_t *element_create(void *value) +{ + element_t *this = malloc_thing(element_t); + + this->previous = NULL; + this->next = NULL; + this->value = value; + + return (this); +} + + +typedef struct private_linked_list_t private_linked_list_t; + +/** + * Private data of a linked_list_t object. + * + */ +struct private_linked_list_t { + /** + * Public part of linked list. + */ + linked_list_t public; + + /** + * Number of items in the list. + */ + int count; + + /** + * First element in list. + * NULL if no elements in list. + */ + element_t *first; + + /** + * Last element in list. + * NULL if no elements in list. + */ + element_t *last; +}; + + +typedef struct private_iterator_t private_iterator_t; + +/** + * Private variables and functions of linked list iterator. + */ +struct private_iterator_t { + /** + * Public part of linked list iterator. + */ + iterator_t public; + + /** + * Associated linked list. + */ + private_linked_list_t * list; + + /** + * Current element of the iterator. + */ + element_t *current; + + /** + * Direction of iterator. + */ + bool forward; + + /** + * Mutex to use to synchronize access + */ + pthread_mutex_t *mutex; + + /** + * iteration hook + */ + iterator_hook_t *hook; + + /** + * user parameter for iterator hook + */ + void *hook_param; +}; + +/** + * Implementation of iterator_t.get_count. + */ +static int get_list_count(private_iterator_t *this) +{ + return this->list->count; +} + +/** + * default iterator hook which does nothing + */ +static bool iterator_hook(void *param, void *in, void **out) +{ + *out = in; + return TRUE; +} + +/** + * Implementation of iterator_t.set_iterator_hook. + */ +static void set_iterator_hook(private_iterator_t *this, iterator_hook_t *hook, + void* param) +{ + if (hook == NULL) + { + this->hook = iterator_hook; + this->hook_param = NULL; + } + else + { + this->hook = hook; + this->hook_param = param; + } +} + +/** + * Implementation of iterator_t.iterate. + */ +static bool iterate(private_iterator_t *this, void** value) +{ + if (this->list->count == 0) + { + return FALSE; + } + if (this->current == NULL) + { + this->current = (this->forward) ? this->list->first : this->list->last; + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } + return TRUE; + } + if (this->forward) + { + if (this->current->next == NULL) + { + return FALSE; + } + this->current = this->current->next; + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } + return TRUE; + } + if (this->current->previous == NULL) + { + return FALSE; + } + this->current = this->current->previous; + if (!this->hook(this->hook_param, this->current->value, value)) + { + return iterate(this, value); + } + return TRUE; +} + +/** + * Implementation of iterator_t.reset. + */ +static void iterator_reset(private_iterator_t *this) +{ + this->current = NULL; +} + +/** + * Implementation of iterator_t.remove. + */ +static status_t remove_(private_iterator_t *this) +{ + element_t *new_current; + + if (this->current == NULL) + { + return NOT_FOUND; + } + + if (this->list->count == 0) + { + return NOT_FOUND; + } + /* find out the new iterator position, depending on iterator direction */ + if (this->forward && this->current->previous != NULL) + { + new_current = this->current->previous; + } + else if (!this->forward && this->current->next != NULL) + { + new_current = this->current->next; + } + else + { + new_current = NULL; + } + + /* now delete the entry :-) */ + if (this->current->previous == NULL) + { + if (this->current->next == NULL) + { + this->list->first = NULL; + this->list->last = NULL; + } + else + { + this->current->next->previous = NULL; + this->list->first = this->current->next; + } + } + else if (this->current->next == NULL) + { + this->current->previous->next = NULL; + this->list->last = this->current->previous; + } + else + { + this->current->previous->next = this->current->next; + this->current->next->previous = this->current->previous; + } + + this->list->count--; + free(this->current); + /* set the new iterator position */ + this->current = new_current; + return SUCCESS; +} + +/** + * Implementation of iterator_t.insert_before. + */ +static void insert_before(private_iterator_t * iterator, void *item) +{ + if (iterator->current == NULL) + { + iterator->list->public.insert_first(&(iterator->list->public), item); + } + + element_t *element = element_create(item); + if (iterator->current->previous == NULL) + { + iterator->current->previous = element; + element->next = iterator->current; + iterator->list->first = element; + } + else + { + iterator->current->previous->next = element; + element->previous = iterator->current->previous; + iterator->current->previous = element; + element->next = iterator->current; + } + iterator->list->count++; +} + +/** + * Implementation of iterator_t.replace. + */ +static status_t replace(private_iterator_t *this, void **old_item, void *new_item) +{ + if (this->current == NULL) + { + return NOT_FOUND; + } + if (old_item != NULL) + { + *old_item = this->current->value; + } + this->current->value = new_item; + + return SUCCESS; +} + +/** + * Implementation of iterator_t.insert_after. + */ +static void insert_after(private_iterator_t *iterator, void *item) +{ + if (iterator->current == NULL) + { + iterator->list->public.insert_first(&(iterator->list->public),item); + return; + } + + element_t *element = element_create(item); + if (iterator->current->next == NULL) + { + iterator->current->next = element; + element->previous = iterator->current; + iterator->list->last = element; + } + else + { + iterator->current->next->previous = element; + element->next = iterator->current->next; + iterator->current->next = element; + element->previous = iterator->current; + } + iterator->list->count++; +} + +/** + * Implementation of iterator_t.destroy. + */ +static void iterator_destroy(private_iterator_t *this) +{ + if (this->mutex) + { + pthread_mutex_unlock(this->mutex); + } + free(this); +} + +/** + * Implementation of linked_list_t.get_count. + */ +static int get_count(private_linked_list_t *this) +{ + return this->count; +} + +/** + * Implementation of linked_list_t.insert_first. + */ +static void insert_first(private_linked_list_t *this, void *item) +{ + element_t *element; + + element = element_create(item); + if (this->count == 0) + { + /* first entry in list */ + this->first = element; + this->last = element; + element->previous = NULL; + element->next = NULL; + } + else + { + element_t *old_first_element = this->first; + element->next = old_first_element; + element->previous = NULL; + old_first_element->previous = element; + this->first = element; + } + this->count++; +} + +/** + * Implementation of linked_list_t.remove_first. + */ +static status_t remove_first(private_linked_list_t *this, void **item) +{ + element_t *element = this->first; + + if (element == NULL) + { + return NOT_FOUND; + } + if (element->next != NULL) + { + element->next->previous = NULL; + } + this->first = element->next; + + if (item != NULL) + { + *item = element->value; + } + if (--this->count == 0) + { + this->last = NULL; + } + + free(element); + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.get_first. + */ +static status_t get_first(private_linked_list_t *this, void **item) +{ + if (this->count == 0) + { + return NOT_FOUND; + } + *item = this->first->value; + return SUCCESS; +} + +/** + * Implementation of linked_list_t.insert_last. + */ +static void insert_last(private_linked_list_t *this, void *item) +{ + element_t *element = element_create(item); + + if (this->count == 0) + { + /* first entry in list */ + this->first = element; + this->last = element; + element->previous = NULL; + element->next = NULL; + } + else + { + element_t *old_last_element = this->last; + element->previous = old_last_element; + element->next = NULL; + old_last_element->next = element; + this->last = element; + } + this->count++; +} + +/** + * Implementation of linked_list_t.remove_last. + */ +static status_t remove_last(private_linked_list_t *this, void **item) +{ + element_t *element = this->last; + + if (element == NULL) + { + return NOT_FOUND; + } + if (element->previous != NULL) + { + element->previous->next = NULL; + } + this->last = element->previous; + + if (item != NULL) + { + *item = element->value; + } + if (--this->count == 0) + { + this->first = NULL; + } + + free(element); + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.insert_at_position. + */ +static status_t insert_at_position (private_linked_list_t *this,size_t position, void *item) +{ + element_t *current_element; + int i; + + if (this->count <= position) + { + return INVALID_ARG; + } + + current_element = this->first; + + for (i = 0; i < position;i++) + { + current_element = current_element->next; + } + + if (current_element == NULL) + { + this->public.insert_last(&(this->public),item); + return SUCCESS; + } + + element_t *element = element_create(item); + if (current_element->previous == NULL) + { + current_element->previous = element; + element->next = current_element; + this->first = element; + } + else + { + current_element->previous->next = element; + element->previous = current_element->previous; + current_element->previous = element; + element->next = current_element; + } + + + this->count++; + return SUCCESS; +} + +/** + * Implementation of linked_list_t.remove_at_position. + */ +static status_t remove_at_position(private_linked_list_t *this,size_t position, void **item) +{ + iterator_t *iterator; + int i; + + if (this->count <= position) + { + return INVALID_ARG; + } + + iterator = this->public.create_iterator(&(this->public),TRUE); + iterator->iterate(iterator, item); + for (i = 0; i < position; i++) + { + if (!iterator->iterate(iterator, item)) + { + iterator->destroy(iterator); + return INVALID_ARG; + } + } + iterator->remove(iterator); + iterator->destroy(iterator); + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.get_at_position. + */ +static status_t get_at_position(private_linked_list_t *this,size_t position, void **item) +{ + int i; + iterator_t *iterator; + + if (this->count <= position) + { + return INVALID_ARG; + } + + iterator = this->public.create_iterator(&(this->public),TRUE); + iterator->iterate(iterator, item); + for (i = 0; i < position; i++) + { + if (!iterator->iterate(iterator, item)) + { + iterator->destroy(iterator); + return INVALID_ARG; + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +/** + * Implementation of linked_list_t.get_last. + */ +static status_t get_last(private_linked_list_t *this, void **item) +{ + if (this->count == 0) + { + return NOT_FOUND; + } + + *item = this->last->value; + + return SUCCESS; +} + +/** + * Implementation of linked_list_t.invoke. + */ +static void invoke(private_linked_list_t *this, size_t offset) +{ + element_t *current = this->first; + + while (current) + { + void (**method)(void*) = current->value + offset; + (*method)(current->value); + current = current->next; + } +} + +/** + * Implementation of linked_list_t.destroy. + */ +static void destroy(private_linked_list_t *this) +{ + void *value; + /* Remove all list items before destroying list */ + while (this->public.remove_first(&(this->public), &value) == SUCCESS) + { + /* values are not destroyed so memory leaks are possible + * if list is not empty when deleting */ + } + free(this); +} + +/** + * Implementation of linked_list_t.destroy_offset. + */ +static void destroy_offset(private_linked_list_t *this, size_t offset) +{ + element_t *current = this->first, *next; + + while (current) + { + void (**method)(void*) = current->value + offset; + (*method)(current->value); + next = current->next; + free(current); + current = next; + } + free(this); +} + +/** + * Implementation of linked_list_t.destroy_function. + */ +static void destroy_function(private_linked_list_t *this, void (*fn)(void*)) +{ + element_t *current = this->first, *next; + + while (current) + { + fn(current->value); + next = current->next; + free(current); + current = next; + } + free(this); +} + +/** + * Implementation of linked_list_t.create_iterator. + */ +static iterator_t *create_iterator(private_linked_list_t *linked_list, bool forward) +{ + private_iterator_t *this = malloc_thing(private_iterator_t); + + this->public.get_count = (int (*) (iterator_t*)) get_list_count; + this->public.iterate = (bool (*) (iterator_t*, void **value)) iterate; + this->public.set_iterator_hook = (void(*)(iterator_t*, iterator_hook_t*, void*))set_iterator_hook; + this->public.insert_before = (void (*) (iterator_t*, void *item)) insert_before; + this->public.insert_after = (void (*) (iterator_t*, void *item)) insert_after; + this->public.replace = (status_t (*) (iterator_t*, void **, void *)) replace; + this->public.remove = (status_t (*) (iterator_t*)) remove_; + this->public.reset = (void (*) (iterator_t*)) iterator_reset; + this->public.destroy = (void (*) (iterator_t*)) iterator_destroy; + + this->forward = forward; + this->current = NULL; + this->list = linked_list; + this->mutex = NULL; + this->hook = iterator_hook; + + return &this->public; +} + +/** + * Implementation of linked_list_t.create_iterator_locked. + */ +static iterator_t *create_iterator_locked(private_linked_list_t *linked_list, + pthread_mutex_t *mutex) +{ + private_iterator_t *this = (private_iterator_t*)create_iterator(linked_list, TRUE); + this->mutex = mutex; + + pthread_mutex_lock(mutex); + + return &this->public; +} + +/* + * Described in header. + */ +linked_list_t *linked_list_create() +{ + private_linked_list_t *this = malloc_thing(private_linked_list_t); + + this->public.get_count = (int (*) (linked_list_t *)) get_count; + this->public.create_iterator = (iterator_t * (*) (linked_list_t *,bool))create_iterator; + this->public.create_iterator_locked = (iterator_t * (*) (linked_list_t *,pthread_mutex_t*))create_iterator_locked; + this->public.get_first = (status_t (*) (linked_list_t *, void **item))get_first; + this->public.get_last = (status_t (*) (linked_list_t *, void **item))get_last; + this->public.insert_first = (void (*) (linked_list_t *, void *item))insert_first; + this->public.insert_last = (void (*) (linked_list_t *, void *item))insert_last; + this->public.remove_first = (status_t (*) (linked_list_t *, void **item))remove_first; + this->public.remove_last = (status_t (*) (linked_list_t *, void **item))remove_last; + this->public.insert_at_position = (status_t (*) (linked_list_t *,size_t, void *))insert_at_position; + this->public.remove_at_position = (status_t (*) (linked_list_t *,size_t, void **))remove_at_position; + this->public.get_at_position = (status_t (*) (linked_list_t *,size_t, void **))get_at_position; + this->public.invoke = (void (*)(linked_list_t*,size_t))invoke; + this->public.destroy = (void (*) (linked_list_t *))destroy; + this->public.destroy_offset = (void (*) (linked_list_t *,size_t))destroy_offset; + this->public.destroy_function = (void (*)(linked_list_t*,void(*)(void*)))destroy_function; + + this->count = 0; + this->first = NULL; + this->last = NULL; + + return &this->public; +} diff --git a/src/libstrongswan/utils/linked_list.h b/src/libstrongswan/utils/linked_list.h new file mode 100644 index 000000000..58bcbbdaa --- /dev/null +++ b/src/libstrongswan/utils/linked_list.h @@ -0,0 +1,232 @@ +/** + * @file linked_list.h + * + * @brief Interface of linked_list_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef LINKED_LIST_H_ +#define LINKED_LIST_H_ + +typedef struct linked_list_t linked_list_t; + +#include + +#include +#include + +/** + * @brief Class implementing a double linked list. + * + * General purpose linked list. This list is not synchronized. + * + * @b Costructors: + * - linked_list_create() + * + * @ingroup utils + */ +struct linked_list_t { + + /** + * @brief Gets the count of items in the list. + * + * @param this calling object + * @return number of items in list + */ + int (*get_count) (linked_list_t *this); + + /** + * @brief Creates a iterator for the given list. + * + * @warning Created iterator_t object has to get destroyed by the caller. + * + * @param this calling object + * @param forward iterator direction (TRUE: front to end) + * @return new iterator_t object + */ + iterator_t *(*create_iterator) (linked_list_t *this, bool forward); + + /** + * @brief Creates a iterator, locking a mutex. + * + * The supplied mutex is acquired immediately, and released + * when the iterator gets destroyed. + * + * @param this calling object + * @param mutex mutex to use for exclusive access + * @return new iterator_t object + */ + iterator_t *(*create_iterator_locked) (linked_list_t *this, + pthread_mutex_t *mutex); + + /** + * @brief Inserts a new item at the beginning of the list. + * + * @param this calling object + * @param[in] item item value to insert in list + */ + void (*insert_first) (linked_list_t *this, void *item); + + /** + * @brief Removes the first item in the list and returns its value. + * + * @param this calling object + * @param[out] item returned value of first item, or NULL + * @return + * - SUCCESS + * - NOT_FOUND, if list is empty + */ + status_t (*remove_first) (linked_list_t *this, void **item); + + /** + * @brief Returns the value of the first list item without removing it. + * + * @param this calling object + * @param[out] item returned value of first item + * @return + * - SUCCESS + * - NOT_FOUND, if list is empty + */ + status_t (*get_first) (linked_list_t *this, void **item); + + /** + * @brief Inserts a new item at the end of the list. + * + * @param this calling object + * @param[in] item value to insert into list + */ + void (*insert_last) (linked_list_t *this, void *item); + + /** + * @brief Inserts a new item at a given position in the list. + * + * @param this calling object + * @param position position starting at 0 to insert new entry + * @param[in] item value to insert into list + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*insert_at_position) (linked_list_t *this,size_t position, void *item); + + /** + * @brief Removes an item from a given position in the list. + * + * @param this calling object + * @param position position starting at 0 to remove entry from + * @param[out] item removed item will be stored at this location + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*remove_at_position) (linked_list_t *this, size_t position, void **item); + + /** + * @brief Get an item from a given position in the list. + * + * @param this calling object + * @param position position starting at 0 to get entry from + * @param[out] item item will be stored at this location + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*get_at_position) (linked_list_t *this, size_t position, void **item); + + /** + * @brief Removes the last item in the list and returns its value. + * + * @param this calling object + * @param[out] item returned value of last item, or NULL + * @return + * - SUCCESS + * - NOT_FOUND if list is empty + */ + status_t (*remove_last) (linked_list_t *this, void **item); + + /** + * @brief Returns the value of the last list item without removing it. + * + * @param this calling object + * @param[out] item returned value of last item + * @return + * - SUCCESS + * - NOT_FOUND if list is empty + */ + status_t (*get_last) (linked_list_t *this, void **item); + + /** + * @brief Invoke a method on all of the contained objects. + * + * If a linked list contains objects with function pointers, + * invoke() can call a method on each of the objects. The + * method is specified by an offset of the function pointer, + * which can be evalutated at compile time using the offsetof + * macro, e.g.: list->invoke(list, offsetof(object_t, method)); + * + * @param this calling object + * @param offset offset of the method to invoke on objects + */ + void (*invoke) (linked_list_t *this, size_t offset); + + /** + * @brief Destroys a linked_list object. + * + * @param this calling object + */ + void (*destroy) (linked_list_t *this); + + /** + * @brief Destroys a list and its objects using the destructor. + * + * If a linked list and the contained objects should be destroyed, use + * destroy_offset. The supplied offset specifies the destructor to + * call on each object. The offset may be calculated using the offsetof + * macro, e.g.: list->destroy_offset(list, offsetof(object_t, destroy)); + * + * @param this calling object + * @param offset offset of the objects destructor + */ + void (*destroy_offset) (linked_list_t *this, size_t offset); + + /** + * @brief Destroys a list and its contents using a a cleanup function. + * + * If a linked list and its contents should get destroyed using a specific + * cleanup function, use destroy_function. This is useful when the + * list contains malloc()-ed blocks which should get freed, + * e.g.: list->destroy_function(list, free); + * + * @param this calling object + * @param function function to call on each object + */ + void (*destroy_function) (linked_list_t *this, void (*)(void*)); +}; + +/** + * @brief Creates an empty linked list object. + * + * @return linked_list_t object. + * + * @ingroup utils + */ +linked_list_t *linked_list_create(void); + + +#endif /*LINKED_LIST_H_*/ diff --git a/src/libstrongswan/utils/randomizer.c b/src/libstrongswan/utils/randomizer.c new file mode 100644 index 000000000..c15d108c7 --- /dev/null +++ b/src/libstrongswan/utils/randomizer.c @@ -0,0 +1,165 @@ +/** + * @file randomizer.c + * + * @brief Implementation of randomizer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "randomizer.h" + + +typedef struct private_randomizer_t private_randomizer_t; + +/** + * Private data of an randomizer_t object. + */ +struct private_randomizer_t { + + /** + * Public randomizer_t interface. + */ + randomizer_t public; + + /** + * @brief Reads a specific number of bytes from random or pseudo random device. + * + * @param this calling object + * @param pseudo_random TRUE, if from pseudo random bytes should be read, + * FALSE for true random bytes + * @param bytes number of bytes to read + * @param[out] buffer pointer to buffer where to write the data in. + * Size of buffer has to be at least bytes. + */ + status_t (*get_bytes_from_device) (private_randomizer_t *this,bool pseudo_random, size_t bytes, u_int8_t *buffer); +}; + + +/** + * Implementation of private_randomizer_t.get_bytes_from_device. + */ +static status_t get_bytes_from_device(private_randomizer_t *this,bool pseudo_random, size_t bytes, u_int8_t *buffer) +{ + size_t ndone; + int device; + size_t got; + char * device_name; + + device_name = pseudo_random ? DEV_URANDOM : DEV_RANDOM; + + device = open(device_name, 0); + if (device < 0) { + return FAILED; + } + ndone = 0; + + /* read until nbytes are read */ + while (ndone < bytes) + { + got = read(device, buffer + ndone, bytes - ndone); + if (got <= 0) { + close(device); + return FAILED; + } + ndone += got; + } + close(device); + return SUCCESS; +} + +/** + * Implementation of randomizer_t.get_random_bytes. + */ +static status_t get_random_bytes(private_randomizer_t *this,size_t bytes, u_int8_t *buffer) +{ + return this->get_bytes_from_device(this, FALSE, bytes, buffer); +} + +/** + * Implementation of randomizer_t.allocate_random_bytes. + */ +static status_t allocate_random_bytes(private_randomizer_t *this, size_t bytes, chunk_t *chunk) +{ + status_t status; + chunk->len = bytes; + chunk->ptr = malloc(bytes); + status = this->get_bytes_from_device(this, FALSE, bytes, chunk->ptr); + if (status != SUCCESS) + { + free(chunk->ptr); + } + return status; +} + +/** + * Implementation of randomizer_t.get_pseudo_random_bytes. + */ +static status_t get_pseudo_random_bytes(private_randomizer_t *this,size_t bytes, u_int8_t *buffer) +{ + return (this->get_bytes_from_device(this, TRUE, bytes, buffer)); +} + +/** + * Implementation of randomizer_t.allocate_pseudo_random_bytes. + */ +static status_t allocate_pseudo_random_bytes(private_randomizer_t *this, size_t bytes, chunk_t *chunk) +{ + status_t status; + chunk->len = bytes; + chunk->ptr = malloc(bytes); + status = this->get_bytes_from_device(this, TRUE, bytes, chunk->ptr); + if (status != SUCCESS) + { + free(chunk->ptr); + } + return status; +} + +/** + * Implementation of randomizer_t.destroy. + */ +static void destroy(private_randomizer_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +randomizer_t *randomizer_create(void) +{ + private_randomizer_t *this = malloc_thing(private_randomizer_t); + + /* public functions */ + this->public.get_random_bytes = (status_t (*) (randomizer_t *,size_t, u_int8_t *)) get_random_bytes; + this->public.allocate_random_bytes = (status_t (*) (randomizer_t *,size_t, chunk_t *)) allocate_random_bytes; + this->public.get_pseudo_random_bytes = (status_t (*) (randomizer_t *,size_t, u_int8_t *)) get_pseudo_random_bytes; + this->public.allocate_pseudo_random_bytes = (status_t (*) (randomizer_t *,size_t, chunk_t *)) allocate_pseudo_random_bytes; + this->public.destroy = (void (*) (randomizer_t *))destroy; + + /* private functions */ + this->get_bytes_from_device = get_bytes_from_device; + + return &(this->public); +} diff --git a/src/libstrongswan/utils/randomizer.h b/src/libstrongswan/utils/randomizer.h new file mode 100644 index 000000000..afbade059 --- /dev/null +++ b/src/libstrongswan/utils/randomizer.h @@ -0,0 +1,114 @@ +/** + * @file randomizer.h + * + * @brief Interface of randomizer_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ + +#ifndef RANDOMIZER_H_ +#define RANDOMIZER_H_ + +typedef struct randomizer_t randomizer_t; + +#include + +#ifndef DEV_RANDOM +/** + * Device to read real random bytes + */ +# define DEV_RANDOM "/dev/random" +#endif + +#ifndef DEV_URANDOM +/** + * Device to read pseudo random bytes + */ +# define DEV_URANDOM "/dev/urandom" +#endif + +/** + * @brief Class used to get random and pseudo random values. + * + * @b Constructors: + * - randomizer_create() + * + * @ingroup utils + */ +struct randomizer_t { + + /** + * @brief Reads a specific number of bytes from random device. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to read + * @param[out] buffer pointer to buffer where to write the data in. + * Size of buffer has to be at least bytes. + * @return SUCCESS, or FAILED + */ + status_t (*get_random_bytes) (randomizer_t *this, size_t bytes, u_int8_t *buffer); + + /** + * @brief Allocates space and writes in random bytes. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to allocate + * @param[out] chunk chunk which will hold the allocated random bytes + * @return SUCCESS, or FAILED + */ + status_t (*allocate_random_bytes) (randomizer_t *this, size_t bytes, chunk_t *chunk); + + /** + * @brief Reads a specific number of bytes from pseudo random device. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to read + * @param[out] buffer pointer to buffer where to write the data in. + * size of buffer has to be at least bytes. + * @return SUCCESS, or FAILED + */ + status_t (*get_pseudo_random_bytes) (randomizer_t *this,size_t bytes, u_int8_t *buffer); + + /** + * @brief Allocates space and writes in pseudo random bytes. + * + * @param this calling randomizer_t object + * @param bytes number of bytes to allocate + * @param[out] chunk chunk which will hold the allocated random bytes + * @return SUCCESS, or FAILED + */ + status_t (*allocate_pseudo_random_bytes) (randomizer_t *this, size_t bytes, chunk_t *chunk); + + /** + * @brief Destroys a randomizer_t object. + * + * @param this randomizer_t object to destroy + */ + void (*destroy) (randomizer_t *this); +}; + +/** + * @brief Creates a randomizer_t object. + * + * @return created randomizer_t, or + * + * @ingroup utils + */ +randomizer_t *randomizer_create(void); + +#endif /*RANDOMIZER_H_*/ diff --git a/src/openac/Makefile.am b/src/openac/Makefile.am new file mode 100644 index 000000000..c1e2a593a --- /dev/null +++ b/src/openac/Makefile.am @@ -0,0 +1,98 @@ +ipsec_PROGRAMS = openac +openac_SOURCES = openac.c build.c build.h loglite.c + +INCLUDES = \ +-I$(top_srcdir)/src/libfreeswan \ +-I$(top_srcdir)/src/pluto \ +-I$(top_srcdir)/src/libcrypto \ +-I$(top_srcdir)/src/whack + +AM_CFLAGS = -DDEBUG -DNO_PLUTO -DIPSEC_CONFDIR=\"${confdir}\" +openac_LDADD = ac.o asn1.o ca.o certs.o constants.o crl.o defs.o mp_defs.o fetch.o id.o keys.o lex.o \ + md2.o md5.o ocsp.o oid.o pem.o pgp.o pkcs1.o rnd.o sha1.o smartcard.o x509.o \ + $(top_srcdir)/src/libfreeswan/libfreeswan.a $(top_srcdir)/src/libcrypto/libcrypto.a \ + -lgmp + +# This compile option activates dynamic URL fetching using libcurl +if USE_LIBCURL + openac_LDADD += -lcurl +endif + +# This compile option activates smartcard support +if USE_SMARTCARD + openac_LDADD += -ldl +endif + +dist_man_MANS = openac.8 + +PLUTODIR=$(top_srcdir)/src/pluto + +ac.o : $(PLUTODIR)/ac.c $(PLUTODIR)/ac.h + $(COMPILE) -c -o $@ $< + +asn1.o : $(PLUTODIR)/asn1.c $(PLUTODIR)/asn1.h + $(COMPILE) -c -o $@ $< + +ca.o : $(PLUTODIR)/ca.c $(PLUTODIR)/ca.h + $(COMPILE) -c -o $@ $< + +certs.o : $(PLUTODIR)/certs.c $(PLUTODIR)/certs.h + $(COMPILE) -c -o $@ $< + +constants.o : $(PLUTODIR)/constants.c $(PLUTODIR)/constants.h + $(COMPILE) -c -o $@ $< + +crl.o : $(PLUTODIR)/crl.c $(PLUTODIR)/crl.h + $(COMPILE) -c -o $@ $< + +defs.o : $(PLUTODIR)/defs.c $(PLUTODIR)/defs.h + $(COMPILE) -c -o $@ $< + +mp_defs.o : $(PLUTODIR)/mp_defs.c $(PLUTODIR)/mp_defs.h + $(COMPILE) -c -o $@ $< + +fetch.o : $(PLUTODIR)/fetch.c $(PLUTODIR)/fetch.h + $(COMPILE) -c -o $@ $< + +id.o : $(PLUTODIR)/id.c $(PLUTODIR)/id.h + $(COMPILE) -c -o $@ $< + +keys.o : $(PLUTODIR)/keys.c $(PLUTODIR)/keys.h + $(COMPILE) -c -o $@ $< + +lex.o : $(PLUTODIR)/lex.c $(PLUTODIR)/lex.h + $(COMPILE) -c -o $@ $< + +md2.o : $(PLUTODIR)/md2.c $(PLUTODIR)/md2.h + $(COMPILE) -c -o $@ $< + +md5.o : $(PLUTODIR)/md5.c $(PLUTODIR)/md5.h + $(COMPILE) -c -o $@ $< + +ocsp.o : $(PLUTODIR)/ocsp.c $(PLUTODIR)/ocsp.h + $(COMPILE) -c -o $@ $< + +oid.o : $(PLUTODIR)/oid.c $(PLUTODIR)/oid.h + $(COMPILE) -c -o $@ $< + +pem.o : $(PLUTODIR)/pem.c $(PLUTODIR)/pem.h + $(COMPILE) -c -o $@ $< + +pgp.o : $(PLUTODIR)/pgp.c $(PLUTODIR)/pgp.h + $(COMPILE) -c -o $@ $< + +pkcs1.o : $(PLUTODIR)/pkcs1.c $(PLUTODIR)/pkcs1.h + $(COMPILE) -c -o $@ $< + +rnd.o : $(PLUTODIR)/rnd.c $(PLUTODIR)/rnd.h + $(COMPILE) -c -o $@ $< + +sha1.o : $(PLUTODIR)/sha1.c $(PLUTODIR)/sha1.h + $(COMPILE) -c -o $@ $< + +smartcard.o : $(PLUTODIR)/smartcard.c $(PLUTODIR)/smartcard.h + $(COMPILE) -c -o $@ $< + +x509.o : $(PLUTODIR)/x509.c $(PLUTODIR)/x509.h + $(COMPILE) -c -o $@ $< + diff --git a/src/openac/Makefile.in b/src/openac/Makefile.in new file mode 100644 index 000000000..8a2bee51f --- /dev/null +++ b/src/openac/Makefile.in @@ -0,0 +1,624 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +ipsec_PROGRAMS = openac$(EXEEXT) + +# This compile option activates dynamic URL fetching using libcurl +@USE_LIBCURL_TRUE@am__append_1 = -lcurl + +# This compile option activates smartcard support +@USE_SMARTCARD_TRUE@am__append_2 = -ldl +subdir = src/openac +DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)" +ipsecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(ipsec_PROGRAMS) +am_openac_OBJECTS = openac.$(OBJEXT) build.$(OBJEXT) loglite.$(OBJEXT) +openac_OBJECTS = $(am_openac_OBJECTS) +am__DEPENDENCIES_1 = +openac_DEPENDENCIES = ac.o asn1.o ca.o certs.o constants.o crl.o \ + defs.o mp_defs.o fetch.o id.o keys.o lex.o md2.o md5.o ocsp.o \ + oid.o pem.o pgp.o pkcs1.o rnd.o sha1.o smartcard.o x509.o \ + $(top_srcdir)/src/libfreeswan/libfreeswan.a \ + $(top_srcdir)/src/libcrypto/libcrypto.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(openac_SOURCES) +DIST_SOURCES = $(openac_SOURCES) +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man_MANS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +openac_SOURCES = openac.c build.c build.h loglite.c +INCLUDES = \ +-I$(top_srcdir)/src/libfreeswan \ +-I$(top_srcdir)/src/pluto \ +-I$(top_srcdir)/src/libcrypto \ +-I$(top_srcdir)/src/whack + +AM_CFLAGS = -DDEBUG -DNO_PLUTO -DIPSEC_CONFDIR=\"${confdir}\" +openac_LDADD = ac.o asn1.o ca.o certs.o constants.o crl.o defs.o \ + mp_defs.o fetch.o id.o keys.o lex.o md2.o md5.o ocsp.o oid.o \ + pem.o pgp.o pkcs1.o rnd.o sha1.o smartcard.o x509.o \ + $(top_srcdir)/src/libfreeswan/libfreeswan.a \ + $(top_srcdir)/src/libcrypto/libcrypto.a -lgmp $(am__append_1) \ + $(am__append_2) +dist_man_MANS = openac.8 +PLUTODIR = $(top_srcdir)/src/pluto +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/openac/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/openac/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-ipsecPROGRAMS: $(ipsec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ipsecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-ipsecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +clean-ipsecPROGRAMS: + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +openac$(EXEEXT): $(openac_OBJECTS) $(openac_DEPENDENCIES) + @rm -f openac$(EXEEXT) + $(LINK) $(openac_LDFLAGS) $(openac_OBJECTS) $(openac_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/build.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loglite.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openac.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-ipsecPROGRAMS clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-ipsecPROGRAMS install-man + +install-exec-am: + +install-info: install-info-am + +install-man: install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-ipsecPROGRAMS uninstall-man + +uninstall-man: uninstall-man8 + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-ipsecPROGRAMS clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-ipsecPROGRAMS install-man install-man8 install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am \ + uninstall-ipsecPROGRAMS uninstall-man uninstall-man8 + + +ac.o : $(PLUTODIR)/ac.c $(PLUTODIR)/ac.h + $(COMPILE) -c -o $@ $< + +asn1.o : $(PLUTODIR)/asn1.c $(PLUTODIR)/asn1.h + $(COMPILE) -c -o $@ $< + +ca.o : $(PLUTODIR)/ca.c $(PLUTODIR)/ca.h + $(COMPILE) -c -o $@ $< + +certs.o : $(PLUTODIR)/certs.c $(PLUTODIR)/certs.h + $(COMPILE) -c -o $@ $< + +constants.o : $(PLUTODIR)/constants.c $(PLUTODIR)/constants.h + $(COMPILE) -c -o $@ $< + +crl.o : $(PLUTODIR)/crl.c $(PLUTODIR)/crl.h + $(COMPILE) -c -o $@ $< + +defs.o : $(PLUTODIR)/defs.c $(PLUTODIR)/defs.h + $(COMPILE) -c -o $@ $< + +mp_defs.o : $(PLUTODIR)/mp_defs.c $(PLUTODIR)/mp_defs.h + $(COMPILE) -c -o $@ $< + +fetch.o : $(PLUTODIR)/fetch.c $(PLUTODIR)/fetch.h + $(COMPILE) -c -o $@ $< + +id.o : $(PLUTODIR)/id.c $(PLUTODIR)/id.h + $(COMPILE) -c -o $@ $< + +keys.o : $(PLUTODIR)/keys.c $(PLUTODIR)/keys.h + $(COMPILE) -c -o $@ $< + +lex.o : $(PLUTODIR)/lex.c $(PLUTODIR)/lex.h + $(COMPILE) -c -o $@ $< + +md2.o : $(PLUTODIR)/md2.c $(PLUTODIR)/md2.h + $(COMPILE) -c -o $@ $< + +md5.o : $(PLUTODIR)/md5.c $(PLUTODIR)/md5.h + $(COMPILE) -c -o $@ $< + +ocsp.o : $(PLUTODIR)/ocsp.c $(PLUTODIR)/ocsp.h + $(COMPILE) -c -o $@ $< + +oid.o : $(PLUTODIR)/oid.c $(PLUTODIR)/oid.h + $(COMPILE) -c -o $@ $< + +pem.o : $(PLUTODIR)/pem.c $(PLUTODIR)/pem.h + $(COMPILE) -c -o $@ $< + +pgp.o : $(PLUTODIR)/pgp.c $(PLUTODIR)/pgp.h + $(COMPILE) -c -o $@ $< + +pkcs1.o : $(PLUTODIR)/pkcs1.c $(PLUTODIR)/pkcs1.h + $(COMPILE) -c -o $@ $< + +rnd.o : $(PLUTODIR)/rnd.c $(PLUTODIR)/rnd.h + $(COMPILE) -c -o $@ $< + +sha1.o : $(PLUTODIR)/sha1.c $(PLUTODIR)/sha1.h + $(COMPILE) -c -o $@ $< + +smartcard.o : $(PLUTODIR)/smartcard.c $(PLUTODIR)/smartcard.h + $(COMPILE) -c -o $@ $< + +x509.o : $(PLUTODIR)/x509.c $(PLUTODIR)/x509.h + $(COMPILE) -c -o $@ $< +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/openac/build.c b/src/openac/build.c new file mode 100644 index 000000000..bd3df6fee --- /dev/null +++ b/src/openac/build.c @@ -0,0 +1,242 @@ +/* Build a X.509 attribute certificate + * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler + * Copyright (C) 2004 Andreas Steffen + * Zuercher Hochschule Winterthur, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: build.c,v 1.14 2005/09/06 11:47:57 as Exp $ + */ + +#include +#include + +#include + +#include "../pluto/constants.h" +#include "../pluto/defs.h" +#include "../pluto/oid.h" +#include "../pluto/asn1.h" +#include "../pluto/x509.h" +#include "../pluto/log.h" + +#include "build.h" + +static u_char ASN1_group_oid_str[] = { + 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x0a ,0x04 +}; + +static const chunk_t ASN1_group_oid = strchunk(ASN1_group_oid_str); + +static u_char ASN1_authorityKeyIdentifier_oid_str[] = { + 0x06, 0x03, + 0x55, 0x1d, 0x23 +}; + +static const chunk_t ASN1_authorityKeyIdentifier_oid + = strchunk(ASN1_authorityKeyIdentifier_oid_str); + +static u_char ASN1_noRevAvail_ext_str[] = { + 0x30, 0x09, + 0x06, 0x03, + 0x55, 0x1d, 0x38, + 0x04, 0x02, + 0x05, 0x00 +}; + +static const chunk_t ASN1_noRevAvail_ext = strchunk(ASN1_noRevAvail_ext_str); + +/* + * build directoryName + */ +static chunk_t +build_directoryName(asn1_t tag, chunk_t name) +{ + return asn1_wrap(tag, "m" + , asn1_simple_object(ASN1_CONTEXT_C_4, name)); +} + +/* + * build holder + */ +static chunk_t +build_holder(void) +{ + return asn1_wrap(ASN1_SEQUENCE, "mm" + , asn1_wrap(ASN1_CONTEXT_C_0, "mm" + , build_directoryName(ASN1_SEQUENCE, user->issuer) + , asn1_simple_object(ASN1_INTEGER, user->serialNumber) + ) + , build_directoryName(ASN1_CONTEXT_C_1, user->subject)); +} + +/* + * build v2Form + */ +static chunk_t +build_v2_form(void) +{ + return asn1_wrap(ASN1_CONTEXT_C_0, "m" + , build_directoryName(ASN1_SEQUENCE, signer->subject)); +} + +/* + * build attrCertValidityPeriod + */ +static chunk_t +build_attr_cert_validity(void) +{ + return asn1_wrap(ASN1_SEQUENCE, "mm" + , timetoasn1(¬Before, ASN1_GENERALIZEDTIME) + , timetoasn1(¬After, ASN1_GENERALIZEDTIME)); +} + +/* + * build attributes + */ +static chunk_t +build_ietfAttributes(ietfAttrList_t *list) +{ + chunk_t ietfAttributes; + ietfAttrList_t *item = list; + size_t size = 0; + u_char *pos; + + /* precalculate the total size of all values */ + while (item != NULL) + { + size_t len = item->attr->value.len; + + size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len; + item = item->next; + } + pos = build_asn1_object(&ietfAttributes, ASN1_SEQUENCE, size); + + while (list != NULL) + { + ietfAttr_t *attr = list->attr; + asn1_t type = ASN1_NULL; + + switch (attr->kind) + { + case IETF_ATTRIBUTE_OCTETS: + type = ASN1_OCTET_STRING; + break; + case IETF_ATTRIBUTE_STRING: + type = ASN1_UTF8STRING; + break; + case IETF_ATTRIBUTE_OID: + type = ASN1_OID; + break; + } + mv_chunk(&pos, asn1_simple_object(type, attr->value)); + + list = list->next; + } + + return asn1_wrap(ASN1_SEQUENCE, "m", ietfAttributes); +} + +/* + * build attribute type + */ +static chunk_t +build_attribute_type(const chunk_t type, chunk_t content) +{ + return asn1_wrap(ASN1_SEQUENCE, "cm" + , type + , asn1_wrap(ASN1_SET, "m", content)); +} + +/* + * build attributes + */ +static chunk_t +build_attributes(void) +{ + return asn1_wrap(ASN1_SEQUENCE, "m" + , build_attribute_type(ASN1_group_oid + , build_ietfAttributes(groups))); +} + +/* + * build authorityKeyIdentifier + */ +static chunk_t +build_authorityKeyID(x509cert_t *signer) +{ + chunk_t keyIdentifier = (signer->subjectKeyID.ptr == NULL) + ? empty_chunk + : asn1_simple_object(ASN1_CONTEXT_S_0 + , signer->subjectKeyID); + + chunk_t authorityCertIssuer = build_directoryName(ASN1_CONTEXT_C_1 + , signer->issuer); + + chunk_t authorityCertSerialNumber = asn1_simple_object(ASN1_CONTEXT_S_2 + , signer->serialNumber); + + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_authorityKeyIdentifier_oid + , asn1_wrap(ASN1_OCTET_STRING, "m" + , asn1_wrap(ASN1_SEQUENCE, "mmm" + , keyIdentifier + , authorityCertIssuer + , authorityCertSerialNumber + ) + ) + ); +} + +/* + * build extensions + */ +static chunk_t +build_extensions(void) +{ + return asn1_wrap(ASN1_SEQUENCE, "mc" + , build_authorityKeyID(signer) + , ASN1_noRevAvail_ext); +} + +/* + * build attributeCertificateInfo + */ +static chunk_t +build_attr_cert_info(void) +{ + return asn1_wrap(ASN1_SEQUENCE, "cmmcmmmm" + , ASN1_INTEGER_1 + , build_holder() + , build_v2_form() + , ASN1_sha1WithRSA_id + , asn1_simple_object(ASN1_INTEGER, serial) + , build_attr_cert_validity() + , build_attributes() + , build_extensions()); +} + +/* + * build an X.509 attribute certificate + */ +chunk_t +build_attr_cert(void) +{ + chunk_t attributeCertificateInfo = build_attr_cert_info(); + chunk_t signatureValue = pkcs1_build_signature(attributeCertificateInfo + , OID_SHA1, signerkey, TRUE); + + return asn1_wrap(ASN1_SEQUENCE, "mcm" + , attributeCertificateInfo + , ASN1_sha1WithRSA_id + , signatureValue); +} diff --git a/src/openac/build.h b/src/openac/build.h new file mode 100644 index 000000000..deeddda04 --- /dev/null +++ b/src/openac/build.h @@ -0,0 +1,47 @@ +/* Build a X.509 attribute certificate + * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler + * Copyright (C) 2004 Andreas Steffen + * Zuercher Hochschule Winterthur, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: build.h,v 1.4 2004/11/03 14:28:52 as Exp $ + */ + +#ifndef _BUILD_H +#define _BUILD_H + +#include + +#include "../pluto/x509.h" +#include "../pluto/keys.h" +#include "../pluto/ac.h" + +/* + * global variables accessible by both main() and build.c + */ +extern x509cert_t *user; +extern x509cert_t *signer; + +extern ietfAttrList_t *groups; +extern struct RSA_private_key *signerkey; + +extern time_t notBefore; +extern time_t notAfter; + +extern chunk_t serial; + +/* + * exported functions + */ +extern chunk_t build_attr_cert(void); + +#endif /* _BUILD_H */ diff --git a/src/openac/loglite.c b/src/openac/loglite.c new file mode 100644 index 000000000..4219eb707 --- /dev/null +++ b/src/openac/loglite.c @@ -0,0 +1,295 @@ +/* error logging functions + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: loglite.c,v 1.2 2005/07/11 18:38:16 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* used only if MSG_NOSIGNAL not defined */ +#include +#include +#include + +#include + +#include +#include +#include +#include + +bool + log_to_stderr = FALSE, /* should log go to stderr? */ + log_to_syslog = TRUE; /* should log go to syslog? */ + +void +init_log(const char *program) +{ + if (log_to_stderr) + setbuf(stderr, NULL); + if (log_to_syslog) + openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); +} + +void +close_log(void) +{ + if (log_to_syslog) + closelog(); +} + +void +plog(const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "%s\n", m); + if (log_to_syslog) + syslog(LOG_WARNING, "%s", m); +} + +void +loglog(int mess_no, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "%s\n", m); + if (log_to_syslog) + syslog(LOG_WARNING, "%s", m); +} + +void +log_errno_routine(int e, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "ERROR: %s. Errno %d: %s\n", m, e, strerror(e)); + if (log_to_syslog) + syslog(LOG_ERR, "ERROR: %s. Errno %d: %s", m, e, strerror(e)); +} + +void +exit_log(const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "FATAL ERROR: %s\n", m); + if (log_to_syslog) + syslog(LOG_ERR, "FATAL ERROR: %s", m); + exit(1); +} + +void +exit_log_errno_routine(int e, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "FATAL ERROR: %s. Errno %d: %s\n", m, e, strerror(e)); + if (log_to_syslog) + syslog(LOG_ERR, "FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e)); + exit(1); +} + +void +whack_log(int mess_no, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + fprintf(stderr, "%s\n", m); +} + +/* Build up a diagnostic in a static buffer. + * Although this would be a generally useful function, it is very + * hard to come up with a discipline that prevents different uses + * from interfering. It is intended that by limiting it to building + * diagnostics, we will avoid this problem. + * Juggling is performed to allow an argument to be a previous + * result: the new string may safely depend on the old one. This + * restriction is not checked in any way: violators will produce + * confusing results (without crashing!). + */ +char diag_space[sizeof(diag_space)]; + +err_t +builddiag(const char *fmt, ...) +{ + static char diag_space[LOG_WIDTH]; /* longer messages will be truncated */ + char t[sizeof(diag_space)]; /* build result here first */ + va_list args; + + va_start(args, fmt); + t[0] = '\0'; /* in case nothing terminates string */ + vsnprintf(t, sizeof(t), fmt, args); + va_end(args); + strcpy(diag_space, t); + return diag_space; +} + +/* Debugging message support */ + +#ifdef DEBUG + +void +switch_fail(int n, const char *file_str, unsigned long line_no) +{ + char buf[30]; + + snprintf(buf, sizeof(buf), "case %d unexpected", n); + passert_fail(buf, file_str, line_no); +} + +void +passert_fail(const char *pred_str, const char *file_str, unsigned long line_no) +{ + /* we will get a possibly unplanned prefix. Hope it works */ + loglog(RC_LOG_SERIOUS, "ASSERTION FAILED at %s:%lu: %s", file_str, line_no, pred_str); + abort(); /* exiting correctly doesn't always work */ +} + +lset_t + base_debugging = DBG_NONE, /* default to reporting nothing */ + cur_debugging = DBG_NONE; + +void +pexpect_log(const char *pred_str, const char *file_str, unsigned long line_no) +{ + /* we will get a possibly unplanned prefix. Hope it works */ + loglog(RC_LOG_SERIOUS, "EXPECTATION FAILED at %s:%lu: %s", file_str, line_no, pred_str); +} + +/* log a debugging message (prefixed by "| ") */ + +void +DBG_log(const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "| %s\n", m); + if (log_to_syslog) + syslog(LOG_DEBUG, "| %s", m); +} + +/* dump raw bytes in hex to stderr (for lack of any better destination) */ + +void +DBG_dump(const char *label, const void *p, size_t len) +{ +# define DUMP_LABEL_WIDTH 20 /* arbitrary modest boundary */ +# define DUMP_WIDTH (4 * (1 + 4 * 3) + 1) + char buf[DUMP_LABEL_WIDTH + DUMP_WIDTH]; + char *bp; + const unsigned char *cp = p; + + bp = buf; + + if (label != NULL && label[0] != '\0') + { + /* Handle the label. Care must be taken to avoid buffer overrun. */ + size_t llen = strlen(label); + + if (llen + 1 > sizeof(buf)) + { + DBG_log("%s", label); + } + else + { + strcpy(buf, label); + if (buf[llen-1] == '\n') + { + buf[llen-1] = '\0'; /* get rid of newline */ + DBG_log("%s", buf); + } + else if (llen < DUMP_LABEL_WIDTH) + { + bp = buf + llen; + } + else + { + DBG_log("%s", buf); + } + } + } + + do { + int i, j; + + for (i = 0; len!=0 && i!=4; i++) + { + *bp++ = ' '; + for (j = 0; len!=0 && j!=4; len--, j++) + { + static const char hexdig[] = "0123456789abcdef"; + + *bp++ = ' '; + *bp++ = hexdig[(*cp >> 4) & 0xF]; + *bp++ = hexdig[*cp & 0xF]; + cp++; + } + } + *bp = '\0'; + DBG_log("%s", buf); + bp = buf; + } while (len != 0); +# undef DUMP_LABEL_WIDTH +# undef DUMP_WIDTH +} + +#endif /* DEBUG */ diff --git a/src/openac/openac.8 b/src/openac/openac.8 new file mode 100644 index 000000000..8e609a1b1 --- /dev/null +++ b/src/openac/openac.8 @@ -0,0 +1,180 @@ +.TH IPSEC_OPENAC 8 "29 September 2005" +.SH NAME +ipsec openac \- Generation of X.509 attribute certificates +.SH SYNOPSIS +.B ipsec +.B openac +[ +.B \-\-help +] [ +.B \-\-version +] [ +.B \-\-optionsfrom +\fIfilename\fP +] [ +.B \-\-quiet +] +.br +\ \ \ [ +.B \-\-debug\(hyall +] [ +.B \-\-debug\(hyparsing +] [ +.B \-\-debug\(hyraw +] [ +.B \-\-debug\(hyprivate +] +.br +\ \ \ [ +.B \-\-days +\fIdays\fP +] [ +.B \-\-hours +\fIhours\fP +] +.br +\ \ \ [ +.B \-\-startdate +\fIYYYYMMDDHHMMSSZ\fP +] [ +.B \-\-stopdate +\fIYYYYMMDDHHMMSSZ\fP +] +.br +.B \ \ \ \-\-cert +\fIcertfile\fP +.B \-\-key +\fIkeyfile\fP +[ +.B \-\-password +\fIpassword\fP +] +.br +.B \ \ \ \-\-usercert +\fIcertfile\fP +.B \-\-groups +\fIattr1,attr2,...\fP +.B \-\-out +\fIfilename\fP +.SH DESCRIPTION +.BR openac +is intended to be used by an Authorization Authority (AA) to generate and sign +X.509 attribute certificates. Currently only the inclusion of one ore several group +attributes is supported. An attribute certificate is linked to a holder by +including the issuer and serial number of the holder's X.509 certificate. +.SH OPTIONS +.TP +\fB\-\-help\fP +display the usage message. +.TP +\fB\-\-version\fP +display the version of \fBopenac\fP. +.TP +\fB\-\-optionsfrom\fP\ \fIfilename\fP +adds the contents of the file to the argument list. +If \fIfilename\fP is a relative path then the file is searched in the directory +\fI/etc/openac\fP. +.TP +\fB\-\-quiet\fP +By default \fBopenac\fP logs all control output both to syslog and stderr. +With the \fB\-\-quiet\fP option no output is written to stderr. +.TP +\fB\-\-days\fP\ \fIdays\fP +Validity of the X.509 attribute certificate in days. If neiter the \fB\-\-days\fP\ nor +the \fB\-\-hours\fP\ option is specified then a default validity interval of 1 day is assumed. +The \fB\-\-days\fP\ option can be combined with the \fB\-\-hours\fP\ option. +.TP +\fB\-\-hours\fP\ \fIhours\fP +Validity of the X.509 attribute certificate in hours. If neiter the \fB\-\-hours\fP\ nor +the \fB\-\-days\fP\ option is specified then a default validity interval of 24 hours is assumed. +The \fB\-\-hours\fP\ option can be combined with the \fB\-\-days\fP\ option. +.TP +\fB\-\-startdate\fP\ \fIYYYYMMDDHHMMSSZ\fP +defines the \fBnotBefore\fP date when the X.509 attribute certificate becomes valid. +The date \fIYYYYMMDDHHMMSS\fP must be specified in UTC (\fIZ\fPulu time). +If the \fB\-\-startdate\fP option is not specified then the current date is taken as a default. + +.TP +\fB\-\-stopdate\fP\ \fIYYYYMMDDHHMMSSZ\fP +defines the \fBnotAfter\fP date when the X.509 attribute certificate will expire. +The date \fIYYYYMMDDHHMMSS\fP must be specified in UTC (\fIZ\fPulu time). +If the \fB\-\-stopdate\fP option is not specified then the default \fBnotAfter\fP value is computed +by adding the validity interval specified by the \fB\-\-days\fP\ and/or \fB\-\-days\fP\ options +to the \fBnotBefore\fP date. +.TP +\fB\-\-cert\fP\ \fIcertfile\fP +specifies the file containing the X.509 certificate of the Authorization Authority. +The certificate is stored either in PEM or DER format. +.TP +\fB\-\-key\fP\ \fIkeyfile\fP +specifies the encrypted file containing the private RSA key of the Authoritzation +Authority. The private key is stored in PKCS#1 format. +.TP +\fB\-\-password\fP\ \fIpassword\fP +specifies the password with which the private RSA keyfile defined by the +\fB\-\-key\fP option has been protected. If the option is missing then the +password is prompted for on the command line. +.TP +\fB\-\-usercert\fP\ \fIcertfile\fP +specifies file containing the X.509 certificate of the user to which the generated attribute +certificate will apply. The certificate file is stored either in PEM or DER format. +.TP +\fB\-\-groups\fP\ \fIattr1,attr2\fP +specifies a comma-separated list of group attributes that will go into the +X.509 attribute certificate. +.TP +\fB\-\-out\fP\ \fIfilename\fP +specifies the file where the generated X.509 attribute certificate will be stored to. +.SS Debugging +.LP +\fBopenac\fP produces a prodigious amount of debugging information. To do so, +it must be compiled with \-DDEBUG. There are several classes of debugging output, +and \fBopenac\fP may be directed to produce a selection of them. All lines of +debugging output are prefixed with ``|\ '' to distinguish them from error messages. +.LP +When \fBopenac\fP is invoked, it may be given arguments to specify +which classes to output. The current options are: +.TP +\fB\-\-debug-raw\fP +show the raw bytes of the parsed user and authorization authority certificates +as well as of the generated X.509 attribute certificate. +.TP +\fB\-\-debug-parsing\fP +show the parsed structure of user and authorization authority certificats +as well as of the generated X.509 attribute certificate. +.TP +\fB\-\-debug-all\fP +all of the above. +.TP +\fB\-\-debug-private\fP +enables debugging output of the authorization authority's private key. +.SH EXIT STATUS +.LP +The execution of \fBopenac\fP terminates with one of the following two exit codes: +.TP +0 +means that the attribute certificate was successfully generated and stored. +.TP +1 +means that something went wrong. +.SH FILES +\fI/etc/openac/serial\fP\ \ \ serial number of latest attribute certificate +.SH SEE ALSO +.LP +The X.509 attribute certificates generated with \fBopenac\fP can be used to +enforce group policies defined by \fIipsec.conf\fP(5). Use \fIipsec_auto\fP(8) +to load and list X.509 attribute certificates. +.LP +For more information on X.509 attribute certificates, refer to the following +IETF RFC: +.IP +RFC 3281 An Internet Attribute Certificate Profile for Authorization +.SH HISTORY +The \fBopenac\fP program was originally written by Ariane Seiler and Ueli Galizzi. +The software was recoded by Andreas Steffen using strongSwan's X.509 library and +the ASN.1 code synthesis functions written by Christoph Gysin and Christoph Zwahlen. +All authors were with the Zurich University of Applied Sciences in Winterthur, +Switzerland. +.LP +.SH BUGS +Bugs should be reported to the mailing list. diff --git a/src/openac/openac.c b/src/openac/openac.c new file mode 100755 index 000000000..00f287b3a --- /dev/null +++ b/src/openac/openac.c @@ -0,0 +1,438 @@ +/* Generation of X.509 attribute certificates + * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler + * Copyright (C) 2004 Andreas Steffen + * Zuercher Hochschule Winterthur, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: openac.c,v 1.18 2006/01/04 21:12:33 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../pluto/constants.h" +#include "../pluto/defs.h" +#include "../pluto/mp_defs.h" +#include "../pluto/log.h" +#include "../pluto/asn1.h" +#include "../pluto/certs.h" +#include "../pluto/x509.h" +#include "../pluto/crl.h" +#include "../pluto/keys.h" +#include "../pluto/ac.h" + +#include "build.h" + +#define OPENAC_PATH IPSEC_CONFDIR "/openac" +#define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial" + +const char openac_version[] = "openac 0.3"; + +/* by default the CRL policy is lenient */ +bool strict_crl_policy = FALSE; + +/* by default pluto does not check crls dynamically */ +long crl_check_interval = 0; + +/* by default pluto logs out after every smartcard use */ +bool pkcs11_keep_state = FALSE; + +static void +usage(const char *mess) +{ + if (mess != NULL && *mess != '\0') + fprintf(stderr, "%s\n", mess); + fprintf(stderr + , "Usage: openac" + " [--help]" + " [--version]" + " [--optionsfrom ]" + " [--quiet]" +#ifdef DEBUG + " \\\n\t" + " [--debug-all]" + " [--debug-parsing]" + " [--debug-raw]" + " [--debug-private]" +#endif + " \\\n\t" + " [--days ]" + " [--hours ]" + " \\\n\t" + " [--startdate ]" + " [--enddate ]" + " \\\n\t" + " --cert " + " --key " + " [--password ]" + " \\\n\t" + " --usercert " + " --groups " + " --out " + "\n" + ); + exit(mess == NULL? 0 : 1); +} + +/* + * read the last serial number from file + */ +static chunk_t +read_serial(void) +{ + MP_INT number; + + char buf[BUF_LEN]; + char bytes[BUF_LEN]; + + FILE *fd = fopen(OPENAC_SERIAL, "r"); + + /* serial number defaults to 0 */ + size_t len = 1; + bytes[0] = 0x00; + + if (fd) + { + if (fscanf(fd, "%s", buf)) + { + err_t ugh = ttodata(buf, 0, 16, bytes, BUF_LEN, &len); + + if (ugh != NULL) + plog(" error reading serial number from %s: %s" + , OPENAC_SERIAL, ugh); + } + fclose(fd); + } + else + plog(" file '%s' does not exist yet - serial number set to 01" + , OPENAC_SERIAL); + + /* conversion of read serial number to a multiprecision integer + * and incrementing it by one + * and representing it as a two's complement octet string + */ + n_to_mpz(&number, bytes, len); + mpz_add_ui(&number, &number, 0x01); + serial = mpz_to_n(&number, 1 + mpz_sizeinbase(&number, 2)/BITS_PER_BYTE); + mpz_clear(&number); + + return serial; +} + +/* + * write back the last serial number to file + */ +static void +write_serial(chunk_t serial) +{ + char buf[BUF_LEN]; + + FILE *fd = fopen(OPENAC_SERIAL, "w"); + + if (fd) + { + datatot(serial.ptr, serial.len, 16, buf, BUF_LEN); + plog(" serial number is %s", buf); + fprintf(fd, "%s\n", buf); + fclose(fd); + } + else + plog(" could not open file '%s' for writing", OPENAC_SERIAL); +} + +/* + * global variables accessible by both main() and build.c + */ +x509cert_t *user = NULL; +x509cert_t *signer = NULL; + +ietfAttrList_t *groups = NULL; +struct RSA_private_key *signerkey = NULL; + +time_t notBefore = 0; +time_t notAfter = 0; + +chunk_t serial; + + +int +main(int argc, char **argv) +{ + char *keyfile = NULL; + char *certfile = NULL; + char *usercertfile = NULL; + char *outfile = NULL; + + cert_t signercert = empty_cert; + cert_t usercert = empty_cert; + + chunk_t attr_cert = empty_chunk; + x509acert_t *ac = NULL; + + const time_t default_validity = 24*3600; /* 24 hours */ + time_t validity = 0; + + prompt_pass_t pass; + + pass.secret[0] = '\0'; + pass.prompt = TRUE; + pass.fd = STDIN_FILENO; + + log_to_stderr = TRUE; + + /* handle arguments */ + for (;;) + { +# define DBG_OFFSET 256 + static const struct option long_opts[] = { + /* name, has_arg, flag, val */ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "optionsfrom", required_argument, NULL, '+' }, + { "quiet", no_argument, NULL, 'q' }, + { "cert", required_argument, NULL, 'c' }, + { "key", required_argument, NULL, 'k' }, + { "password", required_argument, NULL, 'p' }, + { "usercert", required_argument, NULL, 'u' }, + { "groups", required_argument, NULL, 'g' }, + { "days", required_argument, NULL, 'D' }, + { "hours", required_argument, NULL, 'H' }, + { "startdate", required_argument, NULL, 'S' }, + { "enddate", required_argument, NULL, 'E' }, + { "out", required_argument, NULL, 'o' }, +#ifdef DEBUG + { "debug-all", no_argument, NULL, 'A' }, + { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET }, + { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET }, + { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET }, +#endif + { 0,0,0,0 } + }; + + int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:", long_opts, NULL); + + /* Note: "breaking" from case terminates loop */ + switch (c) + { + case EOF: /* end of flags */ + break; + + case 0: /* long option already handled */ + continue; + + case ':': /* diagnostic already printed by getopt_long */ + case '?': /* diagnostic already printed by getopt_long */ + usage(NULL); + break; /* not actually reached */ + + case 'h': /* --help */ + usage(NULL); + break; /* not actually reached */ + + case 'v': /* --version */ + printf("%s\n", openac_version); + exit(0); + break; /* not actually reached */ + + case '+': /* --optionsfrom */ + { + char path[BUF_LEN]; + + if (*optarg == '/') /* absolute pathname */ + strncpy(path, optarg, BUF_LEN); + else /* relative pathname */ + snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg); + optionsfrom(path, &argc, &argv, optind, stderr); + /* does not return on error */ + } + continue; + + case 'q': /* --quiet */ + log_to_stderr = TRUE; + continue; + + case 'c': /* --cert */ + certfile = optarg; + continue; + + case 'k': /* --key */ + keyfile = optarg; + continue; + + case 'p': /* --key */ + pass.prompt = FALSE; + strncpy(pass.secret, optarg, sizeof(pass.secret)); + continue; + + case 'u': /* --usercert */ + usercertfile = optarg; + continue; + + case 'g': /* --groups */ + decode_groups(optarg, &groups); + continue; + + case 'D': /* --days */ + if (optarg == NULL || !isdigit(optarg[0])) + usage("missing number of days"); + { + char *endptr; + long days = strtol(optarg, &endptr, 0); + + if (*endptr != '\0' || endptr == optarg + || days <= 0) + usage(" must be a positive number"); + validity += 24*3600*days; + } + continue; + + case 'H': /* --hours */ + if (optarg == NULL || !isdigit(optarg[0])) + usage("missing number of hours"); + { + char *endptr; + long hours = strtol(optarg, &endptr, 0); + + if (*endptr != '\0' || endptr == optarg + || hours <= 0) + usage(" must be a positive number"); + validity += 3600*hours; + } + continue; + + case 'S': /* --startdate */ + if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z') + usage("date format must be YYYYMMDDHHMMSSZ"); + { + chunk_t date = { optarg, 15 }; + notBefore = asn1totime(&date, ASN1_GENERALIZEDTIME); + } + continue; + + case 'E': /* --enddate */ + if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z') + usage("date format must be YYYYMMDDHHMMSSZ"); + { + chunk_t date = { optarg, 15 }; + notAfter = asn1totime(&date, ASN1_GENERALIZEDTIME); + } + continue; + + case 'o': /* --outt */ + outfile = optarg; + continue ; + +#ifdef DEBUG + case 'A': /* --debug-all */ + base_debugging = DBG_ALL; + continue; +#endif + default: +#ifdef DEBUG + if (c >= DBG_OFFSET) + { + base_debugging |= c - DBG_OFFSET; + continue; + } +#undef DBG_OFFSET +#endif + bad_case(c); + } + break; + } + + init_log("openac"); + cur_debugging = base_debugging; + + if (optind != argc) + usage("unexpected argument"); + + /* load the signer's RSA private key */ + if (keyfile != NULL) + { + err_t ugh = NULL; + + signerkey = alloc_thing(RSA_private_key_t, "RSA private key"); + ugh = load_rsa_private_key(keyfile, &pass, signerkey); + + if (ugh != NULL) + { + free_RSA_private_content(signerkey); + pfree(signerkey); + plog("%s", ugh); + exit(1); + } + } + + /* load the signer's X.509 certificate */ + if (certfile != NULL) + { + if (!load_cert(certfile, "signer cert", &signercert)) + exit(1); + signer = signercert.u.x509; + } + + /* load the users's X.509 certificate */ + if (usercertfile != NULL) + { + if (!load_cert(usercertfile, "user cert", &usercert)) + exit(1); + user = usercert.u.x509; + } + + /* compute validity interval */ + validity = (validity)? validity : default_validity; + notBefore = (notBefore) ? notBefore : time(NULL); + notAfter = (notAfter) ? notAfter : notBefore + validity; + + /* build and parse attribute certificate */ + if (user != NULL && signer != NULL && signerkey != NULL) + { + /* read the serial number and increment it by one */ + serial = read_serial(); + + attr_cert = build_attr_cert(); + ac = alloc_thing(x509acert_t, "x509acert"); + *ac = empty_ac; + parse_ac(attr_cert, ac); + + /* write the attribute certificate to file */ + if (write_chunk(outfile, "attribute cert", attr_cert, 0022, TRUE)) + write_serial(serial); + } + + /* delete all dynamic objects */ + if (signerkey != NULL) + { + free_RSA_private_content(signerkey); + pfree(signerkey); + } + free_x509cert(signercert.u.x509); + free_x509cert(usercert.u.x509); + free_ietfAttrList(groups); + free_acert(ac); + pfree(serial.ptr); + +#ifdef LEAK_DETECTIVE + report_leaks(); +#endif /* LEAK_DETECTIVE */ + close_log(); + exit(0); +} diff --git a/src/pluto/Makefile.am b/src/pluto/Makefile.am new file mode 100644 index 000000000..b1b848c76 --- /dev/null +++ b/src/pluto/Makefile.am @@ -0,0 +1,140 @@ +# Makefile.am was ported from the old Makefile the most +# painless way. Only the most important options are included, +# further work may be necessary here... + +ipsec_PROGRAMS = pluto _pluto_adns + +pluto_SOURCES = \ +ac.c ac.h \ +alg_info.c alg_info.h \ +asn1.c asn1.h \ +ca.c ca.h \ +certs.c certs.h \ +connections.c connections.h \ +constants.c constants.h \ +cookie.c cookie.h \ +crl.c crl.h \ +crypto.c crypto.h \ +db_ops.c db_ops.h \ +defs.c defs.h \ +demux.c demux.h \ +dnskey.c dnskey.h \ +dsa.c dsa.h \ +elgamal.c elgamal.h \ +fetch.c fetch.h \ +foodgroups.c foodgroups.h \ +gcryptfix.c gcryptfix.h \ +id.c id.h \ +ike_alg.c ike_alg.h \ +ipsec_doi.c ipsec_doi.h \ +kameipsec.h \ +kernel.c kernel.h \ +kernel_alg.c kernel_alg.h \ +kernel_netlink.c kernel_netlink.h \ +kernel_noklips.c kernel_noklips.h \ +kernel_pfkey.c kernel_pfkey.h \ +keys.c keys.h \ +lex.c lex.h \ +log.c log.h \ +md2.c md2.h \ +md5.c md5.h \ +modecfg.c modecfg.h \ +mp_defs.c mp_defs.h \ +nat_traversal.c nat_traversal.h \ +ocsp.c ocsp.h \ +oid.c oid.h \ +packet.c packet.h \ +pem.c pem.h \ +pgp.c pgp.h \ +pkcs1.c pkcs1.h \ +pkcs7.c pkcs7.h \ +plutomain.c \ +primegen.c smallprime.c \ +rcv_whack.c rcv_whack.h \ +rnd.c rnd.h \ +server.c server.h \ +sha1.c sha1.h \ +smartcard.c smartcard.h \ +spdb.c spdb.h \ +state.c state.h \ +timer.c timer.h \ +vendor.c vendor.h \ +virtual.c virtual.h \ +xauth.c xauth.h \ +x509.c x509.h \ +alg/ike_alg_aes.c alg/ike_alg_blowfish.c alg/ike_alg_twofish.c \ +alg/ike_alg_serpent.c alg/ike_alg_sha2.c alg/ike_alginit.c \ +linux26/netlink.h linux26/rtnetlink.h linux26/xfrm.h \ +rsaref/pkcs11t.h rsaref/pkcs11.h rsaref/unix.h rsaref/pkcs11f.h + +_pluto_adns_SOURCES = adns.c adns.h + +INCLUDES = \ +-I$(top_srcdir)/src/libfreeswan \ +-I$(top_srcdir)/src/libcrypto \ +-I$(top_srcdir)/src/whack + +AM_CFLAGS = \ +-DIPSEC_DIR=\"${ipsecdir}\" \ +-DIPSEC_CONFDIR=\"${confdir}\" \ +-DIPSEC_PIDDIR=\"${piddir}\" \ +-DSHARED_SECRETS_FILE=\"${confdir}/ipsec.secrets\" \ +-DKERNEL26_SUPPORT -DKERNEL26_HAS_KAME_DUPLICATES \ +-DPLUTO -DKLIPS -DDEBUG -DTHREADS + +pluto_LDADD = \ +$(top_srcdir)/src/libfreeswan/libfreeswan.a \ +$(top_srcdir)/src/libcrypto/libcrypto.a \ +-lgmp -lresolv -lpthread -ldl + +_pluto_adns_LDADD = \ +$(top_srcdir)/src/libfreeswan/libfreeswan.a \ +-lresolv -ldl + +dist_man_MANS = pluto.8 ipsec.secrets.5 +EXTRA_DIST = oid.pl oid.txt +BUILT_SOURCES = oid.c oid.h +MAINTAINERCLEANFILES = oid.c oid.h + +oid.c: oid.txt oid.pl + $(PERL) oid.pl + +oid.h: oid.txt oid.pl + $(PERL) oid.pl + +# This compile option activates the sending of a strongSwan VID +if USE_VENDORID + AM_CFLAGS += -DVENDORID +endif + +# This compile option activates the support of the Cisco VPN client +if USE_CISCO_QUIRKS + AM_CFLAGS += -DCISCO_QUIRKS +endif + +# This compile option activates NAT traversal with IPSec transport mode +if USE_NAT_TRANSPORT + AM_CFLAGS += -DI_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT +endif + +# This compile option activates dynamic URL fetching using libcurl +if USE_LIBCURL + pluto_LDADD += -lcurl +endif + +# This compile option activates dynamic LDAP CRL fetching +if USE_LIBLDAP + pluto_LDADD += -lldap -llber +endif + +install-exec-local : + mkdir -p -m 755 $(confdir)/ipsec.d + mkdir -p -m 755 $(confdir)/ipsec.d/cacerts + mkdir -p -m 755 $(confdir)/ipsec.d/ocspcerts + mkdir -p -m 755 $(confdir)/ipsec.d/certs + mkdir -p -m 755 $(confdir)/ipsec.d/acerts + mkdir -p -m 755 $(confdir)/ipsec.d/aacerts + mkdir -p -m 755 $(confdir)/ipsec.d/crls + mkdir -p -m 755 $(confdir)/ipsec.d/reqs + mkdir -p -m 700 $(confdir)/ipsec.d/private + diff --git a/src/pluto/Makefile.in b/src/pluto/Makefile.in new file mode 100644 index 000000000..1f996a065 --- /dev/null +++ b/src/pluto/Makefile.in @@ -0,0 +1,878 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program 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. + +@SET_MAKE@ + +# Makefile.am was ported from the old Makefile the most +# painless way. Only the most important options are included, +# further work may be necessary here... + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +ipsec_PROGRAMS = pluto$(EXEEXT) _pluto_adns$(EXEEXT) + +# This compile option activates the sending of a strongSwan VID +@USE_VENDORID_TRUE@am__append_1 = -DVENDORID + +# This compile option activates the support of the Cisco VPN client +@USE_CISCO_QUIRKS_TRUE@am__append_2 = -DCISCO_QUIRKS + +# This compile option activates NAT traversal with IPSec transport mode +@USE_NAT_TRANSPORT_TRUE@am__append_3 = -DI_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT + +# This compile option activates dynamic URL fetching using libcurl +@USE_LIBCURL_TRUE@am__append_4 = -lcurl + +# This compile option activates dynamic LDAP CRL fetching +@USE_LIBLDAP_TRUE@am__append_5 = -lldap -llber +subdir = src/pluto +DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in TODO +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man5dir)" \ + "$(DESTDIR)$(man8dir)" +ipsecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(ipsec_PROGRAMS) +am__pluto_adns_OBJECTS = adns.$(OBJEXT) +_pluto_adns_OBJECTS = $(am__pluto_adns_OBJECTS) +_pluto_adns_DEPENDENCIES = \ + $(top_srcdir)/src/libfreeswan/libfreeswan.a +am_pluto_OBJECTS = ac.$(OBJEXT) alg_info.$(OBJEXT) asn1.$(OBJEXT) \ + ca.$(OBJEXT) certs.$(OBJEXT) connections.$(OBJEXT) \ + constants.$(OBJEXT) cookie.$(OBJEXT) crl.$(OBJEXT) \ + crypto.$(OBJEXT) db_ops.$(OBJEXT) defs.$(OBJEXT) \ + demux.$(OBJEXT) dnskey.$(OBJEXT) dsa.$(OBJEXT) \ + elgamal.$(OBJEXT) fetch.$(OBJEXT) foodgroups.$(OBJEXT) \ + gcryptfix.$(OBJEXT) id.$(OBJEXT) ike_alg.$(OBJEXT) \ + ipsec_doi.$(OBJEXT) kernel.$(OBJEXT) kernel_alg.$(OBJEXT) \ + kernel_netlink.$(OBJEXT) kernel_noklips.$(OBJEXT) \ + kernel_pfkey.$(OBJEXT) keys.$(OBJEXT) lex.$(OBJEXT) \ + log.$(OBJEXT) md2.$(OBJEXT) md5.$(OBJEXT) modecfg.$(OBJEXT) \ + mp_defs.$(OBJEXT) nat_traversal.$(OBJEXT) ocsp.$(OBJEXT) \ + oid.$(OBJEXT) packet.$(OBJEXT) pem.$(OBJEXT) pgp.$(OBJEXT) \ + pkcs1.$(OBJEXT) pkcs7.$(OBJEXT) plutomain.$(OBJEXT) \ + primegen.$(OBJEXT) smallprime.$(OBJEXT) rcv_whack.$(OBJEXT) \ + rnd.$(OBJEXT) server.$(OBJEXT) sha1.$(OBJEXT) \ + smartcard.$(OBJEXT) spdb.$(OBJEXT) state.$(OBJEXT) \ + timer.$(OBJEXT) vendor.$(OBJEXT) virtual.$(OBJEXT) \ + xauth.$(OBJEXT) x509.$(OBJEXT) ike_alg_aes.$(OBJEXT) \ + ike_alg_blowfish.$(OBJEXT) ike_alg_twofish.$(OBJEXT) \ + ike_alg_serpent.$(OBJEXT) ike_alg_sha2.$(OBJEXT) \ + ike_alginit.$(OBJEXT) +pluto_OBJECTS = $(am_pluto_OBJECTS) +am__DEPENDENCIES_1 = +pluto_DEPENDENCIES = $(top_srcdir)/src/libfreeswan/libfreeswan.a \ + $(top_srcdir)/src/libcrypto/libcrypto.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(_pluto_adns_SOURCES) $(pluto_SOURCES) +DIST_SOURCES = $(_pluto_adns_SOURCES) $(pluto_SOURCES) +man5dir = $(mandir)/man5 +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(dist_man_MANS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EAP_SIM_FALSE = @BUILD_EAP_SIM_FALSE@ +BUILD_EAP_SIM_TRUE = @BUILD_EAP_SIM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_CISCO_QUIRKS_FALSE = @USE_CISCO_QUIRKS_FALSE@ +USE_CISCO_QUIRKS_TRUE = @USE_CISCO_QUIRKS_TRUE@ +USE_LEAK_DETECTIVE_FALSE = @USE_LEAK_DETECTIVE_FALSE@ +USE_LEAK_DETECTIVE_TRUE = @USE_LEAK_DETECTIVE_TRUE@ +USE_LIBCURL_FALSE = @USE_LIBCURL_FALSE@ +USE_LIBCURL_TRUE = @USE_LIBCURL_TRUE@ +USE_LIBLDAP_FALSE = @USE_LIBLDAP_FALSE@ +USE_LIBLDAP_TRUE = @USE_LIBLDAP_TRUE@ +USE_NAT_TRANSPORT_FALSE = @USE_NAT_TRANSPORT_FALSE@ +USE_NAT_TRANSPORT_TRUE = @USE_NAT_TRANSPORT_TRUE@ +USE_SMARTCARD_FALSE = @USE_SMARTCARD_FALSE@ +USE_SMARTCARD_TRUE = @USE_SMARTCARD_TRUE@ +USE_VENDORID_FALSE = @USE_VENDORID_FALSE@ +USE_VENDORID_TRUE = @USE_VENDORID_TRUE@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +confdir = @confdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eapdir = @eapdir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +piddir = @piddir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +pluto_SOURCES = \ +ac.c ac.h \ +alg_info.c alg_info.h \ +asn1.c asn1.h \ +ca.c ca.h \ +certs.c certs.h \ +connections.c connections.h \ +constants.c constants.h \ +cookie.c cookie.h \ +crl.c crl.h \ +crypto.c crypto.h \ +db_ops.c db_ops.h \ +defs.c defs.h \ +demux.c demux.h \ +dnskey.c dnskey.h \ +dsa.c dsa.h \ +elgamal.c elgamal.h \ +fetch.c fetch.h \ +foodgroups.c foodgroups.h \ +gcryptfix.c gcryptfix.h \ +id.c id.h \ +ike_alg.c ike_alg.h \ +ipsec_doi.c ipsec_doi.h \ +kameipsec.h \ +kernel.c kernel.h \ +kernel_alg.c kernel_alg.h \ +kernel_netlink.c kernel_netlink.h \ +kernel_noklips.c kernel_noklips.h \ +kernel_pfkey.c kernel_pfkey.h \ +keys.c keys.h \ +lex.c lex.h \ +log.c log.h \ +md2.c md2.h \ +md5.c md5.h \ +modecfg.c modecfg.h \ +mp_defs.c mp_defs.h \ +nat_traversal.c nat_traversal.h \ +ocsp.c ocsp.h \ +oid.c oid.h \ +packet.c packet.h \ +pem.c pem.h \ +pgp.c pgp.h \ +pkcs1.c pkcs1.h \ +pkcs7.c pkcs7.h \ +plutomain.c \ +primegen.c smallprime.c \ +rcv_whack.c rcv_whack.h \ +rnd.c rnd.h \ +server.c server.h \ +sha1.c sha1.h \ +smartcard.c smartcard.h \ +spdb.c spdb.h \ +state.c state.h \ +timer.c timer.h \ +vendor.c vendor.h \ +virtual.c virtual.h \ +xauth.c xauth.h \ +x509.c x509.h \ +alg/ike_alg_aes.c alg/ike_alg_blowfish.c alg/ike_alg_twofish.c \ +alg/ike_alg_serpent.c alg/ike_alg_sha2.c alg/ike_alginit.c \ +linux26/netlink.h linux26/rtnetlink.h linux26/xfrm.h \ +rsaref/pkcs11t.h rsaref/pkcs11.h rsaref/unix.h rsaref/pkcs11f.h + +_pluto_adns_SOURCES = adns.c adns.h +INCLUDES = \ +-I$(top_srcdir)/src/libfreeswan \ +-I$(top_srcdir)/src/libcrypto \ +-I$(top_srcdir)/src/whack + +AM_CFLAGS = -DIPSEC_DIR=\"${ipsecdir}\" -DIPSEC_CONFDIR=\"${confdir}\" \ + -DIPSEC_PIDDIR=\"${piddir}\" \ + -DSHARED_SECRETS_FILE=\"${confdir}/ipsec.secrets\" \ + -DKERNEL26_SUPPORT -DKERNEL26_HAS_KAME_DUPLICATES -DPLUTO \ + -DKLIPS -DDEBUG -DTHREADS $(am__append_1) $(am__append_2) \ + $(am__append_3) +pluto_LDADD = $(top_srcdir)/src/libfreeswan/libfreeswan.a \ + $(top_srcdir)/src/libcrypto/libcrypto.a -lgmp -lresolv \ + -lpthread -ldl $(am__append_4) $(am__append_5) +_pluto_adns_LDADD = \ +$(top_srcdir)/src/libfreeswan/libfreeswan.a \ +-lresolv -ldl + +dist_man_MANS = pluto.8 ipsec.secrets.5 +EXTRA_DIST = oid.pl oid.txt +BUILT_SOURCES = oid.c oid.h +MAINTAINERCLEANFILES = oid.c oid.h +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/pluto/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/pluto/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-ipsecPROGRAMS: $(ipsec_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(ipsecdir)" || $(mkdir_p) "$(DESTDIR)$(ipsecdir)" + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(ipsecdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(ipsecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(ipsecdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-ipsecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(ipsecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(ipsecdir)/$$f"; \ + done + +clean-ipsecPROGRAMS: + @list='$(ipsec_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +_pluto_adns$(EXEEXT): $(_pluto_adns_OBJECTS) $(_pluto_adns_DEPENDENCIES) + @rm -f _pluto_adns$(EXEEXT) + $(LINK) $(_pluto_adns_LDFLAGS) $(_pluto_adns_OBJECTS) $(_pluto_adns_LDADD) $(LIBS) +pluto$(EXEEXT): $(pluto_OBJECTS) $(pluto_DEPENDENCIES) + @rm -f pluto$(EXEEXT) + $(LINK) $(pluto_LDFLAGS) $(pluto_OBJECTS) $(pluto_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ac.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alg_info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/certs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connections.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/constants.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookie.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db_ops.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demux.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnskey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elgamal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fetch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/foodgroups.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gcryptfix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alg_aes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alg_blowfish.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alg_serpent.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alg_sha2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alg_twofish.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ike_alginit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipsec_doi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_alg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_netlink.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_noklips.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kernel_pfkey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keys.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modecfg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mp_defs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nat_traversal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pem.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plutomain.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/primegen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rcv_whack.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rnd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smallprime.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartcard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spdb.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/state.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vendor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xauth.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +ike_alg_aes.o: alg/ike_alg_aes.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_aes.o -MD -MP -MF "$(DEPDIR)/ike_alg_aes.Tpo" -c -o ike_alg_aes.o `test -f 'alg/ike_alg_aes.c' || echo '$(srcdir)/'`alg/ike_alg_aes.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_aes.Tpo" "$(DEPDIR)/ike_alg_aes.Po"; else rm -f "$(DEPDIR)/ike_alg_aes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_aes.c' object='ike_alg_aes.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_aes.o `test -f 'alg/ike_alg_aes.c' || echo '$(srcdir)/'`alg/ike_alg_aes.c + +ike_alg_aes.obj: alg/ike_alg_aes.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_aes.obj -MD -MP -MF "$(DEPDIR)/ike_alg_aes.Tpo" -c -o ike_alg_aes.obj `if test -f 'alg/ike_alg_aes.c'; then $(CYGPATH_W) 'alg/ike_alg_aes.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_aes.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_aes.Tpo" "$(DEPDIR)/ike_alg_aes.Po"; else rm -f "$(DEPDIR)/ike_alg_aes.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_aes.c' object='ike_alg_aes.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_aes.obj `if test -f 'alg/ike_alg_aes.c'; then $(CYGPATH_W) 'alg/ike_alg_aes.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_aes.c'; fi` + +ike_alg_blowfish.o: alg/ike_alg_blowfish.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_blowfish.o -MD -MP -MF "$(DEPDIR)/ike_alg_blowfish.Tpo" -c -o ike_alg_blowfish.o `test -f 'alg/ike_alg_blowfish.c' || echo '$(srcdir)/'`alg/ike_alg_blowfish.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_blowfish.Tpo" "$(DEPDIR)/ike_alg_blowfish.Po"; else rm -f "$(DEPDIR)/ike_alg_blowfish.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_blowfish.c' object='ike_alg_blowfish.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_blowfish.o `test -f 'alg/ike_alg_blowfish.c' || echo '$(srcdir)/'`alg/ike_alg_blowfish.c + +ike_alg_blowfish.obj: alg/ike_alg_blowfish.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_blowfish.obj -MD -MP -MF "$(DEPDIR)/ike_alg_blowfish.Tpo" -c -o ike_alg_blowfish.obj `if test -f 'alg/ike_alg_blowfish.c'; then $(CYGPATH_W) 'alg/ike_alg_blowfish.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_blowfish.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_blowfish.Tpo" "$(DEPDIR)/ike_alg_blowfish.Po"; else rm -f "$(DEPDIR)/ike_alg_blowfish.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_blowfish.c' object='ike_alg_blowfish.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_blowfish.obj `if test -f 'alg/ike_alg_blowfish.c'; then $(CYGPATH_W) 'alg/ike_alg_blowfish.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_blowfish.c'; fi` + +ike_alg_twofish.o: alg/ike_alg_twofish.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_twofish.o -MD -MP -MF "$(DEPDIR)/ike_alg_twofish.Tpo" -c -o ike_alg_twofish.o `test -f 'alg/ike_alg_twofish.c' || echo '$(srcdir)/'`alg/ike_alg_twofish.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_twofish.Tpo" "$(DEPDIR)/ike_alg_twofish.Po"; else rm -f "$(DEPDIR)/ike_alg_twofish.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_twofish.c' object='ike_alg_twofish.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_twofish.o `test -f 'alg/ike_alg_twofish.c' || echo '$(srcdir)/'`alg/ike_alg_twofish.c + +ike_alg_twofish.obj: alg/ike_alg_twofish.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_twofish.obj -MD -MP -MF "$(DEPDIR)/ike_alg_twofish.Tpo" -c -o ike_alg_twofish.obj `if test -f 'alg/ike_alg_twofish.c'; then $(CYGPATH_W) 'alg/ike_alg_twofish.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_twofish.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_twofish.Tpo" "$(DEPDIR)/ike_alg_twofish.Po"; else rm -f "$(DEPDIR)/ike_alg_twofish.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_twofish.c' object='ike_alg_twofish.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_twofish.obj `if test -f 'alg/ike_alg_twofish.c'; then $(CYGPATH_W) 'alg/ike_alg_twofish.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_twofish.c'; fi` + +ike_alg_serpent.o: alg/ike_alg_serpent.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_serpent.o -MD -MP -MF "$(DEPDIR)/ike_alg_serpent.Tpo" -c -o ike_alg_serpent.o `test -f 'alg/ike_alg_serpent.c' || echo '$(srcdir)/'`alg/ike_alg_serpent.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_serpent.Tpo" "$(DEPDIR)/ike_alg_serpent.Po"; else rm -f "$(DEPDIR)/ike_alg_serpent.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_serpent.c' object='ike_alg_serpent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_serpent.o `test -f 'alg/ike_alg_serpent.c' || echo '$(srcdir)/'`alg/ike_alg_serpent.c + +ike_alg_serpent.obj: alg/ike_alg_serpent.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_serpent.obj -MD -MP -MF "$(DEPDIR)/ike_alg_serpent.Tpo" -c -o ike_alg_serpent.obj `if test -f 'alg/ike_alg_serpent.c'; then $(CYGPATH_W) 'alg/ike_alg_serpent.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_serpent.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_serpent.Tpo" "$(DEPDIR)/ike_alg_serpent.Po"; else rm -f "$(DEPDIR)/ike_alg_serpent.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_serpent.c' object='ike_alg_serpent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_serpent.obj `if test -f 'alg/ike_alg_serpent.c'; then $(CYGPATH_W) 'alg/ike_alg_serpent.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_serpent.c'; fi` + +ike_alg_sha2.o: alg/ike_alg_sha2.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_sha2.o -MD -MP -MF "$(DEPDIR)/ike_alg_sha2.Tpo" -c -o ike_alg_sha2.o `test -f 'alg/ike_alg_sha2.c' || echo '$(srcdir)/'`alg/ike_alg_sha2.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_sha2.Tpo" "$(DEPDIR)/ike_alg_sha2.Po"; else rm -f "$(DEPDIR)/ike_alg_sha2.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_sha2.c' object='ike_alg_sha2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_sha2.o `test -f 'alg/ike_alg_sha2.c' || echo '$(srcdir)/'`alg/ike_alg_sha2.c + +ike_alg_sha2.obj: alg/ike_alg_sha2.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alg_sha2.obj -MD -MP -MF "$(DEPDIR)/ike_alg_sha2.Tpo" -c -o ike_alg_sha2.obj `if test -f 'alg/ike_alg_sha2.c'; then $(CYGPATH_W) 'alg/ike_alg_sha2.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_sha2.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alg_sha2.Tpo" "$(DEPDIR)/ike_alg_sha2.Po"; else rm -f "$(DEPDIR)/ike_alg_sha2.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alg_sha2.c' object='ike_alg_sha2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alg_sha2.obj `if test -f 'alg/ike_alg_sha2.c'; then $(CYGPATH_W) 'alg/ike_alg_sha2.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alg_sha2.c'; fi` + +ike_alginit.o: alg/ike_alginit.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alginit.o -MD -MP -MF "$(DEPDIR)/ike_alginit.Tpo" -c -o ike_alginit.o `test -f 'alg/ike_alginit.c' || echo '$(srcdir)/'`alg/ike_alginit.c; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alginit.Tpo" "$(DEPDIR)/ike_alginit.Po"; else rm -f "$(DEPDIR)/ike_alginit.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alginit.c' object='ike_alginit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alginit.o `test -f 'alg/ike_alginit.c' || echo '$(srcdir)/'`alg/ike_alginit.c + +ike_alginit.obj: alg/ike_alginit.c +@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ike_alginit.obj -MD -MP -MF "$(DEPDIR)/ike_alginit.Tpo" -c -o ike_alginit.obj `if test -f 'alg/ike_alginit.c'; then $(CYGPATH_W) 'alg/ike_alginit.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alginit.c'; fi`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ike_alginit.Tpo" "$(DEPDIR)/ike_alginit.Po"; else rm -f "$(DEPDIR)/ike_alginit.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='alg/ike_alginit.c' object='ike_alginit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ike_alginit.obj `if test -f 'alg/ike_alginit.c'; then $(CYGPATH_W) 'alg/ike_alginit.c'; else $(CYGPATH_W) '$(srcdir)/alg/ike_alginit.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man5: $(man5_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man5dir)" || $(mkdir_p) "$(DESTDIR)$(man5dir)" + @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.5*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 5*) ;; \ + *) ext='5' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \ + done +uninstall-man5: + @$(NORMAL_UNINSTALL) + @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.5*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 5*) ;; \ + *) ext='5' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man5dir)/$$inst"; \ + done +install-man8: $(man8_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ + done +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 8*) ;; \ + *) ext='8' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(ipsecdir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-ipsecPROGRAMS clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-ipsecPROGRAMS install-man + +install-exec-am: install-exec-local + +install-info: install-info-am + +install-man: install-man5 install-man8 + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-ipsecPROGRAMS uninstall-man + +uninstall-man: uninstall-man5 uninstall-man8 + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-ipsecPROGRAMS clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-exec-local install-info \ + install-info-am install-ipsecPROGRAMS install-man install-man5 \ + install-man8 install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-info-am uninstall-ipsecPROGRAMS \ + uninstall-man uninstall-man5 uninstall-man8 + + +oid.c: oid.txt oid.pl + $(PERL) oid.pl + +oid.h: oid.txt oid.pl + $(PERL) oid.pl + +install-exec-local : + mkdir -p -m 755 $(confdir)/ipsec.d + mkdir -p -m 755 $(confdir)/ipsec.d/cacerts + mkdir -p -m 755 $(confdir)/ipsec.d/ocspcerts + mkdir -p -m 755 $(confdir)/ipsec.d/certs + mkdir -p -m 755 $(confdir)/ipsec.d/acerts + mkdir -p -m 755 $(confdir)/ipsec.d/aacerts + mkdir -p -m 755 $(confdir)/ipsec.d/crls + mkdir -p -m 755 $(confdir)/ipsec.d/reqs + mkdir -p -m 700 $(confdir)/ipsec.d/private +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/pluto/TODO b/src/pluto/TODO new file mode 100644 index 000000000..7db4a9ebc --- /dev/null +++ b/src/pluto/TODO @@ -0,0 +1,129 @@ +Pluto TODO list +=============== +RCSID $Id: TODO,v 1.1 2004/03/15 20:35:28 as Exp $ + +- should all log entries that are for errors say ERROR? + +- Add a "plug-in" facility so that others can add features without + changing the mainline code. This is how X509/LDAP/biometric stuff + might be added. + +- (internal change only) routines for outputting payloads should plug + "np" into the previous payload so that a payload generating routine + need not know what the next payload will be. This may be more bother + than it is worth. + +- notifications, in and out + + delete + + first contact + + last contact? (not part of drafts, but would be nice) + +- Make DNS usage for asynchronous (non-blocking) + + looking up KEY and TXT records during negotiation + + perhaps not for whack command arguments and ipsec.secrets since the + library code uses gethostbyname + +- check that ipsec auto and whack to agree on what is worth reporting + +- Should Pluto (rather than ipsec manual) install %passthrough conns? + That way Pluto would know of them. + +- For responding to Road Warriors, how can we decide if the RW has + gone away? The rekeying event is perhaps too imprecise. Even if + rekeying event is good enough, how do we know if the route should be + torn down? Perhaps limiting a Phase 1 ID to one IP address would + help (limiting a client subnet to one peer already helps). Perhaps + (in some rate-limited way) we can take an ICMP host unreachable + as a hint to do some authenticated and reliable probe. + +- it is annoying that Pluto and auto have different models for public keys. + + auto specifies one per connection + + Pluto allows one to be specified per id + Two connections with the same id are going to use the same key: + the one of the last conn to be added! + + I think auto ought to be fixed. It is hard for Pluto to warn when + there is a conflict since the deletion of a connection doesn't + prompt auto to tell pluto to delete the public key. + +- different connections with the same host IP addresses are randomly + interchangeable until the ID payload is received. At least for the + Responder case (and eventually for the opportunistic Initiator). + Worse, all Road Warriors must be considered to have the + indistinguishable IP addresses. This affects ISAKMP SA negotiation. + Currently, there is little flexibility in this negotiation, so the + problem is limited to the specification of acceptable authentication + method(s). Correct, but more work than seems worthwhile, would be + to select the conn based on what is proposed. + + Warning about such confusion at connection definition time isn't great + because there is no confusion when explicitly initiated (a particular + conn is specified). Warning for a Road Warrior conn is possible + since it cannot be initiated (and has been implemented). + +- characterize and ameliorate DOS attacks. Lots of rate limiting. + +- look at John Denker's wish list: http://www.quintillion.com/moat/wish.list + +- use of random numbers needs to be audited. + +- unknown (not just unimplemented) transforms cause a negotiation to + fail. Only the transform should be rejected. + +- we need better policy control. Our present flags need to be + modulated (forbid, allow, offer, require) + +- HS will specify how --copyright and --version should behave + +- HS will initiate project-wide terminology replacing ISAKMP SA, IPSEC + SA, Protection Suite, Phase 1, Main Mode, Phase 2, Quick Mode, ... + Simplicity and clarity will be a goal. + +- interface discovery ought to match what is specified in ipsec.conf. + This probably means grokking /proc/net/ipsec_tncfg. Documented in + ipsec_tncfg(5). This won't do for Hugh's debugging setup. + + +Protocol Issues +=============== + +Notification and delete payloads seem to be "escape hatches" for the +protocols. As such, anything implemented using them seems to be +kludged without being well designed or well situated or well +constrained in the protocols. Often the precise meaning (if any) or +usage is under specified. An implementation is allowed to ignore +them, so they cannot really matter (but they too often do). Their +specification ought to be scrutinized by a protocol guru. + +Any extra payload in last main mode message is not protected (not +authenticated by hash). + +Should notification payloads be interpreted before or after the normal +payloads (i.e. understood in the context of, executed in the context of). + +What is the precise result of an INITIAL_CONNECTION? What is a +"system" (eg. does Phase 1 Identity count)? What is "earlier" or +"before" (simultaneous negotiation is possible, with time being only a +partial order)? Could it be used for FINAL_CONTACT (needed too)? + +Blasting out a pile of UDP messages, especially to a particular +destination, is likely to provoke message loss. The exchanges are +just that, so they individually are self-throttling. But what about +multiple exchanges simultaneously? What about notifications (example: +when shutting down, a flurry of delete notifications are likely). +Should the RFCs be designed to protect against this problem? + +draft-jenkins-ipsec-rekeying-03.txt rekeying is way too complicated. +Our solution looks sound and simple (we have the Responder install the +incoming IPSEC SA before sending its first reply). In "2.2.1.4 +Responder Pre-Set-up Security Hole", the draft claims that setting up +the IPSEC SA early leaves the Responder open to replay attacks. I +think that this is wrong: the Message Id, since it must not be reused, +serves to prove that this isn't a replay. + +The details for notification messages suggested by +draft-ietf-ipsec-notifymsg-02.txt are over-complicated, just to make +them machine-comprehensible. I think this is over-engineering, +justified only if another level of negotiation is contemplated (ugh!). +Plain text is probably sufficient for informing humans (I admit that +there is a problem with I18N). diff --git a/src/pluto/ac.c b/src/pluto/ac.c new file mode 100644 index 000000000..bcf5f80d1 --- /dev/null +++ b/src/pluto/ac.c @@ -0,0 +1,1018 @@ +/* Support of X.509 attribute certificates + * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler + * Copyright (C) 2003 Martin Berner, Lukas Suter + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ac.c,v 1.12 2005/12/06 22:49:32 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "asn1.h" +#include "oid.h" +#include "ac.h" +#include "x509.h" +#include "crl.h" +#include "ca.h" +#include "certs.h" +#include "log.h" +#include "whack.h" +#include "fetch.h" + +/* chained list of X.509 attribute certificates */ + +static x509acert_t *x509acerts = NULL; + +/* chained list of ietfAttributes */ + +static ietfAttrList_t *ietfAttributes = NULL; + +/* ASN.1 definition of ietfAttrSyntax */ + +static const asn1Object_t ietfAttrSyntaxObjects[] = +{ + { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_BODY }, /* 1 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ + { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */ + { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT | + ASN1_BODY }, /* 4 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ + { 2, "oid", ASN1_OID, ASN1_OPT | + ASN1_BODY }, /* 6 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ + { 2, "string", ASN1_UTF8STRING, ASN1_OPT | + ASN1_BODY }, /* 8 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 1, "end loop", ASN1_EOC, ASN1_END } /* 10 */ +}; + +#define IETF_ATTR_OCTETS 4 +#define IETF_ATTR_OID 6 +#define IETF_ATTR_STRING 8 +#define IETF_ATTR_ROOF 11 + +/* ASN.1 definition of roleSyntax */ + +static const asn1Object_t roleSyntaxObjects[] = +{ + { 0, "roleSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "roleAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_OBJ }, /* 1 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ + { 1, "roleName", ASN1_CONTEXT_C_1, ASN1_OBJ } /* 3 */ +}; + +#define ROLE_ROOF 4 + +/* ASN.1 definition of an X509 attribute certificate */ + +static const asn1Object_t acObjects[] = +{ + { 0, "AttributeCertificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "AttributeCertificateInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "version", ASN1_INTEGER, ASN1_DEF | + ASN1_BODY }, /* 2 */ + { 2, "holder", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ + { 3, "baseCertificateID", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 4 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 6 */ + { 4, "issuerUID", ASN1_BIT_STRING, ASN1_OPT | + ASN1_BODY }, /* 7 */ + { 4, "end opt", ASN1_EOC, ASN1_END }, /* 8 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 9 */ + { 3, "entityName", ASN1_CONTEXT_C_1, ASN1_OPT | + ASN1_OBJ }, /* 10 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 11 */ + { 3, "objectDigestInfo", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 12 */ + { 4, "digestedObjectType", ASN1_ENUMERATED, ASN1_BODY }, /* 13*/ + { 4, "otherObjectTypeID", ASN1_OID, ASN1_OPT | + ASN1_BODY }, /* 14 */ + { 4, "end opt", ASN1_EOC, ASN1_END }, /* 15*/ + { 4, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 16 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 17 */ + { 2, "v2Form", ASN1_CONTEXT_C_0, ASN1_NONE }, /* 18 */ + { 3, "issuerName", ASN1_SEQUENCE, ASN1_OPT | + ASN1_OBJ }, /* 19 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 20 */ + { 3, "baseCertificateID", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 21 */ + { 4, "issuerSerial", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ + { 5, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 23 */ + { 5, "serial", ASN1_INTEGER, ASN1_BODY }, /* 24 */ + { 5, "issuerUID", ASN1_BIT_STRING, ASN1_OPT | + ASN1_BODY }, /* 25 */ + { 5, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 27 */ + { 3, "objectDigestInfo", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 28 */ + { 4, "digestInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 29 */ + { 5, "digestedObjectType", ASN1_ENUMERATED, ASN1_BODY }, /* 30 */ + { 5, "otherObjectTypeID", ASN1_OID, ASN1_OPT | + ASN1_BODY }, /* 31 */ + { 5, "end opt", ASN1_EOC, ASN1_END }, /* 32 */ + { 5, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 33 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 34 */ + { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 35 */ + { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 36 */ + { 2, "attrCertValidityPeriod", ASN1_SEQUENCE, ASN1_NONE }, /* 37 */ + { 3, "notBeforeTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 38 */ + { 3, "notAfterTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 39 */ + { 2, "attributes", ASN1_SEQUENCE, ASN1_LOOP }, /* 40 */ + { 3, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 41 */ + { 4, "type", ASN1_OID, ASN1_BODY }, /* 42 */ + { 4, "values", ASN1_SET, ASN1_LOOP }, /* 43 */ + { 5, "value", ASN1_EOC, ASN1_RAW }, /* 44 */ + { 4, "end loop", ASN1_EOC, ASN1_END }, /* 45 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 46 */ + { 2, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 47 */ + { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 48 */ + { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 49 */ + { 4, "critical", ASN1_BOOLEAN, ASN1_DEF | + ASN1_BODY }, /* 50 */ + { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 51 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 52 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 53 */ + { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 54 */ +}; + +#define AC_OBJ_CERTIFICATE 0 +#define AC_OBJ_CERTIFICATE_INFO 1 +#define AC_OBJ_VERSION 2 +#define AC_OBJ_HOLDER_ISSUER 5 +#define AC_OBJ_HOLDER_SERIAL 6 +#define AC_OBJ_ENTITY_NAME 10 +#define AC_OBJ_ISSUER_NAME 19 +#define AC_OBJ_ISSUER 23 +#define AC_OBJ_SIG_ALG 35 +#define AC_OBJ_SERIAL_NUMBER 36 +#define AC_OBJ_NOT_BEFORE 38 +#define AC_OBJ_NOT_AFTER 39 +#define AC_OBJ_ATTRIBUTE_TYPE 42 +#define AC_OBJ_ATTRIBUTE_VALUE 44 +#define AC_OBJ_EXTN_ID 49 +#define AC_OBJ_CRITICAL 50 +#define AC_OBJ_EXTN_VALUE 51 +#define AC_OBJ_ALGORITHM 53 +#define AC_OBJ_SIGNATURE 54 +#define AC_OBJ_ROOF 55 + +const x509acert_t empty_ac = { + NULL , /* *next */ + 0 , /* installed */ + { NULL, 0 }, /* certificate */ + { NULL, 0 }, /* certificateInfo */ + 1 , /* version */ + /* holder */ + /* baseCertificateID */ + { NULL, 0 }, /* holderIssuer */ + { NULL, 0 }, /* holderSerial */ + /* entityName */ + { NULL, 0 }, /* generalNames */ + /* v2Form */ + { NULL, 0 }, /* issuerName */ + /* signature */ + OID_UNKNOWN, /* sigAlg */ + { NULL, 0 }, /* serialNumber */ + /* attrCertValidityPeriod */ + 0 , /* notBefore */ + 0 , /* notAfter */ + /* attributes */ + NULL , /* charging */ + NULL , /* groups */ + /* extensions */ + { NULL, 0 }, /* authKeyID */ + { NULL, 0 }, /* authKeySerialNumber */ + FALSE , /* noRevAvail */ + /* signatureAlgorithm */ + OID_UNKNOWN, /* algorithm */ + { NULL, 0 }, /* signature */ +}; + + +/* compare two ietfAttributes, returns zero if a equals b + * negative/positive if a is earlier/later in the alphabet than b + */ +static int +cmp_ietfAttr(ietfAttr_t *a,ietfAttr_t *b) +{ + int cmp_len, len, cmp_value; + + /* cannot compare OID with STRING or OCTETS attributes */ + if (a->kind == IETF_ATTRIBUTE_OID && b->kind != IETF_ATTRIBUTE_OID) + return 1; + + cmp_len = a->value.len - b->value.len; + len = (cmp_len < 0)? a->value.len : b->value.len; + cmp_value = memcmp(a->value.ptr, b->value.ptr, len); + + return (cmp_value == 0)? cmp_len : cmp_value; +} + +/* + * add an ietfAttribute to the chained list + */ +static ietfAttr_t* +add_ietfAttr(ietfAttr_t *attr) +{ + ietfAttrList_t **listp = &ietfAttributes; + ietfAttrList_t *list = *listp; + int cmp = -1; + + while (list != NULL) + { + cmp = cmp_ietfAttr(attr, list->attr); + if (cmp <= 0) + break; + listp = &list->next; + list = *listp; + } + + if (cmp == 0) + { + /* attribute already exists, increase count */ + pfree(attr); + list->attr->count++; + return list->attr; + } + else + { + ietfAttrList_t *el = alloc_thing(ietfAttrList_t, "ietfAttrList"); + + /* new attribute, unshare value */ + attr->value.ptr = clone_bytes(attr->value.ptr, attr->value.len + , "attr value"); + attr->count = 1; + time(&attr->installed); + + el->attr = attr; + el->next = list; + *listp = el; + + return attr; + } +} + +/* + * decodes a comma separated list of group attributes + */ +void +decode_groups(char *groups, ietfAttrList_t **listp) +{ + if (groups == NULL) + return; + + while (strlen(groups) > 0) + { + char *end; + char *next = strchr(groups, ','); + + if (next == NULL) + end = next = groups + strlen(groups); + else + end = next++; + + /* eat preceeding whitespace */ + while (groups < end && *groups == ' ') + groups++; + + /* eat trailing whitespace */ + while (end > groups && *(end-1) == ' ') + end--; + + if (groups < end) + { + ietfAttr_t *attr = alloc_thing(ietfAttr_t, "ietfAttr"); + ietfAttrList_t *el = alloc_thing(ietfAttrList_t, "ietfAttrList"); + + attr->kind = IETF_ATTRIBUTE_STRING; + attr->value.ptr = groups; + attr->value.len = end - groups; + attr->count = 0; + + el->attr = add_ietfAttr(attr); + el->next = *listp; + *listp = el; + } + + groups = next; + } +} + +static bool +same_attribute(const ietfAttr_t *a, const ietfAttr_t *b) +{ + return (a->kind == b->kind && a->value.len == b->value.len + && memcmp(a->value.ptr, b->value.ptr, b->value.len) == 0); +} + +bool +group_membership(const ietfAttrList_t *peer_list + , const char *conn + , const ietfAttrList_t *conn_list) +{ + if (conn_list == NULL) + return TRUE; + + while (peer_list != NULL) + { + const ietfAttr_t *peer_attr = peer_list->attr; + const ietfAttrList_t *list = conn_list; + + while (list != NULL) + { + ietfAttr_t *conn_attr = list->attr; + + if (same_attribute(conn_attr, peer_attr)) + { + DBG(DBG_CONTROL, + DBG_log("%s: peer matches group '%.*s'" + , conn + , (int)peer_attr->value.len, peer_attr->value.ptr) + ) + return TRUE; + } + list = list->next; + } + peer_list = peer_list->next; + } + DBG(DBG_CONTROL, + DBG_log("%s: peer doesn't match any group", conn) + ) + return FALSE; +} + + +void +unshare_ietfAttrList(ietfAttrList_t **listp) +{ + ietfAttrList_t *list = *listp; + + while (list != NULL) + { + ietfAttrList_t *el = alloc_thing(ietfAttrList_t, "ietfAttrList"); + + el->attr = list->attr; + el->attr->count++; + el->next = NULL; + *listp = el; + listp = &el->next; + list = list->next; + } +} + +/* + * parses ietfAttrSyntax + */ +static ietfAttrList_t* +parse_ietfAttrSyntax(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + ietfAttrList_t *list = NULL; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < IETF_ATTR_ROOF) + { + if (!extract_object(ietfAttrSyntaxObjects, &objectID, &object, &level, &ctx)) + return NULL; + + switch (objectID) + { + case IETF_ATTR_OCTETS: + case IETF_ATTR_OID: + case IETF_ATTR_STRING: + { + ietfAttr_t *attr = alloc_thing(ietfAttr_t, "ietfAttr"); + ietfAttrList_t *el = alloc_thing(ietfAttrList_t, "ietfAttrList"); + + attr->kind = (objectID - IETF_ATTR_OCTETS) / 2; + attr->value = object; + attr->count = 0; + + el->attr = add_ietfAttr(attr); + el->next = list; + list = el; + } + break; + default: + break; + } + objectID++; + } + return list; +} +/* + * parses roleSyntax + */ +static void +parse_roleSyntax(chunk_t blob, int level0) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < ROLE_ROOF) + { + if (!extract_object(roleSyntaxObjects, &objectID, &object, &level, &ctx)) + return; + + switch (objectID) { + default: + break; + } + objectID++; + } +} + +/* + * Parses an X.509 attribute certificate + */ +bool +parse_ac(chunk_t blob, x509acert_t *ac) +{ + asn1_ctx_t ctx; + bool critical; + chunk_t object; + u_int level; + u_int type = OID_UNKNOWN; + u_int extn_oid = OID_UNKNOWN; + int objectID = 0; + + asn1_init(&ctx, blob, 0, FALSE, DBG_RAW); + + while (objectID < AC_OBJ_ROOF) { + + if (!extract_object(acObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + /* those objects which will parsed further need the next higher level */ + level++; + + switch (objectID) + { + case AC_OBJ_CERTIFICATE: + ac->certificate = object; + break; + case AC_OBJ_CERTIFICATE_INFO: + ac->certificateInfo = object; + break; + case AC_OBJ_VERSION: + ac->version = (object.len) ? (1 + (u_int)*object.ptr) : 1; + DBG(DBG_PARSING, + DBG_log(" v%d", ac->version); + ) + if (ac->version != 2) + { + plog("v%d attribute certificates are not supported" + , ac->version); + return FALSE; + } + break; + case AC_OBJ_HOLDER_ISSUER: + ac->holderIssuer = get_directoryName(object, level, FALSE); + break; + case AC_OBJ_HOLDER_SERIAL: + ac->holderSerial = object; + break; + case AC_OBJ_ENTITY_NAME: + ac->entityName = get_directoryName(object, level, TRUE); + break; + case AC_OBJ_ISSUER_NAME: + ac->issuerName = get_directoryName(object, level, FALSE); + break; + case AC_OBJ_SIG_ALG: + ac->sigAlg = parse_algorithmIdentifier(object, level, NULL); + break; + case AC_OBJ_SERIAL_NUMBER: + ac->serialNumber = object; + break; + case AC_OBJ_NOT_BEFORE: + ac->notBefore = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case AC_OBJ_NOT_AFTER: + ac->notAfter = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case AC_OBJ_ATTRIBUTE_TYPE: + type = known_oid(object); + break; + case AC_OBJ_ATTRIBUTE_VALUE: + { + switch (type) { + case OID_AUTHENTICATION_INFO: + DBG(DBG_PARSING, + DBG_log(" need to parse authenticationInfo") + ) + break; + case OID_ACCESS_IDENTITY: + DBG(DBG_PARSING, + DBG_log(" need to parse accessIdentity") + ) + break; + case OID_CHARGING_IDENTITY: + ac->charging = parse_ietfAttrSyntax(object, level); + break; + case OID_GROUP: + ac->groups = parse_ietfAttrSyntax(object, level); + break; + case OID_ROLE: + parse_roleSyntax(object, level); + break; + default: + break; + } + } + break; + case AC_OBJ_EXTN_ID: + extn_oid = known_oid(object); + break; + case AC_OBJ_CRITICAL: + critical = object.len && *object.ptr; + DBG(DBG_PARSING, + DBG_log(" %s",(critical)?"TRUE":"FALSE"); + ) + break; + case AC_OBJ_EXTN_VALUE: + { + switch (extn_oid) { + case OID_CRL_DISTRIBUTION_POINTS: + DBG(DBG_PARSING, + DBG_log(" need to parse crlDistributionPoints") + ) + break; + case OID_AUTHORITY_KEY_ID: + parse_authorityKeyIdentifier(object, level + , &ac->authKeyID, &ac->authKeySerialNumber); + break; + case OID_TARGET_INFORMATION: + DBG(DBG_PARSING, + DBG_log(" need to parse targetInformation") + ) + break; + case OID_NO_REV_AVAIL: + ac->noRevAvail = TRUE; + break; + default: + break; + } + } + break; + case AC_OBJ_ALGORITHM: + ac->algorithm = parse_algorithmIdentifier(object, level, NULL); + break; + case AC_OBJ_SIGNATURE: + ac->signature = object; + break; + + default: + break; + } + objectID++; + } + time(&ac->installed); + return TRUE; +} + +/* + * compare two X.509 attribute certificates by comparing their signatures + */ +static bool +same_x509acert(x509acert_t *a, x509acert_t *b) +{ + return a->signature.len == b->signature.len && + memcmp(a->signature.ptr, b->signature.ptr, b->signature.len) == 0; +} + +/* + * release an ietfAttribute, free it if count reaches zero + */ +static void +release_ietfAttr(ietfAttr_t* attr) +{ + if (--attr->count == 0) + { + ietfAttrList_t **plist = &ietfAttributes; + ietfAttrList_t *list = *plist; + + while (list->attr != attr) + { + plist = &list->next; + list = *plist; + } + *plist = list->next; + + pfree(attr->value.ptr); + pfree(attr); + pfree(list); + } +} + +/* + * free an ietfAttrList + */ +void +free_ietfAttrList(ietfAttrList_t* list) +{ + while (list != NULL) + { + ietfAttrList_t *el = list; + + release_ietfAttr(el->attr); + list = list->next; + pfree(el); + } +} + +/* + * free a X.509 attribute certificate + */ +void +free_acert(x509acert_t *ac) +{ + if (ac != NULL) + { + free_ietfAttrList(ac->charging); + free_ietfAttrList(ac->groups); + pfreeany(ac->certificate.ptr); + pfree(ac); + } +} + +/* + * free first X.509 attribute certificate in the chained list + */ +static void +free_first_acert(void) +{ + x509acert_t *first = x509acerts; + x509acerts = first->next; + free_acert(first); +} + +/* + * Free all attribute certificates in the chained list + */ +void +free_acerts(void) +{ + while (x509acerts != NULL) + free_first_acert(); +} + +/* + * get a X.509 attribute certificate for a given holder + */ +x509acert_t* +get_x509acert(chunk_t issuer, chunk_t serial) +{ + x509acert_t *ac = x509acerts; + x509acert_t *prev_ac = NULL; + + while (ac != NULL) + { + if (same_dn(issuer, ac->holderIssuer) + && same_serial(serial, ac->holderSerial)) + { + if (ac!= x509acerts) + { + /* bring the certificate up front */ + prev_ac->next = ac->next; + ac->next = x509acerts; + x509acerts = ac; + } + return ac; + } + prev_ac = ac; + ac = ac->next; + } + return NULL; +} + +/* + * add a X.509 attribute certificate to the chained list + */ +static void +add_acert(x509acert_t *ac) +{ + x509acert_t *old_ac = get_x509acert(ac->holderIssuer, ac->holderSerial); + + if (old_ac != NULL) + { + if (ac->notBefore >old_ac->notBefore) + { + /* delete the old attribute cert */ + free_first_acert(); + DBG(DBG_CONTROL, + DBG_log("attribute cert is newer - existing cert deleted") + ) + } + else + { + DBG(DBG_CONTROL, + DBG_log("attribute cert is not newer - existing cert kept"); + ) + free_acert(ac); + return; + } + } + plog("attribute cert added"); + + /* insert new attribute cert at the root of the chain */ + ac->next = x509acerts; + x509acerts = ac; +} + +/* verify the validity of an attribute certificate by + * checking the notBefore and notAfter dates + */ +static err_t +check_ac_validity(const x509acert_t *ac) +{ + time_t current_time; + + time(¤t_time); + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log(" not before : %s", timetoa(&ac->notBefore, TRUE)); + DBG_log(" current time: %s", timetoa(¤t_time, TRUE)); + DBG_log(" not after : %s", timetoa(&ac->notAfter, TRUE)); + ) + + if (current_time < ac->notBefore) + return "attribute certificate is not valid yet"; + if (current_time > ac->notAfter) + return "attribute certificate has expired"; + else + return NULL; +} + +/* + * verifies a X.509 attribute certificate + */ +bool +verify_x509acert(x509acert_t *ac, bool strict) +{ + u_char buf[BUF_LEN]; + x509cert_t *aacert; + err_t ugh = NULL; + time_t valid_until = ac->notAfter; + + DBG(DBG_CONTROL, + dntoa(buf, BUF_LEN, ac->entityName); + DBG_log("holder: '%s'",buf); + dntoa(buf, BUF_LEN, ac->issuerName); + DBG_log("issuer: '%s'",buf); + ) + + ugh = check_ac_validity(ac); + + if (ugh != NULL) + { + plog("%s", ugh); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("attribute certificate is valid") + ) + + lock_authcert_list("verify_x509acert"); + aacert = get_authcert(ac->issuerName, ac->authKeySerialNumber + , ac->authKeyID, AUTH_AA); + unlock_authcert_list("verify_x509acert"); + + if (aacert == NULL) + { + plog("issuer aacert not found"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("issuer aacert found") + ) + + if (!check_signature(ac->certificateInfo, ac->signature + , ac->algorithm, ac->algorithm, aacert)) + { + plog("attribute certificate signature is invalid"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("attribute certificate signature is valid"); + ) + + return verify_x509cert(aacert, strict, &valid_until); +} + +/* + * Loads X.509 attribute certificates + */ +void +load_acerts(void) +{ + u_char buf[BUF_LEN]; + + /* change directory to specified path */ + u_char *save_dir = getcwd(buf, BUF_LEN); + + if (!chdir(A_CERT_PATH)) + { + struct dirent **filelist; + int n; + + plog("Changing to directory '%s'",A_CERT_PATH); + n = scandir(A_CERT_PATH, &filelist, file_select, alphasort); + + if (n > 0) + { + while (n--) + { + chunk_t blob = empty_chunk; + bool pgp = FALSE; + + if (load_coded_file(filelist[n]->d_name, NULL, "acert", &blob, &pgp)) + { + x509acert_t *ac = alloc_thing(x509acert_t, "x509acert"); + + *ac = empty_ac; + + if (parse_ac(blob, ac) + && verify_x509acert(ac, FALSE)) + add_acert(ac); + else + free_acert(ac); + } + free(filelist[n]); + } + free(filelist); + } + } + /* restore directory path */ + chdir(save_dir); +} + +/* + * lists group attributes separated by commas on a single line + */ +void +format_groups(const ietfAttrList_t *list, char *buf, int len) +{ + bool first_group = TRUE; + + while (list != NULL && len > 0) + { + ietfAttr_t *attr = list->attr; + + if (attr->kind == IETF_ATTRIBUTE_OCTETS + || attr->kind == IETF_ATTRIBUTE_STRING) + { + int written = snprintf(buf, len, "%s%.*s" + , (first_group)? "" : ", " + , (int)attr->value.len, attr->value.ptr); + + first_group = FALSE; + + /* return value of snprintf() up to glibc 2.0.6 */ + if (written < 0) + break; + + buf += written; + len -= written; + } + list = list->next; + } +} + +/* + * list all X.509 attribute certificates in the chained list + */ +void +list_acerts(bool utc) +{ + x509acert_t *ac = x509acerts; + time_t now; + + /* determine the current time */ + time(&now); + + if (ac != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of X.509 Attribute Certificates:"); + whack_log(RC_COMMENT, " "); + } + + while (ac != NULL) + { + u_char buf[BUF_LEN]; + + whack_log(RC_COMMENT, "%s",timetoa(&ac->installed, utc)); + if (ac->entityName.ptr != NULL) + { + dntoa(buf, BUF_LEN, ac->entityName); + whack_log(RC_COMMENT, " holder: '%s'", buf); + } + if (ac->holderIssuer.ptr != NULL) + { + dntoa(buf, BUF_LEN, ac->holderIssuer); + whack_log(RC_COMMENT, " hissuer: '%s'", buf); + } + if (ac->holderSerial.ptr != NULL) + { + datatot(ac->holderSerial.ptr, ac->holderSerial.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " hserial: %s", buf); + } + if (ac->groups != NULL) + { + format_groups(ac->groups, buf, BUF_LEN); + whack_log(RC_COMMENT, " groups: %s", buf); + } + dntoa(buf, BUF_LEN, ac->issuerName); + whack_log(RC_COMMENT, " issuer: '%s'", buf); + datatot(ac->serialNumber.ptr, ac->serialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " serial: %s", buf); + whack_log(RC_COMMENT, " validity: not before %s %s", + timetoa(&ac->notBefore, utc), + (ac->notBefore < now)?"ok":"fatal (not valid yet)"); + whack_log(RC_COMMENT, " not after %s %s", + timetoa(&ac->notAfter, utc), + check_expiry(ac->notAfter, ACERT_WARNING_INTERVAL, TRUE)); + if (ac->authKeyID.ptr != NULL) + { + datatot(ac->authKeyID.ptr, ac->authKeyID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authkey: %s", buf); + } + if (ac->authKeySerialNumber.ptr != NULL) + { + datatot(ac->authKeySerialNumber.ptr, ac->authKeySerialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " aserial: %s", buf); + } + + ac = ac->next; + } +} + +/* + * list all group attributes in alphabetical order + */ +void +list_groups(bool utc) +{ + ietfAttrList_t *list = ietfAttributes; + + if (list != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of Group Attributes:"); + whack_log(RC_COMMENT, " "); + } + + while (list != NULL) + { + ietfAttr_t *attr = list->attr; + + whack_log(RC_COMMENT, "%s, count: %d", timetoa(&attr->installed, utc), + attr->count); + + switch (attr->kind) + { + case IETF_ATTRIBUTE_OCTETS: + case IETF_ATTRIBUTE_STRING: + whack_log(RC_COMMENT, " %.*s", (int)attr->value.len, attr->value.ptr); + break; + case IETF_ATTRIBUTE_OID: + whack_log(RC_COMMENT, " OID"); + break; + default: + break; + } + + list = list->next; + } +} diff --git a/src/pluto/ac.h b/src/pluto/ac.h new file mode 100644 index 000000000..3913d745d --- /dev/null +++ b/src/pluto/ac.h @@ -0,0 +1,103 @@ +/* Support of X.509 attribute certificates + * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler + * Copyright (C) 2003 Martin Berner, Lukas Suter + + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ac.h,v 1.8 2005/02/17 20:56:04 as Exp $ + */ + +#ifndef _AC_H +#define _AC_H + +/* definition of ietfAttribute kinds */ + +typedef enum { + IETF_ATTRIBUTE_OCTETS = 0, + IETF_ATTRIBUTE_OID = 1, + IETF_ATTRIBUTE_STRING = 2 +} ietfAttribute_t; + +/* access structure for an ietfAttribute */ + +typedef struct ietfAttr ietfAttr_t; + +struct ietfAttr { + time_t installed; + int count; + ietfAttribute_t kind; + chunk_t value; +}; + +typedef struct ietfAttrList ietfAttrList_t; + +struct ietfAttrList { + ietfAttrList_t *next; + ietfAttr_t *attr; +}; + + +/* access structure for an X.509 attribute certificate */ + +typedef struct x509acert x509acert_t; + +struct x509acert { + x509acert_t *next; + time_t installed; + chunk_t certificate; + chunk_t certificateInfo; + u_int version; + /* holder */ + /* baseCertificateID */ + chunk_t holderIssuer; + chunk_t holderSerial; + chunk_t entityName; + /* v2Form */ + chunk_t issuerName; + /* signature */ + int sigAlg; + chunk_t serialNumber; + /* attrCertValidityPeriod */ + time_t notBefore; + time_t notAfter; + /* attributes */ + ietfAttrList_t *charging; + ietfAttrList_t *groups; + /* extensions */ + chunk_t authKeyID; + chunk_t authKeySerialNumber; + bool noRevAvail; + /* signatureAlgorithm */ + int algorithm; + chunk_t signature; +}; + +/* used for initialization */ +extern const x509acert_t empty_ac; + +extern void unshare_ietfAttrList(ietfAttrList_t **listp); +extern void free_ietfAttrList(ietfAttrList_t *list); +extern void decode_groups(char *groups, ietfAttrList_t **listp); +extern bool group_membership(const ietfAttrList_t *my_list + , const char *conn, const ietfAttrList_t *conn_list); +extern bool parse_ac(chunk_t blob, x509acert_t *ac); +extern bool verify_x509acert(x509acert_t *ac, bool strict); +extern x509acert_t* get_x509acert(chunk_t issuer, chunk_t serial); +extern void load_acerts(void); +extern void free_acert(x509acert_t *ac); +extern void free_acerts(void); +extern void list_acerts(bool utc); +extern void list_groups(bool utc); +extern void format_groups(const ietfAttrList_t *list, char *buf, int len); + + +#endif /* _AH_H */ diff --git a/src/pluto/adns.c b/src/pluto/adns.c new file mode 100644 index 000000000..c5977d23c --- /dev/null +++ b/src/pluto/adns.c @@ -0,0 +1,615 @@ +/* Pluto Asynchronous DNS Helper Program -- for internal use only! + * Copyright (C) 2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: adns.c,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#ifndef USE_LWRES /* whole file! */ + +/* This program executes as multiple processes. The Master process + * receives queries (struct adns_query messages) from Pluto and distributes + * them amongst Worker processes. These Worker processes are created + * by the Master whenever a query arrives and no existing Worker is free. + * At most MAX_WORKERS will be created; after that, the Master will queue + * queries until a Worker becomes free. When a Worker has an answer from + * the resolver, it sends the answer as a struct adns_answer message to the + * Master. The Master then forwards the answer to Pluto, noting that + * the Worker is free to accept another query. + * + * The protocol is simple: Pluto sends a sequence of queries and receives + * a sequence of answers. select(2) is used by Pluto and by the Master + * process to decide when to read, but writes are done without checking + * for readiness. Communications is via pipes. Since only one process + * can write to each pipe, messages will not be interleaved. Fixed length + * records are used for simplicity. + * + * Pluto needs a way to indicate to the Master when to shut down + * and the Master needs to indicate this to each worker. EOF on the pipe + * signifies this. + * + * The interfaces between these components are considered private to + * Pluto. This allows us to get away with less checking. This is a + * reason to use pipes instead of TCP/IP. + * + * Although the code uses plain old UNIX processes, it could be modified + * to use threads. That might reduce resource requirements. It would + * preclude running on systems without thread-safe resolvers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* ??? for h_errno */ + +#include + +/* GCC magic! */ +#ifdef GCC_LINT +# define UNUSED __attribute__ ((unused)) +#else +# define UNUSED /* ignore */ +#endif + +#include "constants.h" +#include "adns.h" /* needs */ + +/* shared by all processes */ + +static const char *name; /* program name, for messages */ + +static bool debug = FALSE; + +/* Read a variable-length record from a pipe (and no more!). + * First bytes must be a size_t containing the length. + * HES_CONTINUE if record read + * HES_OK if EOF + * HES_IO_ERROR_IN if errno tells the tale. + * Others are errors. + */ +static enum helper_exit_status +read_pipe(int fd, unsigned char *stuff, size_t minlen, size_t maxlen) +{ + size_t n = 0; + size_t goal = minlen; + + do { + ssize_t m = read(fd, stuff + n, goal - n); + + if (m == -1) + { + if (errno != EINTR) + { + syslog(LOG_ERR, "Input error on pipe: %s", strerror(errno)); + return HES_IO_ERROR_IN; + } + } + else if (m == 0) + { + return HES_OK; /* treat empty message as EOF */ + } + else + { + n += m; + if (n >= sizeof(size_t)) + { + goal = *(size_t *)(void *)stuff; + if (goal < minlen || maxlen < goal) + { + if (debug) + fprintf(stderr, "%lu : [%lu, %lu]\n" + , (unsigned long)goal + , (unsigned long)minlen, (unsigned long)maxlen); + return HES_BAD_LEN; + } + } + } + } while (n < goal); + + return HES_CONTINUE; +} + +/* Write a variable-length record to a pipe. + * First bytes must be a size_t containing the length. + * HES_CONTINUE if record written + * Others are errors. + */ +static enum helper_exit_status +write_pipe(int fd, const unsigned char *stuff) +{ + size_t len = *(const size_t *)(const void *)stuff; + size_t n = 0; + + do { + ssize_t m = write(fd, stuff + n, len - n); + + if (m == -1) + { + /* error, but ignore and retry if EINTR */ + if (errno != EINTR) + { + syslog(LOG_ERR, "Output error from master: %s", strerror(errno)); + return HES_IO_ERROR_OUT; + } + } + else + { + n += m; + } + } while (n != len); + return HES_CONTINUE; +} + +/**************** worker process ****************/ + +/* The interface in RHL6.x and BIND distribution 8.2.2 are different, + * so we build some of our own :-( + */ + +/* Support deprecated interface to allow for older releases of the resolver. + * Fake new interface! + * See resolver(3) bind distribution (should be in RHL6.1, but isn't). + * __RES was 19960801 in RHL6.2, an old resolver. + */ + +#if (__RES) <= 19960801 +# define OLD_RESOLVER 1 +#endif + +#ifdef OLD_RESOLVER + +# define res_ninit(statp) res_init() +# define res_nquery(statp, dname, class, type, answer, anslen) \ + res_query(dname, class, type, answer, anslen) +# define res_nclose(statp) res_close() + +static struct __res_state *statp = &_res; + +#else /* !OLD_RESOLVER */ + +static struct __res_state my_res_state /* = { 0 } */; +static res_state statp = &my_res_state; + +#endif /* !OLD_RESOLVER */ + +static int +worker(int qfd, int afd) +{ + { + int r = res_ninit(statp); + + if (r != 0) + { + syslog(LOG_ERR, "cannot initialize resolver"); + return HES_RES_INIT; + } +#ifndef OLD_RESOLVER + statp->options |= RES_ROTATE; +#endif + statp->options |= RES_DEBUG; + } + + for (;;) + { + struct adns_query q; + struct adns_answer a; + + enum helper_exit_status r = read_pipe(qfd, (unsigned char *)&q + , sizeof(q), sizeof(q)); + + if (r != HES_CONTINUE) + return r; /* some kind of exit */ + + if (q.qmagic != ADNS_Q_MAGIC) + { + syslog(LOG_ERR, "error in input from master: bad magic"); + return HES_BAD_MAGIC; + } + + a.amagic = ADNS_A_MAGIC; + a.serial = q.serial; + + a.result = res_nquery(statp, q.name_buf, C_IN, q.type, a.ans, sizeof(a.ans)); + a.h_errno_val = h_errno; + + a.len = offsetof(struct adns_answer, ans) + (a.result < 0? 0 : a.result); + +#ifdef DEBUG + if (((q.debugging & IMPAIR_DELAY_ADNS_KEY_ANSWER) && q.type == T_KEY) + || ((q.debugging & IMPAIR_DELAY_ADNS_TXT_ANSWER) && q.type == T_TXT)) + sleep(30); /* delay the answer */ +#endif + + /* write answer, possibly a bit at a time */ + r = write_pipe(afd, (const unsigned char *)&a); + + if (r != HES_CONTINUE) + return r; /* some kind of exit */ + } +} + +/**************** master process ****************/ + +bool eof_from_pluto = FALSE; +#define PLUTO_QFD 0 /* queries come on stdin */ +#define PLUTO_AFD 1 /* answers go out on stdout */ + +#ifndef MAX_WORKERS +# define MAX_WORKERS 10 /* number of in-flight queries */ +#endif + +struct worker_info { + int qfd; /* query pipe's file descriptor */ + int afd; /* answer pipe's file descriptor */ + pid_t pid; + bool busy; + void *continuation; /* of outstanding request */ +}; + +static struct worker_info wi[MAX_WORKERS]; +static struct worker_info *wi_roof = wi; + +/* request FIFO */ + +struct query_list { + struct query_list *next; + struct adns_query aq; +}; + +static struct query_list *oldest_query = NULL; +static struct query_list *newest_query; /* undefined when oldest == NULL */ +static struct query_list *free_queries = NULL; + +static bool +spawn_worker(void) +{ + int qfds[2]; + int afds[2]; + pid_t p; + + if (pipe(qfds) != 0 || pipe(afds) != 0) + { + syslog(LOG_ERR, "pipe(2) failed: %s", strerror(errno)); + exit(HES_PIPE); + } + + wi_roof->qfd = qfds[1]; /* write end of query pipe */ + wi_roof->afd = afds[0]; /* read end of answer pipe */ + + p = fork(); + if (p == -1) + { + /* fork failed: ignore if at least one worker exists */ + if (wi_roof == wi) + { + syslog(LOG_ERR, "fork(2) error creating first worker: %s", strerror(errno)); + exit(HES_FORK); + } + close(qfds[0]); + close(qfds[1]); + close(afds[0]); + close(afds[1]); + return FALSE; + } + else if (p == 0) + { + /* child */ + struct worker_info *w; + + close(PLUTO_QFD); + close(PLUTO_AFD); + /* close all master pipes, including ours */ + for (w = wi; w <= wi_roof; w++) + { + close(w->qfd); + close(w->afd); + } + exit(worker(qfds[0], afds[1])); + } + else + { + /* parent */ + struct worker_info *w = wi_roof++; + + w->pid = p; + w->busy = FALSE; + close(qfds[0]); + close(afds[1]); + return TRUE; + } +} + +static void +send_eof(struct worker_info *w) +{ + pid_t p; + int status; + + close(w->qfd); + w->qfd = NULL_FD; + + close(w->afd); + w->afd = NULL_FD; + + /* reap child */ + p = waitpid(w->pid, &status, 0); + /* ignore result -- what could we do with it? */ +} + +static void +forward_query(struct worker_info *w) +{ + struct query_list *q = oldest_query; + + if (q == NULL) + { + if (eof_from_pluto) + send_eof(w); + } + else + { + enum helper_exit_status r + = write_pipe(w->qfd, (const unsigned char *) &q->aq); + + if (r != HES_CONTINUE) + exit(r); + + w->busy = TRUE; + + oldest_query = q->next; + q->next = free_queries; + free_queries = q; + } +} + +static void +query(void) +{ + struct query_list *q = free_queries; + enum helper_exit_status r; + + /* find an unused queue entry */ + if (q == NULL) + { + q = malloc(sizeof(*q)); + if (q == NULL) + { + syslog(LOG_ERR, "malloc(3) failed"); + exit(HES_MALLOC); + } + } + else + { + free_queries = q->next; + } + + r = read_pipe(PLUTO_QFD, (unsigned char *)&q->aq + , sizeof(q->aq), sizeof(q->aq)); + + if (r == HES_OK) + { + /* EOF: we're done, except for unanswered queries */ + struct worker_info *w; + + eof_from_pluto = TRUE; + q->next = free_queries; + free_queries = q; + + /* Send bye-bye to unbusy processes. + * Note that if there are queued queries, there won't be + * any non-busy workers. + */ + for (w = wi; w != wi_roof; w++) + if (!w->busy) + send_eof(w); + } + else if (r != HES_CONTINUE) + { + exit(r); + } + else if (q->aq.qmagic != ADNS_Q_MAGIC) + { + syslog(LOG_ERR, "error in query from Pluto: bad magic"); + exit(HES_BAD_MAGIC); + } + else + { + struct worker_info *w; + + /* got a query */ + + /* add it to FIFO */ + q->next = NULL; + if (oldest_query == NULL) + oldest_query = q; + else + newest_query->next = q; + newest_query = q; + + /* See if any worker available */ + for (w = wi; ; w++) + { + if (w == wi_roof) + { + /* no free worker */ + if (w == wi + MAX_WORKERS) + break; /* no more to be created */ + /* make a new one */ + if (!spawn_worker()) + break; /* cannot create one at this time */ + } + if (!w->busy) + { + /* assign first to free worker */ + forward_query(w); + break; + } + } + } + return; +} + +static void +answer(struct worker_info *w) +{ + struct adns_answer a; + enum helper_exit_status r = read_pipe(w->afd, (unsigned char *)&a + , offsetof(struct adns_answer, ans), sizeof(a)); + + if (r == HES_OK) + { + /* unexpected EOF */ + syslog(LOG_ERR, "unexpected EOF from worker"); + exit(HES_IO_ERROR_IN); + } + else if (r != HES_CONTINUE) + { + exit(r); + } + else if (a.amagic != ADNS_A_MAGIC) + { + syslog(LOG_ERR, "Input from worker error: bad magic"); + exit(HES_BAD_MAGIC); + } + else if (a.continuation != w->continuation) + { + /* answer doesn't match query */ + syslog(LOG_ERR, "Input from worker error: continuation mismatch"); + exit(HES_SYNC); + } + else + { + /* pass the answer on to Pluto */ + enum helper_exit_status r + = write_pipe(PLUTO_AFD, (const unsigned char *) &a); + + if (r != HES_CONTINUE) + exit(r); + w->busy = FALSE; + forward_query(w); + } +} + +/* assumption: input limited; accept blocking on output */ +static int +master(void) +{ + for (;;) + { + fd_set readfds; + int maxfd = PLUTO_QFD; /* approximate lower bound */ + int ndes = 0; + struct worker_info *w; + + FD_ZERO(&readfds); + if (!eof_from_pluto) + { + FD_SET(PLUTO_QFD, &readfds); + ndes++; + } + for (w = wi; w != wi_roof; w++) + { + if (w->busy) + { + FD_SET(w->afd, &readfds); + ndes++; + if (maxfd < w->afd) + maxfd = w->afd; + } + } + + if (ndes == 0) + return HES_OK; /* done! */ + + do { + ndes = select(maxfd + 1, &readfds, NULL, NULL, NULL); + } while (ndes == -1 && errno == EINTR); + if (ndes == -1) + { + syslog(LOG_ERR, "select(2) error: %s", strerror(errno)); + exit(HES_IO_ERROR_SELECT); + } + else if (ndes > 0) + { + if (FD_ISSET(PLUTO_QFD, &readfds)) + { + query(); + ndes--; + } + for (w = wi; ndes > 0 && w != wi_roof; w++) + { + if (w->busy && FD_ISSET(w->afd, &readfds)) + { + answer(w); + ndes--; + } + } + } + } +} + +/* Not to be invoked by strangers -- user hostile. + * Mandatory args: query-fd answer-fd + * Optional arg: -d, signifying "debug". + */ + +static void +adns_usage(const char *fmt, const char *arg) +{ + const char **sp = ipsec_copyright_notice(); + + fprintf(stderr, "INTERNAL TO PLUTO: DO NOT EXECUTE\n"); + + fprintf(stderr, fmt, arg); + fprintf(stderr, "\n%s\n", ipsec_version_string()); + + for (; *sp != NULL; sp++) + fprintf(stderr, "%s\n", *sp); + + syslog(LOG_ERR, fmt, arg); + exit(HES_INVOCATION); +} + +int +main(int argc UNUSED, char **argv) +{ + int i = 1; + + name = argv[0]; + + while (i < argc) + { + if (streq(argv[i], "-d")) + { + i++; + debug = TRUE; + } + else + { + adns_usage("unexpected argument \"%s\"", argv[i]); + /*NOTREACHED*/ + } + } + + return master(); +} + +#endif /* !USE_LWRES */ diff --git a/src/pluto/adns.h b/src/pluto/adns.h new file mode 100644 index 000000000..00fc4ad07 --- /dev/null +++ b/src/pluto/adns.h @@ -0,0 +1,75 @@ +/* Pluto Asynchronous DNS Helper Program's Header + * Copyright (C) 2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: adns.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#ifndef USE_LWRES /* whole file! */ + +/* The interface in RHL6.x and BIND distribution 8.2.2 are different, + * so we build some of our own :-( + */ + +# ifndef NS_MAXDNAME +# define NS_MAXDNAME MAXDNAME /* I hope this is long enough for IPv6 */ +# endif + +# ifndef NS_PACKETSZ +# define NS_PACKETSZ PACKETSZ +# endif + +/* protocol version */ + +#define ADNS_Q_MAGIC (((((('d' << 8) + 'n') << 8) + 's') << 8) + 4) +#define ADNS_A_MAGIC (((((('d' << 8) + 'n') << 8) + 's') << 8) + 128 + 4) + +/* note: both struct adns_query and struct adns_answer must start with + * size_t len; + */ + +struct adns_query { + size_t len; + unsigned int qmagic; + unsigned long serial; + lset_t debugging; /* only used #ifdef DEBUG, but don't want layout to change */ + u_char name_buf[NS_MAXDNAME + 2]; + int type; /* T_KEY or T_TXT */ +}; + +struct adns_answer { + size_t len; + unsigned int amagic; + unsigned long serial; + struct adns_continuation *continuation; + int result; + int h_errno_val; + u_char ans[NS_PACKETSZ * 10]; /* very probably bigger than necessary */ +}; + +enum helper_exit_status { + HES_CONTINUE = -1, /* not an exit */ + HES_OK = 0, /* all's well that ends well (perhaps EOF) */ + HES_INVOCATION, /* improper invocation */ + HES_IO_ERROR_SELECT, /* IO error in select() */ + HES_MALLOC, /* malloc failed */ + HES_IO_ERROR_IN, /* error reading pipe */ + HES_IO_ERROR_OUT, /* error reading pipe */ + HES_PIPE, /* pipe(2) failed */ + HES_SYNC, /* answer from worker doesn't match query */ + HES_FORK, /* fork(2) failed */ + HES_RES_INIT, /* resolver initialization failed */ + HES_BAD_LEN, /* implausible .len field */ + HES_BAD_MAGIC, /* .magic field wrong */ +}; + +#endif /* !USE_LWRES */ diff --git a/src/pluto/alg/ike_alg_aes.c b/src/pluto/alg/ike_alg_aes.c new file mode 100644 index 000000000..44de09b4c --- /dev/null +++ b/src/pluto/alg/ike_alg_aes.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "libaes/aes_cbc.h" +#include "alg_info.h" +#include "ike_alg.h" + +#define AES_CBC_BLOCK_SIZE (128/BITS_PER_BYTE) +#define AES_KEY_MIN_LEN 128 +#define AES_KEY_DEF_LEN 128 +#define AES_KEY_MAX_LEN 256 + +static void +do_aes(u_int8_t *buf, size_t buf_len, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc) +{ + aes_context aes_ctx; + char iv_bak[AES_CBC_BLOCK_SIZE]; + char *new_iv = NULL; /* logic will avoid copy to NULL */ + + aes_set_key(&aes_ctx, key, key_size, 0); + + /* + * my AES cbc does not touch passed IV (optimization for + * ESP handling), so I must "emulate" des-like IV + * crunching + */ + if (!enc) + memcpy(new_iv=iv_bak, (char*) buf + buf_len - AES_CBC_BLOCK_SIZE + , AES_CBC_BLOCK_SIZE); + + AES_cbc_encrypt(&aes_ctx, buf, buf, buf_len, iv, enc); + + if (enc) + new_iv = (char*) buf + buf_len-AES_CBC_BLOCK_SIZE; + + memcpy(iv, new_iv, AES_CBC_BLOCK_SIZE); +} + +struct encrypt_desc algo_aes = +{ + algo_type: IKE_ALG_ENCRYPT, + algo_id: OAKLEY_AES_CBC, + algo_next: NULL, + enc_ctxsize: sizeof(aes_context), + enc_blocksize: AES_CBC_BLOCK_SIZE, + keyminlen: AES_KEY_MIN_LEN, + keydeflen: AES_KEY_DEF_LEN, + keymaxlen: AES_KEY_MAX_LEN, + do_crypt: do_aes, +}; + +int ike_alg_aes_init(void); + +int +ike_alg_aes_init(void) +{ + int ret = ike_alg_register_enc(&algo_aes); + return ret; +} +/* +IKE_ALG_INIT_NAME: ike_alg_aes_init +*/ diff --git a/src/pluto/alg/ike_alg_blowfish.c b/src/pluto/alg/ike_alg_blowfish.c new file mode 100644 index 000000000..2bbef051b --- /dev/null +++ b/src/pluto/alg/ike_alg_blowfish.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "libblowfish/blowfish.h" +#include "alg_info.h" +#include "ike_alg.h" + +#define BLOWFISH_CBC_BLOCK_SIZE 8 /* block size */ +#define BLOWFISH_KEY_MIN_LEN 128 +#define BLOWFISH_KEY_MAX_LEN 448 + + +static void +do_blowfish(u_int8_t *buf, size_t buf_len, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc) +{ + BF_KEY bf_ctx; + + BF_set_key(&bf_ctx, key_size , key); + BF_cbc_encrypt(buf, buf, buf_len, &bf_ctx, iv, enc); +} + +struct encrypt_desc algo_blowfish = +{ + algo_type: IKE_ALG_ENCRYPT, + algo_id: OAKLEY_BLOWFISH_CBC, + algo_next: NULL, + enc_ctxsize: sizeof(BF_KEY), + enc_blocksize: BLOWFISH_CBC_BLOCK_SIZE, + keyminlen: BLOWFISH_KEY_MIN_LEN, + keydeflen: BLOWFISH_KEY_MIN_LEN, + keymaxlen: BLOWFISH_KEY_MAX_LEN, + do_crypt: do_blowfish, +}; + +int ike_alg_blowfish_init(void); + +int +ike_alg_blowfish_init(void) +{ + int ret = ike_alg_register_enc(&algo_blowfish); + + return ret; +} +/* +IKE_ALG_INIT_NAME: ike_alg_blowfish_init +*/ diff --git a/src/pluto/alg/ike_alg_serpent.c b/src/pluto/alg/ike_alg_serpent.c new file mode 100644 index 000000000..fb01caa41 --- /dev/null +++ b/src/pluto/alg/ike_alg_serpent.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "libserpent/serpent_cbc.h" +#include "alg_info.h" +#include "ike_alg.h" + +#define SERPENT_CBC_BLOCK_SIZE (128/BITS_PER_BYTE) +#define SERPENT_KEY_MIN_LEN 128 +#define SERPENT_KEY_DEF_LEN 128 +#define SERPENT_KEY_MAX_LEN 256 + +static void +do_serpent(u_int8_t *buf, size_t buf_size, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc) +{ + serpent_context serpent_ctx; + char iv_bak[SERPENT_CBC_BLOCK_SIZE]; + char *new_iv = NULL; /* logic will avoid copy to NULL */ + + + serpent_set_key(&serpent_ctx, key, key_size); + /* + * my SERPENT cbc does not touch passed IV (optimization for + * ESP handling), so I must "emulate" des-like IV + * crunching + */ + if (!enc) + memcpy(new_iv=iv_bak, + (char*) buf + buf_size-SERPENT_CBC_BLOCK_SIZE, + SERPENT_CBC_BLOCK_SIZE); + + serpent_cbc_encrypt(&serpent_ctx, buf, buf, buf_size, iv, enc); + + if (enc) + new_iv = (char*) buf + buf_size-SERPENT_CBC_BLOCK_SIZE; + + memcpy(iv, new_iv, SERPENT_CBC_BLOCK_SIZE); +} + +struct encrypt_desc encrypt_desc_serpent = +{ + algo_type: IKE_ALG_ENCRYPT, + algo_id: OAKLEY_SERPENT_CBC, + algo_next: NULL, + enc_ctxsize: sizeof(struct serpent_context), + enc_blocksize: SERPENT_CBC_BLOCK_SIZE, + keyminlen: SERPENT_KEY_MIN_LEN, + keydeflen: SERPENT_KEY_DEF_LEN, + keymaxlen: SERPENT_KEY_MAX_LEN, + do_crypt: do_serpent, +}; + +int ike_alg_serpent_init(void); + +int +ike_alg_serpent_init(void) +{ + int ret = ike_alg_register_enc(&encrypt_desc_serpent); + + return ret; +} +/* +IKE_ALG_INIT_NAME: ike_alg_serpent_init +*/ diff --git a/src/pluto/alg/ike_alg_sha2.c b/src/pluto/alg/ike_alg_sha2.c new file mode 100644 index 000000000..6b7c8438c --- /dev/null +++ b/src/pluto/alg/ike_alg_sha2.c @@ -0,0 +1,634 @@ +#include +#include +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "libsha2/sha2.h" +#include "alg_info.h" +#include "ike_alg.h" + +static void +sha256_hash_final(u_char *hash, sha256_context *ctx) +{ + sha256_final(ctx); + memcpy(hash, ctx->sha_out, SHA2_256_DIGEST_SIZE); +} + +static void +sha384_hash_final(u_char *hash, sha512_context *ctx) +{ + sha512_final(ctx); + memcpy(hash, ctx->sha_out, SHA2_384_DIGEST_SIZE); +} + +static void +sha512_hash_final(u_char *hash, sha512_context *ctx) +{ + sha512_final(ctx); + memcpy(hash, ctx->sha_out, SHA2_512_DIGEST_SIZE); +} + +/* SHA-256 hash test vectors + * from "The Secure Hash Algorithm Validation System (SHAVS)" + * July 22, 2004, Lawrence E. Bassham III, NIST + */ + +static const u_char sha256_short2_msg[] = { + 0x19 +}; + +static const u_char sha256_short2_msg_digest[] = { + 0x68, 0xaa, 0x2e, 0x2e, 0xe5, 0xdf, 0xf9, 0x6e, + 0x33, 0x55, 0xe6, 0xc7, 0xee, 0x37, 0x3e, 0x3d, + 0x6a, 0x4e, 0x17, 0xf7, 0x5f, 0x95, 0x18, 0xd8, + 0x43, 0x70, 0x9c, 0x0c, 0x9b, 0xc3, 0xe3, 0xd4 +}; + +static const u_char sha256_short4_msg[] = { + 0xe3, 0xd7, 0x25, 0x70, 0xdc, 0xdd, 0x78, 0x7c, + 0xe3, 0x88, 0x7a, 0xb2, 0xcd, 0x68, 0x46, 0x52 +}; + +static const u_char sha256_short4_msg_digest[] = { + 0x17, 0x5e, 0xe6, 0x9b, 0x02, 0xba, 0x9b, 0x58, + 0xe2, 0xb0, 0xa5, 0xfd, 0x13, 0x81, 0x9c, 0xea, + 0x57, 0x3f, 0x39, 0x40, 0xa9, 0x4f, 0x82, 0x51, + 0x28, 0xcf, 0x42, 0x09, 0xbe, 0xab, 0xb4, 0xe8 +}; + +static const u_char sha256_long2_msg[] = { + 0x83, 0x26, 0x75, 0x4e, 0x22, 0x77, 0x37, 0x2f, + 0x4f, 0xc1, 0x2b, 0x20, 0x52, 0x7a, 0xfe, 0xf0, + 0x4d, 0x8a, 0x05, 0x69, 0x71, 0xb1, 0x1a, 0xd5, + 0x71, 0x23, 0xa7, 0xc1, 0x37, 0x76, 0x00, 0x00, + 0xd7, 0xbe, 0xf6, 0xf3, 0xc1, 0xf7, 0xa9, 0x08, + 0x3a, 0xa3, 0x9d, 0x81, 0x0d, 0xb3, 0x10, 0x77, + 0x7d, 0xab, 0x8b, 0x1e, 0x7f, 0x02, 0xb8, 0x4a, + 0x26, 0xc7, 0x73, 0x32, 0x5f, 0x8b, 0x23, 0x74, + 0xde, 0x7a, 0x4b, 0x5a, 0x58, 0xcb, 0x5c, 0x5c, + 0xf3, 0x5b, 0xce, 0xe6, 0xfb, 0x94, 0x6e, 0x5b, + 0xd6, 0x94, 0xfa, 0x59, 0x3a, 0x8b, 0xeb, 0x3f, + 0x9d, 0x65, 0x92, 0xec, 0xed, 0xaa, 0x66, 0xca, + 0x82, 0xa2, 0x9d, 0x0c, 0x51, 0xbc, 0xf9, 0x33, + 0x62, 0x30, 0xe5, 0xd7, 0x84, 0xe4, 0xc0, 0xa4, + 0x3f, 0x8d, 0x79, 0xa3, 0x0a, 0x16, 0x5c, 0xba, + 0xbe, 0x45, 0x2b, 0x77, 0x4b, 0x9c, 0x71, 0x09, + 0xa9, 0x7d, 0x13, 0x8f, 0x12, 0x92, 0x28, 0x96, + 0x6f, 0x6c, 0x0a, 0xdc, 0x10, 0x6a, 0xad, 0x5a, + 0x9f, 0xdd, 0x30, 0x82, 0x57, 0x69, 0xb2, 0xc6, + 0x71, 0xaf, 0x67, 0x59, 0xdf, 0x28, 0xeb, 0x39, + 0x3d, 0x54, 0xd6 +}; + +static const u_char sha256_long2_msg_digest[] = { + 0x97, 0xdb, 0xca, 0x7d, 0xf4, 0x6d, 0x62, 0xc8, + 0xa4, 0x22, 0xc9, 0x41, 0xdd, 0x7e, 0x83, 0x5b, + 0x8a, 0xd3, 0x36, 0x17, 0x63, 0xf7, 0xe9, 0xb2, + 0xd9, 0x5f, 0x4f, 0x0d, 0xa6, 0xe1, 0xcc, 0xbc +}; + +static const hash_testvector_t sha256_hash_testvectors[] = { + { sizeof(sha256_short2_msg), sha256_short2_msg, sha256_short2_msg_digest }, + { sizeof(sha256_short4_msg), sha256_short4_msg, sha256_short4_msg_digest }, + { sizeof(sha256_long2_msg), sha256_long2_msg, sha256_long2_msg_digest }, + { 0, NULL, NULL } +}; + +/* SHA-384 hash test vectors + * from "The Secure Hash Algorithm Validation System (SHAVS)" + * July 22, 2004, Lawrence E. Bassham III, NIST + */ + +static const u_char sha384_short2_msg[] = { + 0xb9 +}; + +static const u_char sha384_short2_msg_digest[] = { + 0xbc, 0x80, 0x89, 0xa1, 0x90, 0x07, 0xc0, 0xb1, + 0x41, 0x95, 0xf4, 0xec, 0xc7, 0x40, 0x94, 0xfe, + 0xc6, 0x4f, 0x01, 0xf9, 0x09, 0x29, 0x28, 0x2c, + 0x2f, 0xb3, 0x92, 0x88, 0x15, 0x78, 0x20, 0x8a, + 0xd4, 0x66, 0x82, 0x8b, 0x1c, 0x6c, 0x28, 0x3d, + 0x27, 0x22, 0xcf, 0x0a, 0xd1, 0xab, 0x69, 0x38 +}; + +static const u_char sha384_short4_msg[] = { + 0xa4, 0x1c, 0x49, 0x77, 0x79, 0xc0, 0x37, 0x5f, + 0xf1, 0x0a, 0x7f, 0x4e, 0x08, 0x59, 0x17, 0x39 +}; + +static const u_char sha384_short4_msg_digest[] = { + 0xc9, 0xa6, 0x84, 0x43, 0xa0, 0x05, 0x81, 0x22, + 0x56, 0xb8, 0xec, 0x76, 0xb0, 0x05, 0x16, 0xf0, + 0xdb, 0xb7, 0x4f, 0xab, 0x26, 0xd6, 0x65, 0x91, + 0x3f, 0x19, 0x4b, 0x6f, 0xfb, 0x0e, 0x91, 0xea, + 0x99, 0x67, 0x56, 0x6b, 0x58, 0x10, 0x9c, 0xbc, + 0x67, 0x5c, 0xc2, 0x08, 0xe4, 0xc8, 0x23, 0xf7 +}; + +static const u_char sha384_long2_msg[] = { + 0x39, 0x96, 0x69, 0xe2, 0x8f, 0x6b, 0x9c, 0x6d, + 0xbc, 0xbb, 0x69, 0x12, 0xec, 0x10, 0xff, 0xcf, + 0x74, 0x79, 0x03, 0x49, 0xb7, 0xdc, 0x8f, 0xbe, + 0x4a, 0x8e, 0x7b, 0x3b, 0x56, 0x21, 0xdb, 0x0f, + 0x3e, 0x7d, 0xc8, 0x7f, 0x82, 0x32, 0x64, 0xbb, + 0xe4, 0x0d, 0x18, 0x11, 0xc9, 0xea, 0x20, 0x61, + 0xe1, 0xc8, 0x4a, 0xd1, 0x0a, 0x23, 0xfa, 0xc1, + 0x72, 0x7e, 0x72, 0x02, 0xfc, 0x3f, 0x50, 0x42, + 0xe6, 0xbf, 0x58, 0xcb, 0xa8, 0xa2, 0x74, 0x6e, + 0x1f, 0x64, 0xf9, 0xb9, 0xea, 0x35, 0x2c, 0x71, + 0x15, 0x07, 0x05, 0x3c, 0xf4, 0xe5, 0x33, 0x9d, + 0x52, 0x86, 0x5f, 0x25, 0xcc, 0x22, 0xb5, 0xe8, + 0x77, 0x84, 0xa1, 0x2f, 0xc9, 0x61, 0xd6, 0x6c, + 0xb6, 0xe8, 0x95, 0x73, 0x19, 0x9a, 0x2c, 0xe6, + 0x56, 0x5c, 0xbd, 0xf1, 0x3d, 0xca, 0x40, 0x38, + 0x32, 0xcf, 0xcb, 0x0e, 0x8b, 0x72, 0x11, 0xe8, + 0x3a, 0xf3, 0x2a, 0x11, 0xac, 0x17, 0x92, 0x9f, + 0xf1, 0xc0, 0x73, 0xa5, 0x1c, 0xc0, 0x27, 0xaa, + 0xed, 0xef, 0xf8, 0x5a, 0xad, 0x7c, 0x2b, 0x7c, + 0x5a, 0x80, 0x3e, 0x24, 0x04, 0xd9, 0x6d, 0x2a, + 0x77, 0x35, 0x7b, 0xda, 0x1a, 0x6d, 0xae, 0xed, + 0x17, 0x15, 0x1c, 0xb9, 0xbc, 0x51, 0x25, 0xa4, + 0x22, 0xe9, 0x41, 0xde, 0x0c, 0xa0, 0xfc, 0x50, + 0x11, 0xc2, 0x3e, 0xcf, 0xfe, 0xfd, 0xd0, 0x96, + 0x76, 0x71, 0x1c, 0xf3, 0xdb, 0x0a, 0x34, 0x40, + 0x72, 0x0e ,0x16, 0x15, 0xc1, 0xf2, 0x2f, 0xbc, + 0x3c, 0x72, 0x1d, 0xe5, 0x21, 0xe1, 0xb9, 0x9b, + 0xa1, 0xbd, 0x55, 0x77, 0x40, 0x86, 0x42, 0x14, + 0x7e, 0xd0, 0x96 +}; + +static const u_char sha384_long2_msg_digest[] = { + 0x4f, 0x44, 0x0d, 0xb1, 0xe6, 0xed, 0xd2, 0x89, + 0x9f, 0xa3, 0x35, 0xf0, 0x95, 0x15, 0xaa, 0x02, + 0x5e, 0xe1, 0x77, 0xa7, 0x9f, 0x4b, 0x4a, 0xaf, + 0x38, 0xe4, 0x2b, 0x5c, 0x4d, 0xe6, 0x60, 0xf5, + 0xde, 0x8f, 0xb2, 0xa5, 0xb2, 0xfb, 0xd2, 0xa3, + 0xcb, 0xff, 0xd2, 0x0c, 0xff, 0x12, 0x88, 0xc0 +}; + +static const hash_testvector_t sha384_hash_testvectors[] = { + { sizeof(sha384_short2_msg), sha384_short2_msg, sha384_short2_msg_digest }, + { sizeof(sha384_short4_msg), sha384_short4_msg, sha384_short4_msg_digest }, + { sizeof(sha384_long2_msg), sha384_long2_msg, sha384_long2_msg_digest }, + { 0, NULL, NULL } +}; + +/* SHA-512 hash test vectors + * from "The Secure Hash Algorithm Validation System (SHAVS)" + * July 22, 2004, Lawrence E. Bassham III, NIST + */ + +static const u_char sha512_short2_msg[] = { + 0xd0 +}; + +static const u_char sha512_short2_msg_digest[] = { + 0x99, 0x92, 0x20, 0x29, 0x38, 0xe8, 0x82, 0xe7, + 0x3e, 0x20, 0xf6, 0xb6, 0x9e, 0x68, 0xa0, 0xa7, + 0x14, 0x90, 0x90, 0x42, 0x3d, 0x93, 0xc8, 0x1b, + 0xab, 0x3f, 0x21, 0x67, 0x8d, 0x4a, 0xce, 0xee, + 0xe5, 0x0e, 0x4e, 0x8c, 0xaf, 0xad, 0xa4, 0xc8, + 0x5a, 0x54, 0xea, 0x83, 0x06, 0x82, 0x6c, 0x4a, + 0xd6, 0xe7, 0x4c, 0xec, 0xe9, 0x63, 0x1b, 0xfa, + 0x8a, 0x54, 0x9b, 0x4a, 0xb3, 0xfb, 0xba, 0x15 +}; + +static const u_char sha512_short4_msg[] = { + 0x8d, 0x4e, 0x3c, 0x0e, 0x38, 0x89, 0x19, 0x14, + 0x91, 0x81, 0x6e, 0x9d, 0x98, 0xbf, 0xf0, 0xa0 +}; + +static const u_char sha512_short4_msg_digest[] = { + 0xcb, 0x0b, 0x67, 0xa4, 0xb8, 0x71, 0x2c, 0xd7, + 0x3c, 0x9a, 0xab, 0xc0, 0xb1, 0x99, 0xe9, 0x26, + 0x9b, 0x20, 0x84, 0x4a, 0xfb, 0x75, 0xac, 0xbd, + 0xd1, 0xc1, 0x53, 0xc9, 0x82, 0x89, 0x24, 0xc3, + 0xdd, 0xed, 0xaa, 0xfe, 0x66, 0x9c, 0x5f, 0xdd, + 0x0b, 0xc6, 0x6f, 0x63, 0x0f, 0x67, 0x73, 0x98, + 0x82, 0x13, 0xeb, 0x1b, 0x16, 0xf5, 0x17, 0xad, + 0x0d, 0xe4, 0xb2, 0xf0, 0xc9, 0x5c, 0x90, 0xf8 +}; + +static const u_char sha512_long2_msg[] = { + 0xa5, 0x5f, 0x20, 0xc4, 0x11, 0xaa, 0xd1, 0x32, + 0x80, 0x7a, 0x50, 0x2d, 0x65, 0x82, 0x4e, 0x31, + 0xa2, 0x30, 0x54, 0x32, 0xaa, 0x3d, 0x06, 0xd3, + 0xe2, 0x82, 0xa8, 0xd8, 0x4e, 0x0d, 0xe1, 0xde, + 0x69, 0x74, 0xbf, 0x49, 0x54, 0x69, 0xfc, 0x7f, + 0x33, 0x8f, 0x80, 0x54, 0xd5, 0x8c, 0x26, 0xc4, + 0x93, 0x60, 0xc3, 0xe8, 0x7a, 0xf5, 0x65, 0x23, + 0xac, 0xf6, 0xd8, 0x9d, 0x03, 0xe5, 0x6f, 0xf2, + 0xf8, 0x68, 0x00, 0x2b, 0xc3, 0xe4, 0x31, 0xed, + 0xc4, 0x4d, 0xf2, 0xf0, 0x22, 0x3d, 0x4b, 0xb3, + 0xb2, 0x43, 0x58, 0x6e, 0x1a, 0x7d, 0x92, 0x49, + 0x36, 0x69, 0x4f, 0xcb, 0xba, 0xf8, 0x8d, 0x95, + 0x19, 0xe4, 0xeb, 0x50, 0xa6, 0x44, 0xf8, 0xe4, + 0xf9, 0x5e, 0xb0, 0xea, 0x95, 0xbc, 0x44, 0x65, + 0xc8, 0x82, 0x1a, 0xac, 0xd2, 0xfe, 0x15, 0xab, + 0x49, 0x81, 0x16, 0x4b, 0xbb, 0x6d, 0xc3, 0x2f, + 0x96, 0x90, 0x87, 0xa1, 0x45, 0xb0, 0xd9, 0xcc, + 0x9c, 0x67, 0xc2, 0x2b, 0x76, 0x32, 0x99, 0x41, + 0x9c, 0xc4, 0x12, 0x8b, 0xe9, 0xa0, 0x77, 0xb3, + 0xac, 0xe6, 0x34, 0x06, 0x4e, 0x6d, 0x99, 0x28, + 0x35, 0x13, 0xdc, 0x06, 0xe7, 0x51, 0x5d, 0x0d, + 0x73, 0x13, 0x2e, 0x9a, 0x0d, 0xc6, 0xd3, 0xb1, + 0xf8, 0xb2, 0x46, 0xf1, 0xa9, 0x8a, 0x3f, 0xc7, + 0x29, 0x41, 0xb1, 0xe3, 0xbb, 0x20, 0x98, 0xe8, + 0xbf, 0x16, 0xf2, 0x68, 0xd6, 0x4f, 0x0b, 0x0f, + 0x47, 0x07, 0xfe, 0x1e, 0xa1, 0xa1, 0x79, 0x1b, + 0xa2, 0xf3, 0xc0, 0xc7, 0x58, 0xe5, 0xf5, 0x51, + 0x86, 0x3a, 0x96, 0xc9, 0x49, 0xad, 0x47, 0xd7, + 0xfb, 0x40, 0xd2 +}; + +static const u_char sha512_long2_msg_digest[] = { + 0xc6, 0x65, 0xbe, 0xfb, 0x36, 0xda, 0x18, 0x9d, + 0x78, 0x82, 0x2d, 0x10, 0x52, 0x8c, 0xbf, 0x3b, + 0x12, 0xb3, 0xee, 0xf7, 0x26, 0x03, 0x99, 0x09, + 0xc1, 0xa1, 0x6a, 0x27, 0x0d, 0x48, 0x71, 0x93, + 0x77, 0x96, 0x6b, 0x95, 0x7a, 0x87, 0x8e, 0x72, + 0x05, 0x84, 0x77, 0x9a, 0x62, 0x82, 0x5c, 0x18, + 0xda, 0x26, 0x41, 0x5e, 0x49, 0xa7, 0x17, 0x6a, + 0x89, 0x4e, 0x75, 0x10, 0xfd, 0x14, 0x51, 0xf5 +}; + +static const hash_testvector_t sha512_hash_testvectors[] = { + { sizeof(sha512_short2_msg), sha512_short2_msg, sha512_short2_msg_digest }, + { sizeof(sha512_short4_msg), sha512_short4_msg, sha512_short4_msg_digest }, + { sizeof(sha512_long2_msg), sha512_long2_msg, sha512_long2_msg_digest }, + { 0, NULL, NULL } +}; + +/* SHA-256, SHA-384, and SHA-512 hmac test vectors + * from RFC 4231 "Identifiers and Test Vectors for HMAC-SHA-224, + * HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512" + * December 2005, M. Nystrom, RSA Security + */ + +static const u_char sha2_hmac1_key[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; + +static const u_char sha2_hmac1_msg[] = { + 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 +}; + +static const u_char sha2_hmac1_256[] = { + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, + 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, + 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, + 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7 +}; + +static const u_char sha2_hmac1_384[] = { + 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, + 0x6b, 0x08, 0x25, 0xf4, 0xab ,0x46, 0x90, 0x7f, + 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6, + 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, + 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f, + 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6 +}; + +static const u_char sha2_hmac1_512[] = { + 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, + 0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0, + 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78, + 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde, + 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7, 0x02, + 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4, + 0xbe, 0x9d, 0x91, 0x4e, 0xeb, 0x61, 0xf1, 0x70, + 0x2e, 0x69, 0x6c, 0x20, 0x3a, 0x12, 0x68, 0x54 +}; + +static const u_char sha2_hmac2_key[] = { + 0x4a, 0x65, 0x66, 0x65 +}; + +static const u_char sha2_hmac2_msg[] = { + 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, + 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x3f +}; + +static const u_char sha2_hmac2_256[] = { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, + 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, + 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, + 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 +}; + +static const u_char sha2_hmac2_384[] = { + 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, + 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b, + 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47, + 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, + 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7, + 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49 +}; + +static const u_char sha2_hmac2_512[] = { + 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, + 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3, + 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, + 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54, + 0x97, 0x58, 0xbf, 0x75, 0xc0, 0x5a, 0x99, 0x4a, + 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd, + 0xca, 0xea, 0xb1, 0xa3, 0x4d, 0x4a, 0x6b, 0x4b, + 0x63, 0x6e, 0x07, 0x0a, 0x38, 0xbc, 0xe7, 0x37 +}; + +static const u_char sha2_hmac3_key[] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa +}; + +static const u_char sha2_hmac3_msg[] = { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd +}; + +static const u_char sha2_hmac3_256[] = { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, + 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, + 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, + 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe +}; + +static const u_char sha2_hmac3_384[] = { + 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, + 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f, + 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, + 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, + 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9, + 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27 +}; + +static const u_char sha2_hmac3_512[] = { + 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, + 0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9, + 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, + 0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, + 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22, 0xc8, + 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, + 0xb9, 0x46, 0xa3, 0x37, 0xbe, 0xe8, 0x94, 0x26, + 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb +}; + +static const u_char sha2_hmac4_key[] = { + 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 +}; + +static const u_char sha2_hmac4_msg[] = { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd +}; + +static const u_char sha2_hmac4_256[] = { + 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, + 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, + 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, + 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b +}; + +static const u_char sha2_hmac4_384[] = { + 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, + 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7, + 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c, + 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, + 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79, + 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb +}; + +static const u_char sha2_hmac4_512[] = { + 0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, + 0x90, 0xe5, 0xa8, 0xc5, 0xf6, 0x1d, 0x4a, 0xf7, + 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d, + 0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb, + 0xa9, 0x1c, 0xa5, 0xc1, 0x1a, 0xa2, 0x5e, 0xb4, + 0xd6, 0x79, 0x27, 0x5c, 0xc5, 0x78, 0x80, 0x63, + 0xa5, 0xf1, 0x97, 0x41, 0x12, 0x0c, 0x4f, 0x2d, + 0xe2, 0xad, 0xeb, 0xeb, 0x10, 0xa2, 0x98, 0xdd +}; + +static const u_char sha2_hmac6_key[] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa +}; + +static const u_char sha2_hmac6_msg[] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, + 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, + 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, + 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x2d, 0x20, + 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79, + 0x20, 0x46, 0x69, 0x72, 0x73, 0x74 +}; + +static const u_char sha2_hmac6_256[] = { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, + 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, + 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 +}; + +static const u_char sha2_hmac6_384[] = { + 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, + 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4, + 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, + 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, + 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82, + 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52 +}; + +static const u_char sha2_hmac6_512[] = { + 0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, + 0xb7, 0x14, 0x93, 0xc1, 0xdd, 0x7b, 0xe8, 0xb4, + 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, + 0x12, 0x1b, 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, + 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25, 0x98, + 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, + 0x95, 0xe6, 0x4f, 0x73, 0xf6, 0x3f, 0x0a, 0xec, + 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98 +}; + +static const u_char sha2_hmac7_msg[] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, + 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6c, + 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, + 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6b, 0x65, + 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, + 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6e, 0x65, 0x65, + 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, + 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x20, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62, + 0x65, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, + 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e +}; + +static const u_char sha2_hmac7_256[] = { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, + 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, + 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, + 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2 +}; + +static const u_char sha2_hmac7_384[] = { + 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, + 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, + 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, + 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, + 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, + 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e +}; + +static const u_char sha2_hmac7_512[] = { + 0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, + 0xa4, 0xdf, 0xa9, 0xf9, 0x6e, 0x5e, 0x3f, 0xfd, + 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, + 0x5d, 0xf5, 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, + 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82, 0xb1, + 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, + 0x13, 0x46, 0x76, 0xfb, 0x6d, 0xe0, 0x44, 0x60, + 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58 +}; + +static const hmac_testvector_t sha256_hmac_testvectors[] = { + { sizeof(sha2_hmac1_key), sha2_hmac1_key, sizeof(sha2_hmac1_msg), sha2_hmac1_msg, sha2_hmac1_256 }, + { sizeof(sha2_hmac2_key), sha2_hmac2_key, sizeof(sha2_hmac2_msg), sha2_hmac2_msg, sha2_hmac2_256 }, + { sizeof(sha2_hmac3_key), sha2_hmac3_key, sizeof(sha2_hmac3_msg), sha2_hmac3_msg, sha2_hmac3_256 }, + { sizeof(sha2_hmac4_key), sha2_hmac4_key, sizeof(sha2_hmac4_msg), sha2_hmac4_msg, sha2_hmac4_256 }, + { sizeof(sha2_hmac6_key), sha2_hmac6_key, sizeof(sha2_hmac6_msg), sha2_hmac6_msg, sha2_hmac6_256 }, + { sizeof(sha2_hmac6_key), sha2_hmac6_key, sizeof(sha2_hmac7_msg), sha2_hmac7_msg, sha2_hmac7_256 }, + { 0, NULL, 0, NULL, NULL } +}; + +static const hmac_testvector_t sha384_hmac_testvectors[] = { + { sizeof(sha2_hmac1_key), sha2_hmac1_key, sizeof(sha2_hmac1_msg), sha2_hmac1_msg, sha2_hmac1_384 }, + { sizeof(sha2_hmac2_key), sha2_hmac2_key, sizeof(sha2_hmac2_msg), sha2_hmac2_msg, sha2_hmac2_384 }, + { sizeof(sha2_hmac3_key), sha2_hmac3_key, sizeof(sha2_hmac3_msg), sha2_hmac3_msg, sha2_hmac3_384 }, + { sizeof(sha2_hmac4_key), sha2_hmac4_key, sizeof(sha2_hmac4_msg), sha2_hmac4_msg, sha2_hmac4_384 }, + { sizeof(sha2_hmac6_key), sha2_hmac6_key, sizeof(sha2_hmac6_msg), sha2_hmac6_msg, sha2_hmac6_384 }, + { sizeof(sha2_hmac6_key), sha2_hmac6_key, sizeof(sha2_hmac7_msg), sha2_hmac7_msg, sha2_hmac7_384 }, + { 0, NULL, 0, NULL, NULL } +}; + +static const hmac_testvector_t sha512_hmac_testvectors[] = { + { sizeof(sha2_hmac1_key), sha2_hmac1_key, sizeof(sha2_hmac1_msg), sha2_hmac1_msg, sha2_hmac1_512 }, + { sizeof(sha2_hmac2_key), sha2_hmac2_key, sizeof(sha2_hmac2_msg), sha2_hmac2_msg, sha2_hmac2_512 }, + { sizeof(sha2_hmac3_key), sha2_hmac3_key, sizeof(sha2_hmac3_msg), sha2_hmac3_msg, sha2_hmac3_512 }, + { sizeof(sha2_hmac4_key), sha2_hmac4_key, sizeof(sha2_hmac4_msg), sha2_hmac4_msg, sha2_hmac4_512 }, + { sizeof(sha2_hmac6_key), sha2_hmac6_key, sizeof(sha2_hmac6_msg), sha2_hmac6_msg, sha2_hmac6_512 }, + { sizeof(sha2_hmac6_key), sha2_hmac6_key, sizeof(sha2_hmac7_msg), sha2_hmac7_msg, sha2_hmac7_512 }, + { 0, NULL, 0, NULL, NULL } +}; + +struct hash_desc hash_desc_sha2_256 = { + algo_type: IKE_ALG_HASH, + algo_id: OAKLEY_SHA2_256, + algo_next: NULL, + hash_ctx_size: sizeof(sha256_context), + hash_block_size: SHA2_256_BLOCK_SIZE, + hash_digest_size: SHA2_256_DIGEST_SIZE, + hash_testvectors: sha256_hash_testvectors, + hmac_testvectors: sha256_hmac_testvectors, + hash_init: (void (*)(void *))sha256_init, + hash_update: (void (*)(void *, const u_char *, size_t ))sha256_write, + hash_final:(void (*)(u_char *, void *))sha256_hash_final +}; + +struct hash_desc hash_desc_sha2_384 = { + algo_type: IKE_ALG_HASH, + algo_id: OAKLEY_SHA2_384, + algo_next: NULL, + hash_ctx_size: sizeof(sha512_context), + hash_block_size: SHA2_384_BLOCK_SIZE, + hash_digest_size: SHA2_384_DIGEST_SIZE, + hash_testvectors: sha384_hash_testvectors, + hmac_testvectors: sha384_hmac_testvectors, + hash_init: (void (*)(void *))sha384_init, + hash_update: (void (*)(void *, const u_char *, size_t ))sha512_write, + hash_final:(void (*)(u_char *, void *))sha384_hash_final +}; + +struct hash_desc hash_desc_sha2_512 = { + algo_type: IKE_ALG_HASH, + algo_id: OAKLEY_SHA2_512, + algo_next: NULL, + hash_ctx_size: sizeof(sha512_context), + hash_block_size: SHA2_512_BLOCK_SIZE, + hash_digest_size: SHA2_512_DIGEST_SIZE, + hash_testvectors: sha512_hash_testvectors, + hmac_testvectors: sha512_hmac_testvectors, + hash_init: (void (*)(void *))sha512_init, + hash_update: (void (*)(void *, const u_char *, size_t ))sha512_write, + hash_final:(void (*)(u_char *, void *))sha512_hash_final +}; + +int ike_alg_sha2_init(void); + +int +ike_alg_sha2_init(void) +{ + int ret +; + ret = ike_alg_register_hash(&hash_desc_sha2_256); + if (ret) + goto out; + ret = ike_alg_register_hash(&hash_desc_sha2_384); + if (ret) + goto out; + ret = ike_alg_register_hash(&hash_desc_sha2_512); + +out: + return ret; +} + +/* +IKE_ALG_INIT_NAME: ike_alg_sha2_init +*/ diff --git a/src/pluto/alg/ike_alg_twofish.c b/src/pluto/alg/ike_alg_twofish.c new file mode 100644 index 000000000..1788bc394 --- /dev/null +++ b/src/pluto/alg/ike_alg_twofish.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "libtwofish/twofish_cbc.h" +#include "alg_info.h" +#include "ike_alg.h" + +#define TWOFISH_CBC_BLOCK_SIZE (128/BITS_PER_BYTE) +#define TWOFISH_KEY_MIN_LEN 128 +#define TWOFISH_KEY_DEF_LEN 128 +#define TWOFISH_KEY_MAX_LEN 256 + +static void +do_twofish(u_int8_t *buf, size_t buf_size, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc) +{ + twofish_context twofish_ctx; + char iv_bak[TWOFISH_CBC_BLOCK_SIZE]; + char *new_iv = NULL; /* logic will avoid copy to NULL */ + + twofish_set_key(&twofish_ctx, key, key_size); + /* + * my TWOFISH cbc does not touch passed IV (optimization for + * ESP handling), so I must "emulate" des-like IV + * crunching + */ + if (!enc) + memcpy(new_iv=iv_bak, + (char*) buf + buf_size-TWOFISH_CBC_BLOCK_SIZE, + TWOFISH_CBC_BLOCK_SIZE); + + twofish_cbc_encrypt(&twofish_ctx, buf, buf, buf_size, iv, enc); + + if (enc) + new_iv = (char*) buf + buf_size-TWOFISH_CBC_BLOCK_SIZE; + + memcpy(iv, new_iv, TWOFISH_CBC_BLOCK_SIZE); +} + +struct encrypt_desc encrypt_desc_twofish = +{ + algo_type: IKE_ALG_ENCRYPT, + algo_id: OAKLEY_TWOFISH_CBC, + algo_next: NULL, + enc_ctxsize: sizeof(twofish_context), + enc_blocksize: TWOFISH_CBC_BLOCK_SIZE, + keydeflen: TWOFISH_KEY_MIN_LEN, + keyminlen: TWOFISH_KEY_DEF_LEN, + keymaxlen: TWOFISH_KEY_MAX_LEN, + do_crypt: do_twofish, +}; + +struct encrypt_desc encrypt_desc_twofish_ssh = +{ + algo_type: IKE_ALG_ENCRYPT, + algo_id: OAKLEY_TWOFISH_CBC_SSH, + algo_next: NULL, + enc_ctxsize: sizeof(twofish_context), + enc_blocksize: TWOFISH_CBC_BLOCK_SIZE, + keydeflen: TWOFISH_KEY_MIN_LEN, + keyminlen: TWOFISH_KEY_DEF_LEN, + keymaxlen: TWOFISH_KEY_MAX_LEN, + do_crypt: do_twofish, +}; + +int ike_alg_twofish_init(void); + +int +ike_alg_twofish_init(void) +{ + int ret = ike_alg_register_enc(&encrypt_desc_twofish); + + if (ike_alg_register_enc(&encrypt_desc_twofish_ssh) < 0) + plog("ike_alg_twofish_init(): Experimental OAKLEY_TWOFISH_CBC_SSH activation failed"); + + return ret; +} +/* +IKE_ALG_INIT_NAME: ike_alg_twofish_init +*/ diff --git a/src/pluto/alg/ike_alginit.c b/src/pluto/alg/ike_alginit.c new file mode 100644 index 000000000..8784bf31b --- /dev/null +++ b/src/pluto/alg/ike_alginit.c @@ -0,0 +1,7 @@ +extern int ike_alg_init(void); int ike_alg_init(void) { +{ extern int ike_alg_aes_init (void); ike_alg_aes_init();} +{ extern int ike_alg_blowfish_init (void); ike_alg_blowfish_init();} +{ extern int ike_alg_serpent_init (void); ike_alg_serpent_init();} +{ extern int ike_alg_sha2_init (void); ike_alg_sha2_init();} +{ extern int ike_alg_twofish_init (void); ike_alg_twofish_init();} +return 0;} diff --git a/src/pluto/alg_info.c b/src/pluto/alg_info.c new file mode 100644 index 000000000..ac5d1672f --- /dev/null +++ b/src/pluto/alg_info.c @@ -0,0 +1,1205 @@ +/* + * Algorithm info parsing and creation functions + * Author: JuanJo Ciarlante + * + * $Id: alg_info.c,v 1.5 2004/09/29 22:42:49 as Exp $ + * + * This program 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. See . + * + * 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 General Public License + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "alg_info.h" +#include "constants.h" +#ifndef NO_PLUTO +#include "defs.h" +#include "log.h" +#include "whack.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" +#include "kernel_alg.h" +#include "ike_alg.h" +#else +/* + * macros/functions for compilation without pluto (eg: spi for manual conns) + */ +#include +#define passert(x) assert(x) +extern int debug; /* eg: spi.c */ +#define DBG(cond, action) { if (debug) { action ; } } +#define DBG_log(x, args...) fprintf(stderr, x "\n" , ##args); +#define RC_LOG_SERIOUS +#define loglog(x, args...) fprintf(stderr, ##args); +#define alloc_thing(thing, name) alloc_bytes(sizeof (thing), name) +void * alloc_bytes(size_t size, const char *name) { + void *p=malloc(size); + if (p == NULL) + fprintf(stderr, "unable to malloc %lu bytes for %s", + (unsigned long) size, name); + memset(p, '\0', size); + return p; +} +#define pfreeany(ptr) free(ptr) +#endif /* NO_PLUTO */ + +/* + * sadb/ESP aa attrib converters + */ +int +alg_info_esp_aa2sadb(int auth) +{ + int sadb_aalg = 0; + + switch(auth) { + case AUTH_ALGORITHM_HMAC_MD5: + case AUTH_ALGORITHM_HMAC_SHA1: + sadb_aalg = auth + 1; + break; + case AUTH_ALGORITHM_HMAC_SHA2_256: + case AUTH_ALGORITHM_HMAC_SHA2_384: + case AUTH_ALGORITHM_HMAC_SHA2_512: + case AUTH_ALGORITHM_HMAC_RIPEMD: + sadb_aalg = auth; + break; + default: + /* loose ... */ + sadb_aalg = auth; + } + return sadb_aalg; +} + +int /* __attribute__ ((unused)) */ +alg_info_esp_sadb2aa(int sadb_aalg) +{ + int auth = 0; + + switch(sadb_aalg) { + case SADB_AALG_MD5_HMAC: + case SADB_AALG_SHA1_HMAC: + auth = sadb_aalg - 1; + break; + /* since they are the same ... :) */ + case AUTH_ALGORITHM_HMAC_SHA2_256: + case AUTH_ALGORITHM_HMAC_SHA2_384: + case AUTH_ALGORITHM_HMAC_SHA2_512: + case AUTH_ALGORITHM_HMAC_RIPEMD: + auth = sadb_aalg; + break; + default: + /* loose ... */ + auth = sadb_aalg; + } + return auth; +} + +/* + * Search enum_name array with in prefixed uppercase + */ +static int +enum_search_prefix (enum_names *ed, const char *prefix, const char *str, int strlen) +{ + char buf[64]; + char *ptr; + int ret; + int len = sizeof(buf) - 1; /* reserve space for final \0 */ + + for (ptr = buf; *prefix; *ptr++ = *prefix++, len--); + while (strlen-- && len-- && *str) *ptr++ = toupper(*str++); + *ptr = 0; + + DBG(DBG_CRYPT, + DBG_log("enum_search_prefix () calling enum_search(%p, \"%s\")" + , ed, buf) + ) + ret = enum_search(ed, buf); + return ret; +} + +/* + * Search enum_name array with in prefixed and postfixed uppercase + */ +static int +enum_search_ppfix (enum_names *ed, const char *prefix, const char *postfix, const char *str, int strlen) +{ + char buf[64]; + char *ptr; + int ret; + int len = sizeof(buf) - 1; /* reserve space for final \0 */ + + for (ptr = buf; *prefix; *ptr++ = *prefix++, len--); + while (strlen-- && len-- && *str) *ptr++ = toupper(*str++); + while (len-- && *postfix) *ptr++ = *postfix++; + *ptr = 0; + + DBG(DBG_CRYPT, + DBG_log("enum_search_ppfixi () calling enum_search(%p, \"%s\")" + , ed, buf) + ) + ret = enum_search(ed, buf); + return ret; +} + +/* + * Search esp_transformid_names for a match, eg: + * "3des" <=> "ESP_3DES" + */ +#define ESP_MAGIC_ID 0x00ffff01 + +static int +ealg_getbyname_esp(const char *const str, int len) +{ + if (!str || !*str) + return -1; + + /* leave special case for eg: "id248" string */ + if (strcmp("id", str) == 0) + return ESP_MAGIC_ID; + + return enum_search_prefix(&esp_transformid_names, "ESP_", str, len); +} + +/* + * Search auth_alg_names for a match, eg: + * "md5" <=> "AUTH_ALGORITHM_HMAC_MD5" + */ +static int +aalg_getbyname_esp(const char *const str, int len) +{ + int ret; + unsigned num; + + if (!str || !*str) + return -1; + + /* interpret 'SHA' as 'SHA1' */ + if (strncasecmp("SHA", str, len) == 0) + return enum_search(&auth_alg_names, "AUTH_ALGORITHM_HMAC_SHA1"); + + ret = enum_search_prefix(&auth_alg_names,"AUTH_ALGORITHM_HMAC_", str ,len); + if (ret >= 0) + return ret; + + ret = enum_search_prefix(&auth_alg_names,"AUTH_ALGORITHM_", str, len); + if (ret >= 0) + return ret; + + sscanf(str, "id%d%n", &ret, &num); + return (ret >= 0 && num != strlen(str))? -1 : ret; +} + +static int +modp_getbyname_esp(const char *const str, int len) +{ + int ret; + + if (!str || !*str) + return -1; + + ret = enum_search_prefix(&oakley_group_names,"OAKLEY_GROUP_", str, len); + if (ret >= 0) + return ret; + + ret = enum_search_ppfix(&oakley_group_names, "OAKLEY_GROUP_", " (extension)", str, len); + return ret; +} + +void +alg_info_free(struct alg_info *alg_info) +{ + pfreeany(alg_info); +} + +/* + * Raw add routine: only checks for no duplicates + */ +static void +__alg_info_esp_add (struct alg_info_esp *alg_info, int ealg_id, unsigned ek_bits, int aalg_id, unsigned ak_bits) +{ + struct esp_info *esp_info=alg_info->esp; + unsigned cnt = alg_info->alg_info_cnt, i; + + /* check for overflows */ + passert(cnt < elemsof(alg_info->esp)); + + /* dont add duplicates */ + for (i = 0; i < cnt; i++) + { + if (esp_info[i].esp_ealg_id == ealg_id + && (!ek_bits || esp_info[i].esp_ealg_keylen == ek_bits) + && esp_info[i].esp_aalg_id == aalg_id + && (!ak_bits || esp_info[i].esp_aalg_keylen == ak_bits)) + return; + } + + esp_info[cnt].esp_ealg_id = ealg_id; + esp_info[cnt].esp_ealg_keylen = ek_bits; + esp_info[cnt].esp_aalg_id = aalg_id; + esp_info[cnt].esp_aalg_keylen = ak_bits; + + /* sadb values */ + esp_info[cnt].encryptalg = ealg_id; + esp_info[cnt].authalg = alg_info_esp_aa2sadb(aalg_id); + alg_info->alg_info_cnt++; + + DBG(DBG_CRYPT, + DBG_log("__alg_info_esp_add() ealg=%d aalg=%d cnt=%d" + , ealg_id, aalg_id, alg_info->alg_info_cnt) + ) +} + +/* + * Add ESP alg info _with_ logic (policy): + */ +static void +alg_info_esp_add (struct alg_info *alg_info, int ealg_id, int ek_bits, int aalg_id, int ak_bits) +{ + /* Policy: default to 3DES */ + if (ealg_id == 0) + ealg_id = ESP_3DES; + + if (ealg_id > 0) + { +#ifndef NO_PLUTO + if (aalg_id > 0) +#else + /* Allow no auth for manual conns (from spi.c) */ + if (aalg_id >= 0) +#endif + __alg_info_esp_add((struct alg_info_esp *)alg_info, + ealg_id, ek_bits, + aalg_id, ak_bits); + else + { + /* Policy: default to MD5 and SHA1 */ + __alg_info_esp_add((struct alg_info_esp *)alg_info, + ealg_id, ek_bits, + AUTH_ALGORITHM_HMAC_MD5, ak_bits); + __alg_info_esp_add((struct alg_info_esp *)alg_info, + ealg_id, ek_bits, + AUTH_ALGORITHM_HMAC_SHA1, ak_bits); + } + } +} + +#ifndef NO_PLUTO +/************************************** + * + * IKE alg + * + *************************************/ +/* + * Search oakley_enc_names for a match, eg: + * "3des_cbc" <=> "OAKLEY_3DES_CBC" + */ +static int +ealg_getbyname_ike(const char *const str, int len) +{ + int ret; + + if (!str || !*str) + return -1; + + ret = enum_search_prefix(&oakley_enc_names,"OAKLEY_", str, len); + if (ret >= 0) + return ret; + + ret = enum_search_ppfix(&oakley_enc_names, "OAKLEY_", "_CBC", str, len); + return ret; +} + +/* + * Search oakley_hash_names for a match, eg: + * "md5" <=> "OAKLEY_MD5" + */ +static int +aalg_getbyname_ike(const char *const str, int len) +{ + int ret; + unsigned num; + + if (!str || !*str) + return -1; + + /* interpret 'SHA1' as 'SHA' */ + if (strncasecmp("SHA1", str, len) == 0) + return enum_search(&oakley_hash_names, "OAKLEY_SHA"); + + ret = enum_search_prefix(&oakley_hash_names,"OAKLEY_", str, len); + if (ret >= 0) + return ret; + + sscanf(str, "id%d%n", &ret, &num); + return (ret >=0 && num != strlen(str))? -1 : ret; +} + +/* + * Search oakley_group_names for a match, eg: + * "modp1024" <=> "OAKLEY_GROUP_MODP1024" + */ +static int +modp_getbyname_ike(const char *const str, int len) +{ + int ret; + + if (!str || !*str) + return -1; + + ret = enum_search_prefix(&oakley_group_names,"OAKLEY_GROUP_", str, len); + if (ret >= 0) + return ret; + + ret = enum_search_ppfix(&oakley_group_names, "OAKLEY_GROUP_", " (extension)", str, len); + return ret; +} + +static void +__alg_info_ike_add (struct alg_info_ike *alg_info, int ealg_id, unsigned ek_bits, int aalg_id, unsigned ak_bits, int modp_id) +{ + struct ike_info *ike_info = alg_info->ike; + unsigned cnt = alg_info->alg_info_cnt; + unsigned i; + + /* check for overflows */ + passert(cnt < elemsof(alg_info->ike)); + + /* dont add duplicates */ + for (i = 0;i < cnt; i++) + { + if (ike_info[i].ike_ealg == ealg_id + && (!ek_bits || ike_info[i].ike_eklen == ek_bits) + && ike_info[i].ike_halg == aalg_id + && (!ak_bits || ike_info[i].ike_hklen == ak_bits) + && ike_info[i].ike_modp==modp_id) + return; + } + + ike_info[cnt].ike_ealg = ealg_id; + ike_info[cnt].ike_eklen = ek_bits; + ike_info[cnt].ike_halg = aalg_id; + ike_info[cnt].ike_hklen = ak_bits; + ike_info[cnt].ike_modp = modp_id; + alg_info->alg_info_cnt++; + + DBG(DBG_CRYPT, + DBG_log("__alg_info_ike_add() ealg=%d aalg=%d modp_id=%d, cnt=%d" + , ealg_id, aalg_id, modp_id + , alg_info->alg_info_cnt) + ) +} + +/* + * Proposals will be built by looping over default_ike_groups array and + * merging alg_info (ike_info) contents + */ + +static int default_ike_groups[] = { + OAKLEY_GROUP_MODP1536, + OAKLEY_GROUP_MODP1024 +}; + +/* + * Add IKE alg info _with_ logic (policy): + */ +static void +alg_info_ike_add (struct alg_info *alg_info, int ealg_id, int ek_bits, int aalg_id, int ak_bits, int modp_id) +{ + int i = 0; + int n_groups = elemsof(default_ike_groups); + + /* if specified modp_id avoid loop over default_ike_groups */ + if (modp_id) + { + n_groups=0; + goto in_loop; + } + + for (; n_groups--; i++) + { + modp_id = default_ike_groups[i]; +in_loop: + /* Policy: default to 3DES */ + if (ealg_id == 0) + ealg_id = OAKLEY_3DES_CBC; + + if (ealg_id > 0) + { + if (aalg_id > 0) + __alg_info_ike_add((struct alg_info_ike *)alg_info, + ealg_id, ek_bits, + aalg_id, ak_bits, + modp_id); + else + { + /* Policy: default to MD5 and SHA */ + __alg_info_ike_add((struct alg_info_ike *)alg_info, + ealg_id, ek_bits, + OAKLEY_MD5, ak_bits, + modp_id); + __alg_info_ike_add((struct alg_info_ike *)alg_info, + ealg_id, ek_bits, + OAKLEY_SHA, ak_bits, + modp_id); + } + } + } +} +#endif /* NO_PLUTO */ + +/* + * Creates a new alg_info by parsing passed string + */ +enum parser_state_esp { + ST_INI, + ST_EA, /* encrypt algo */ + ST_EA_END, + ST_EK, /* enc. key length */ + ST_EK_END, + ST_AA, /* auth algo */ + ST_AA_END, + ST_AK, /* auth. key length */ + ST_AK_END, + ST_MODP, /* modp spec */ + ST_FLAG_STRICT, + ST_END, + ST_EOF, + ST_ERR +}; + +static const char *parser_state_esp_names[] = { + "ST_INI", + "ST_EA", + "ST_EA_END", + "ST_EK", + "ST_EK_END", + "ST_AA", + "ST_AA_END", + "ST_AK", + "ST_AK_END", + "ST_MOPD", + "ST_FLAG_STRICT", + "ST_END", + "ST_EOF", + "ST_ERR" +}; + +static const char* +parser_state_name_esp(enum parser_state_esp state) +{ + return parser_state_esp_names[state]; +} + +/* XXX:jjo to implement different parser for ESP and IKE */ +struct parser_context { + unsigned state, old_state; + unsigned protoid; + char ealg_buf[16]; + char aalg_buf[16]; + char modp_buf[16]; + int (*ealg_getbyname)(const char *const str, int len); + int (*aalg_getbyname)(const char *const str, int len); + int (*modp_getbyname)(const char *const str, int len); + char *ealg_str; + char *aalg_str; + char *modp_str; + int eklen; + int aklen; + int ch; + const char *err; +}; + +static inline void +parser_set_state(struct parser_context *p_ctx, enum parser_state_esp state) +{ + if (state != p_ctx->state) + { + p_ctx->old_state = p_ctx->state; + p_ctx->state = state; + } +} + +static int +parser_machine(struct parser_context *p_ctx) +{ + int ch = p_ctx->ch; + + /* special 'absolute' cases */ + p_ctx->err = "No error."; + + /* chars that end algo strings */ + switch (ch){ + case 0: /* end-of-string */ + case '!': /* flag as strict algo list */ + case ',': /* algo string separator */ + switch (p_ctx->state) { + case ST_EA: + case ST_EK: + case ST_AA: + case ST_AK: + case ST_MODP: + case ST_FLAG_STRICT: + { + enum parser_state_esp next_state = 0; + + switch (ch) { + case 0: + next_state = ST_EOF; + break; + case ',': + next_state = ST_END; + break; + case '!': + next_state = ST_FLAG_STRICT; + break; + } + /* ch? parser_set_state(p_ctx, ST_END) : parser_set_state(p_ctx, ST_EOF) ; */ + parser_set_state(p_ctx, next_state); + goto out; + } + default: + p_ctx->err = "String ended with invalid char"; + goto err; + } + } +re_eval: + switch (p_ctx->state) { + case ST_INI: + if (isspace(ch)) + break; + if (isalnum(ch)) + { + *(p_ctx->ealg_str++) = ch; + parser_set_state(p_ctx, ST_EA); + break; + } + p_ctx->err = "No alphanum. char initially found"; + goto err; + case ST_EA: + if (isalpha(ch) || ch == '_') + { + *(p_ctx->ealg_str++) = ch; + break; + } + if (isdigit(ch)) + { + /* bravely switch to enc keylen */ + *(p_ctx->ealg_str) = 0; + parser_set_state(p_ctx, ST_EK); + goto re_eval; + } + if (ch == '-') + { + *(p_ctx->ealg_str) = 0; + parser_set_state(p_ctx, ST_EA_END); + break; + } + p_ctx->err = "No valid char found after enc alg string"; + goto err; + case ST_EA_END: + if (isdigit(ch)) + { + /* bravely switch to enc keylen */ + parser_set_state(p_ctx, ST_EK); + goto re_eval; + } + if (isalpha(ch)) + { + parser_set_state(p_ctx, ST_AA); + goto re_eval; + } + p_ctx->err = "No alphanum char found after enc alg separator"; + goto err; + case ST_EK: + if (ch == '-') + { + parser_set_state(p_ctx, ST_EK_END); + break; + } + if (isdigit(ch)) + { + p_ctx->eklen = p_ctx->eklen*10 + ch - '0'; + break; + } + p_ctx->err = "Non digit or valid separator found while reading enc keylen"; + goto err; + case ST_EK_END: + if (isalpha(ch)) + { + parser_set_state(p_ctx, ST_AA); + goto re_eval; + } + p_ctx->err = "Non alpha char found after enc keylen end separator"; + goto err; + case ST_AA: + if (ch == '-') + { + *(p_ctx->aalg_str++) = 0; + parser_set_state(p_ctx, ST_AA_END); + break; + } + if (isalnum(ch) || ch == '_') + { + *(p_ctx->aalg_str++) = ch; + break; + } + p_ctx->err = "Non alphanum or valid separator found in auth string"; + goto err; + case ST_AA_END: + if (isdigit(ch)) + { + parser_set_state(p_ctx, ST_AK); + goto re_eval; + } + /* Only allow modpXXXX string if we have a modp_getbyname method */ + if ((p_ctx->modp_getbyname) && isalpha(ch)) + { + parser_set_state(p_ctx, ST_MODP); + goto re_eval; + } + p_ctx->err = "Non initial digit found for auth keylen"; + goto err; + case ST_AK: + if (ch=='-') + { + parser_set_state(p_ctx, ST_AK_END); + break; + } + if (isdigit(ch)) + { + p_ctx->aklen = p_ctx->aklen*10 + ch - '0'; + break; + } + p_ctx->err = "Non digit found for auth keylen"; + goto err; + case ST_AK_END: + /* Only allow modpXXXX string if we have a modp_getbyname method */ + if ((p_ctx->modp_getbyname) && isalpha(ch)) + { + parser_set_state(p_ctx, ST_MODP); + goto re_eval; + } + p_ctx->err = "Non alpha char found after auth keylen"; + goto err; + case ST_MODP: + if (isalnum(ch)) + { + *(p_ctx->modp_str++) = ch; + break; + } + p_ctx->err = "Non alphanum char found after in modp string"; + goto err; + case ST_FLAG_STRICT: + if (ch == 0) + parser_set_state(p_ctx, ST_END); + p_ctx->err = "Flags character(s) must be at end of whole string"; + goto err; + + /* XXX */ + case ST_END: + case ST_EOF: + case ST_ERR: + break; + /* XXX */ + } +out: + return p_ctx->state; +err: + parser_set_state(p_ctx, ST_ERR); + return ST_ERR; +} + +/* + * Must be called for each "new" char, with new + * character in ctx.ch + */ +static void +parser_init(struct parser_context *p_ctx, unsigned protoid) +{ + memset(p_ctx, 0, sizeof (*p_ctx)); + p_ctx->protoid = protoid; /* XXX: jjo */ + p_ctx->protoid = PROTO_IPSEC_ESP; + p_ctx->ealg_str = p_ctx->ealg_buf; + p_ctx->aalg_str = p_ctx->aalg_buf; + p_ctx->modp_str = p_ctx->modp_buf; + p_ctx->state = ST_INI; + + switch (protoid) { +#ifndef NO_PLUTO + case PROTO_ISAKMP: + p_ctx->ealg_getbyname = ealg_getbyname_ike; + p_ctx->aalg_getbyname = aalg_getbyname_ike; + p_ctx->modp_getbyname = modp_getbyname_ike; + break; +#endif + case PROTO_IPSEC_ESP: + p_ctx->ealg_getbyname = ealg_getbyname_esp; + p_ctx->aalg_getbyname = aalg_getbyname_esp; + break; + } +} + +static int +parser_alg_info_add(struct parser_context *p_ctx, struct alg_info *alg_info) +{ + int ealg_id = 0; + int aalg_id = 0; + int modp_id = 0; +#ifndef NO_PLUTO + const struct oakley_group_desc *gd; +#endif + + if (*p_ctx->ealg_buf) + { + ealg_id = p_ctx->ealg_getbyname(p_ctx->ealg_buf, strlen(p_ctx->ealg_buf)); + if (ealg_id == ESP_MAGIC_ID) + { + ealg_id = p_ctx->eklen; + p_ctx->eklen = 0; + } + if (ealg_id < 0) + { + p_ctx->err = "enc_alg not found"; + return -1; + } + DBG(DBG_CRYPT, + DBG_log("parser_alg_info_add() ealg_getbyname(\"%s\")=%d" + , p_ctx->ealg_buf + , ealg_id) + ) + } + if (*p_ctx->aalg_buf) + { + aalg_id = p_ctx->aalg_getbyname(p_ctx->aalg_buf, strlen(p_ctx->aalg_buf)); + if (aalg_id < 0) + { + p_ctx->err = "hash_alg not found"; + return -1; + } + DBG(DBG_CRYPT, + DBG_log("parser_alg_info_add() aalg_getbyname(\"%s\")=%d" + , p_ctx->aalg_buf + , aalg_id) + ) + } + if (p_ctx->modp_getbyname && *p_ctx->modp_buf) + { + modp_id = p_ctx->modp_getbyname(p_ctx->modp_buf, strlen(p_ctx->modp_buf)); + if (modp_id < 0) + { + p_ctx->err = "modp group not found"; + return -1; + } + DBG(DBG_CRYPT, + DBG_log("parser_alg_info_add() modp_getbyname(\"%s\")=%d" + , p_ctx->modp_buf + , modp_id) + ) + } + switch (alg_info->alg_info_protoid) { + case PROTO_IPSEC_ESP: + alg_info_esp_add(alg_info, + ealg_id, p_ctx->eklen, + aalg_id, p_ctx->aklen); + break; +#ifndef NO_PLUTO + case PROTO_ISAKMP: + if (modp_id && !(gd = lookup_group(modp_id))) + { + p_ctx->err = "found modp group id, but not supported"; + return -1; + } + alg_info_ike_add(alg_info, + ealg_id, p_ctx->eklen, + aalg_id, p_ctx->aklen, + modp_id); + break; +#endif + default: + return -1; + } + return 0; +} + +static int +alg_info_parse_str (struct alg_info *alg_info, const char *alg_str, const char **err_p) +{ + struct parser_context ctx; + int ret; + const char *ptr; + static char err_buf[256]; + + *err_buf = 0; + parser_init(&ctx, alg_info->alg_info_protoid); + if (err_p) + *err_p = NULL; + + /* use default if nul esp string */ + if (!*alg_str) + { + switch (alg_info->alg_info_protoid) { +#ifndef NO_PLUTO + case PROTO_ISAKMP: + alg_info_ike_add(alg_info, 0, 0, 0, 0, 0); + return 0; +#endif + case PROTO_IPSEC_ESP: + alg_info_esp_add(alg_info, 0, 0, 0, 0); + return 0; + default: + /* IMPOSSIBLE */ + passert(alg_info->alg_info_protoid); + } + } + + for (ret = 0, ptr = alg_str; ret < ST_EOF;) + { + ctx.ch = *ptr++; + ret = parser_machine(&ctx); + + switch (ret) { + case ST_FLAG_STRICT: + alg_info->alg_info_flags |= ALG_INFO_F_STRICT; + break; + case ST_END: + case ST_EOF: + DBG(DBG_CRYPT, + DBG_log("alg_info_parse_str() ealg_buf=%s aalg_buf=%s" + "eklen=%d aklen=%d", + ctx.ealg_buf, ctx.aalg_buf, + ctx.eklen, ctx.aklen) + ) + if (parser_alg_info_add(&ctx, alg_info) < 0) + { + snprintf(err_buf, sizeof(err_buf), + "%s, enc_alg=\"%s\", auth_alg=\"%s\", modp=\"%s\"", + ctx.err, + ctx.ealg_buf, + ctx.aalg_buf, + ctx.modp_buf); + goto err; + } + /* zero out for next run (ST_END) */ + parser_init(&ctx, alg_info->alg_info_protoid); + break; + case ST_ERR: + snprintf(err_buf, sizeof(err_buf), + "%s, just after \"%.*s\" (old_state=%s)", + ctx.err, + (int)(ptr-alg_str-1), alg_str , + parser_state_name_esp(ctx.old_state)); + goto err; + default: + if (!ctx.ch) + break; + } + } + return 0; +err: + if (err_p) + *err_p=err_buf; + return -1; +} + +struct alg_info_esp * +alg_info_esp_create_from_str (const char *alg_str, const char **err_p) +{ + struct alg_info_esp *alg_info_esp; + char esp_buf[256]; + static char err_buf[256]; + char *pfs_name; + int ret = 0; + /* + * alg_info storage should be sized dynamically + * but this may require 2passes to know + * transform count in advance. + */ + alg_info_esp = alloc_thing (struct alg_info_esp, "alg_info_esp"); + if (!alg_info_esp) + goto out; + + pfs_name=index (alg_str, ';'); + if (pfs_name) + { + memcpy(esp_buf, alg_str, pfs_name-alg_str); + esp_buf[pfs_name-alg_str] = 0; + alg_str = esp_buf; + pfs_name++; + + /* if pfs strings AND first char is not '0' */ + if (*pfs_name && pfs_name[0] != '0') + { + ret = modp_getbyname_esp(pfs_name, strlen(pfs_name)); + if (ret < 0) + { + /* Bomb if pfsgroup not found */ + DBG(DBG_CRYPT, + DBG_log("alg_info_esp_create_from_str(): pfsgroup \"%s\" not found" + , pfs_name) + ) + if (*err_p) + { + snprintf(err_buf, sizeof(err_buf), + "pfsgroup \"%s\" not found", + pfs_name); + + *err_p = err_buf; + } + goto out; + } + alg_info_esp->esp_pfsgroup = ret; + } + } + else + alg_info_esp->esp_pfsgroup = 0; + + alg_info_esp->alg_info_protoid = PROTO_IPSEC_ESP; + ret = alg_info_parse_str((struct alg_info *)alg_info_esp, alg_str, err_p) ; +out: + if (ret < 0) + { + pfreeany(alg_info_esp); + alg_info_esp = NULL; + } + return alg_info_esp; +} + +#ifndef NO_PLUTO +struct alg_info_ike * +alg_info_ike_create_from_str (const char *alg_str, const char **err_p) +{ + struct alg_info_ike *alg_info_ike; + /* + * alg_info storage should be sized dynamically + * but this may require 2passes to know + * transform count in advance. + */ + alg_info_ike = alloc_thing (struct alg_info_ike, "alg_info_ike"); + alg_info_ike->alg_info_protoid = PROTO_ISAKMP; + + if (alg_info_parse_str((struct alg_info *)alg_info_ike, + alg_str, err_p) < 0) + { + pfreeany(alg_info_ike); + return NULL; + } + return alg_info_ike; +} +#endif + +/* + * alg_info struct can be shared by + * several connections instances, + * handle free() with ref_cnts + */ +void +alg_info_addref(struct alg_info *alg_info) +{ + if (alg_info != NULL) + { + alg_info->ref_cnt++; + DBG(DBG_CRYPT, + DBG_log("alg_info_addref() alg_info->ref_cnt=%d" + , alg_info->ref_cnt) + ) + } +} + +void +alg_info_delref(struct alg_info **alg_info_p) +{ + struct alg_info *alg_info = *alg_info_p; + + if (alg_info != NULL) + { + passert(alg_info->ref_cnt != 0); + alg_info->ref_cnt--; + DBG(DBG_CRYPT, + DBG_log("alg_info_delref() alg_info->ref_cnt=%d" + , alg_info->ref_cnt) + ) + if (alg_info->ref_cnt == 0) + { + DBG(DBG_CRYPT, + DBG_log("alg_info_delref() freeing alg_info") + ) + alg_info_free(alg_info); + } + *alg_info_p = NULL; + } +} + +/* snprint already parsed transform list (alg_info) */ +int +alg_info_snprint(char *buf, int buflen, struct alg_info *alg_info) +{ + char *ptr = buf; + int np = 0; + struct esp_info *esp_info; +#ifndef NO_PLUTO + struct ike_info *ike_info; +#endif + int cnt; + + switch (alg_info->alg_info_protoid) { + case PROTO_IPSEC_ESP: + { + struct alg_info_esp *alg_info_esp = (struct alg_info_esp *)alg_info; + + ALG_INFO_ESP_FOREACH(alg_info_esp, esp_info, cnt) + { + np = snprintf(ptr, buflen, "%d_%03d-%d, " + , esp_info->esp_ealg_id + , (int)esp_info->esp_ealg_keylen + , esp_info->esp_aalg_id); + ptr += np; + buflen -= np; + if (buflen < 0) + goto out; + } + if (alg_info_esp->esp_pfsgroup) + { + np = snprintf(ptr, buflen, "; pfsgroup=%d; " + , alg_info_esp->esp_pfsgroup); + ptr += np; + buflen -= np; + if (buflen < 0) + goto out; + } + break; + } +#ifndef NO_PLUTO + case PROTO_ISAKMP: + ALG_INFO_IKE_FOREACH((struct alg_info_ike *)alg_info, ike_info, cnt) + { + np = snprintf(ptr, buflen, "%d_%03d-%d-%d, " + , ike_info->ike_ealg + , (int)ike_info->ike_eklen + , ike_info->ike_halg + , ike_info->ike_modp); + ptr += np; + buflen -= np; + if (buflen < 0) + goto out; + } + break; +#endif + default: + np = snprintf(buf, buflen, "INVALID protoid=%d\n" + , alg_info->alg_info_protoid); + ptr += np; + buflen -= np; + goto out; + } + + np = snprintf(ptr, buflen, "%s" + , alg_info->alg_info_flags & ALG_INFO_F_STRICT? + "strict":""); + ptr += np; + buflen -= np; +out: + if (buflen < 0) + { + loglog(RC_LOG_SERIOUS + , "buffer space exhausted in alg_info_snprint_ike(), buflen=%d" + , buflen); + } + + return ptr - buf; +} + +#ifndef NO_PLUTO +int +alg_info_snprint_esp(char *buf, int buflen, struct alg_info_esp *alg_info) +{ + char *ptr = buf; + + int cnt = alg_info->alg_info_cnt; + struct esp_info *esp_info = alg_info->esp; + + while (cnt--) + { + if (kernel_alg_esp_enc_ok(esp_info->esp_ealg_id, 0, NULL) + && kernel_alg_esp_auth_ok(esp_info->esp_aalg_id, NULL)) + { + u_int eklen = (esp_info->esp_ealg_keylen) + ? esp_info->esp_ealg_keylen + : kernel_alg_esp_enc_keylen(esp_info->esp_ealg_id) + * BITS_PER_BYTE; + + u_int aklen = esp_info->esp_aalg_keylen + ? esp_info->esp_aalg_keylen + : kernel_alg_esp_auth_keylen(esp_info->esp_aalg_id) + * BITS_PER_BYTE; + + int ret = snprintf(ptr, buflen, "%d_%03d-%d_%03d, ", + esp_info->esp_ealg_id, eklen, + esp_info->esp_aalg_id, aklen); + ptr += ret; + buflen -= ret; + if (buflen < 0) + break; + } + esp_info++; + } + return ptr - buf; +} + +int +alg_info_snprint_ike(char *buf, int buflen, struct alg_info_ike *alg_info) +{ + char *ptr = buf; + + int cnt = alg_info->alg_info_cnt; + struct ike_info *ike_info = alg_info->ike; + + while (cnt--) + { + struct encrypt_desc *enc_desc = ike_alg_get_encrypter(ike_info->ike_ealg); + struct hash_desc *hash_desc = ike_alg_get_hasher(ike_info->ike_halg); + + if (enc_desc != NULL && hash_desc != NULL + && lookup_group(ike_info->ike_modp)) + { + + u_int eklen = (ike_info->ike_eklen) + ? ike_info->ike_eklen + : enc_desc->keydeflen; + + u_int aklen = (ike_info->ike_hklen) + ? ike_info->ike_hklen + : hash_desc->hash_digest_size * BITS_PER_BYTE; + + int ret = snprintf(ptr, buflen, "%d_%03d-%d_%03d-%d, ", + ike_info->ike_ealg, eklen, + ike_info->ike_halg, aklen, + ike_info->ike_modp); + ptr += ret; + buflen -= ret; + if (buflen < 0) + break; + } + ike_info++; + } + return ptr - buf; +} +#endif /* NO_PLUTO */ diff --git a/src/pluto/alg_info.h b/src/pluto/alg_info.h new file mode 100644 index 000000000..cd2011dcc --- /dev/null +++ b/src/pluto/alg_info.h @@ -0,0 +1,85 @@ +/* Algorithm info parsing and creation functions + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: alg_info.h,v 1.4 2004/09/29 22:39:44 as Exp $ + */ + +#ifndef ALG_INFO_H +#define ALG_INFO_H + +struct esp_info { + u_int8_t transid; /* ESP transform */ + u_int16_t auth; /* AUTH */ + size_t enckeylen; /* keylength for ESP transform */ + size_t authkeylen; /* keylength for AUTH */ + u_int8_t encryptalg; /* normally encryptalg=transid */ + u_int8_t authalg; /* normally authalg=auth+1 */ +}; + +struct ike_info { + u_int16_t ike_ealg; /* high 16 bit nums for reserved */ + u_int8_t ike_halg; + size_t ike_eklen; + size_t ike_hklen; + u_int16_t ike_modp; +}; + +#define ALG_INFO_COMMON \ + int alg_info_cnt; \ + int ref_cnt; \ + unsigned alg_info_flags; \ + unsigned alg_info_protoid + +struct alg_info { + ALG_INFO_COMMON; +}; + +struct alg_info_esp { + ALG_INFO_COMMON; + struct esp_info esp[64]; + int esp_pfsgroup; +}; + +struct alg_info_ike { + ALG_INFO_COMMON; + struct ike_info ike[64]; +}; +#define esp_ealg_id transid +#define esp_aalg_id auth +#define esp_ealg_keylen enckeylen /* bits */ +#define esp_aalg_keylen authkeylen /* bits */ + +/* alg_info_flags bits */ +#define ALG_INFO_F_STRICT 0x01 + +extern int alg_info_esp_aa2sadb(int auth); +extern int alg_info_esp_sadb2aa(int sadb_aalg); +extern void alg_info_free(struct alg_info *alg_info); +extern void alg_info_addref(struct alg_info *alg_info); +extern void alg_info_delref(struct alg_info **alg_info); +extern struct alg_info_esp* alg_info_esp_create_from_str(const char *alg_str + , const char **err_p); +extern struct alg_info_ike* alg_info_ike_create_from_str(const char *alg_str + , const char **err_p); +extern int alg_info_parse(const char *str); +extern int alg_info_snprint(char *buf, int buflen + , struct alg_info *alg_info); +extern int alg_info_snprint_esp(char *buf, int buflen + , struct alg_info_esp *alg_info); +extern int alg_info_snprint_ike(char *buf, int buflen + , struct alg_info_ike *alg_info); +#define ALG_INFO_ESP_FOREACH(ai, ai_esp, i) \ + for (i=(ai)->alg_info_cnt,ai_esp=(ai)->esp; i--; ai_esp++) +#define ALG_INFO_IKE_FOREACH(ai, ai_ike, i) \ + for (i=(ai)->alg_info_cnt,ai_ike=(ai)->ike; i--; ai_ike++) +#endif /* ALG_INFO_H */ diff --git a/src/pluto/asn1.c b/src/pluto/asn1.c new file mode 100644 index 000000000..0663bc490 --- /dev/null +++ b/src/pluto/asn1.c @@ -0,0 +1,770 @@ +/* Simple ASN.1 parser + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: asn1.c,v 1.16 2006/01/04 21:00:43 as Exp $ + */ + +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "asn1.h" +#include "oid.h" +#include "log.h" + +/* some common prefabricated ASN.1 constants */ + +static u_char ASN1_INTEGER_0_str[] = { 0x02, 0x00 }; +static u_char ASN1_INTEGER_1_str[] = { 0x02, 0x01, 0x01 }; +static u_char ASN1_INTEGER_2_str[] = { 0x02, 0x01, 0x02 }; + +const chunk_t ASN1_INTEGER_0 = strchunk(ASN1_INTEGER_0_str); +const chunk_t ASN1_INTEGER_1 = strchunk(ASN1_INTEGER_1_str); +const chunk_t ASN1_INTEGER_2 = strchunk(ASN1_INTEGER_2_str); + +/* some popular algorithmIdentifiers */ + +static u_char ASN1_md5_id_str[] = { + 0x30, 0x0C, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, + 0x05, 0x00 +}; + +static u_char ASN1_sha1_id_str[] = { + 0x30, 0x09, + 0x06, 0x05, 0x2B, 0x0E,0x03, 0x02, 0x1A, + 0x05, 0x00 +}; + +static u_char ASN1_md5WithRSA_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, + 0x05, 0x00 +}; + +static u_char ASN1_sha1WithRSA_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, + 0x05, 0x00 +}; + +static u_char ASN1_rsaEncryption_id_str[] = { + 0x30, 0x0D, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00 +}; + +const chunk_t ASN1_md5_id = strchunk(ASN1_md5_id_str); +const chunk_t ASN1_sha1_id = strchunk(ASN1_sha1_id_str); +const chunk_t ASN1_rsaEncryption_id = strchunk(ASN1_rsaEncryption_id_str); +const chunk_t ASN1_md5WithRSA_id = strchunk(ASN1_md5WithRSA_id_str); +const chunk_t ASN1_sha1WithRSA_id = strchunk(ASN1_sha1WithRSA_id_str); + +/* ASN.1 definiton of an algorithmIdentifier */ + +static const asn1Object_t algorithmIdentifierObjects[] = { + { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "parameters", ASN1_EOC, ASN1_RAW } /* 2 */ +}; + +#define ALGORITHM_ID_ALG 1 +#define ALGORITHM_ID_PARAMETERS 2 +#define ALGORITHM_ID_ROOF 3 + +/* + * return the ASN.1 encoded algorithm identifier + */ +chunk_t +asn1_algorithmIdentifier(int oid) +{ + switch (oid) + { + case OID_RSA_ENCRYPTION: + return ASN1_rsaEncryption_id; + case OID_MD5_WITH_RSA: + return ASN1_md5WithRSA_id; + case OID_SHA1_WITH_RSA: + return ASN1_sha1WithRSA_id; + case OID_MD5: + return ASN1_md5_id; + case OID_SHA1: + return ASN1_sha1_id; + default: + return empty_chunk; + } +} + +/* If the oid is listed in the oid_names table then the corresponding + * position in the oid_names table is returned otherwise -1 is returned + */ +int +known_oid(chunk_t object) +{ + int oid = 0; + + while (object.len) + { + if (oid_names[oid].octet == *object.ptr) + { + if (--object.len == 0 || oid_names[oid].down == 0) + { + return oid; /* found terminal symbol */ + } + else + { + object.ptr++; oid++; /* advance to next hex octet */ + } + } + else + { + if (oid_names[oid].next) + oid = oid_names[oid].next; + else + return OID_UNKNOWN; + } + } + return -1; +} + +/* + * Decodes the length in bytes of an ASN.1 object + */ +u_int +asn1_length(chunk_t *blob) +{ + u_char n; + size_t len; + + /* advance from tag field on to length field */ + blob->ptr++; + blob->len--; + + /* read first octet of length field */ + n = *blob->ptr++; + blob->len--; + + if ((n & 0x80) == 0) /* single length octet */ + return n; + + /* composite length, determine number of length octets */ + n &= 0x7f; + + if (n > blob->len) + { + DBG(DBG_PARSING, + DBG_log("number of length octets is larger than ASN.1 object") + ) + return ASN1_INVALID_LENGTH; + } + + if (n > sizeof(len)) + { + DBG(DBG_PARSING, + DBG_log("number of length octets is larger than limit of %d octets" + , (int)sizeof(len)) + ) + return ASN1_INVALID_LENGTH; + } + + len = 0; + + while (n-- > 0) + { + len = 256*len + *blob->ptr++; + blob->len--; + } + return len; +} + +/* + * codes ASN.1 lengths up to a size of 16'777'215 bytes + */ +void +code_asn1_length(size_t length, chunk_t *code) +{ + if (length < 128) + { + code->ptr[0] = length; + code->len = 1; + } + else if (length < 256) + { + code->ptr[0] = 0x81; + code->ptr[1] = (u_char) length; + code->len = 2; + } + else if (length < 65536) + { + code->ptr[0] = 0x82; + code->ptr[1] = length >> 8; + code->ptr[2] = length & 0x00ff; + code->len = 3; + } + else + { + code->ptr[0] = 0x83; + code->ptr[1] = length >> 16; + code->ptr[2] = (length >> 8) & 0x00ff; + code->ptr[3] = length & 0x0000ff; + code->len = 4; + } +} + +/* + * build an empty asn.1 object with tag and length fields already filled in + */ +u_char* +build_asn1_object(chunk_t *object, asn1_t type, size_t datalen) +{ + u_char length_buf[4]; + chunk_t length = { length_buf, 0 }; + u_char *pos; + + /* code the asn.1 length field */ + code_asn1_length(datalen, &length); + + /* allocate memory for the asn.1 TLV object */ + object->len = 1 + length.len + datalen; + object->ptr = alloc_bytes(object->len, "asn1 object"); + + /* set position pointer at the start of the object */ + pos = object->ptr; + + /* copy the asn.1 tag field and advance the pointer */ + *pos++ = type; + + /* copy the asn.1 length field and advance the pointer */ + chunkcpy(pos, length); + + return pos; +} + +/* + * build a simple ASN.1 object + */ +chunk_t +asn1_simple_object(asn1_t tag, chunk_t content) +{ + chunk_t object; + + u_char *pos = build_asn1_object(&object, tag, content.len); + chunkcpy(pos, content); + + return object; +} + +/* Build an ASN.1 object from a variable number of individual chunks. + * Depending on the mode, chunks either are moved ('m') or copied ('c'). + */ +chunk_t +asn1_wrap(asn1_t type, const char *mode, ...) +{ + chunk_t construct; + va_list chunks; + u_char *pos; + int i; + int count = strlen(mode); + + /* sum up lengths of individual chunks */ + va_start(chunks, mode); + construct.len = 0; + for (i = 0; i < count; i++) + { + chunk_t ch = va_arg(chunks, chunk_t); + construct.len += ch.len; + } + va_end(chunks); + + /* allocate needed memory for construct */ + pos = build_asn1_object(&construct, type, construct.len); + + /* copy or move the chunks */ + va_start(chunks, mode); + for (i = 0; i < count; i++) + { + chunk_t ch = va_arg(chunks, chunk_t); + + switch (*mode++) + { + case 'm': + mv_chunk(&pos, ch); + break; + case 'c': + default: + chunkcpy(pos, ch); + } + } + va_end(chunks); + + return construct; +} + +/* + * convert a MP integer into a DER coded ASN.1 object + */ +chunk_t +asn1_integer_from_mpz(const mpz_t value) +{ + size_t bits = mpz_sizeinbase(value, 2); /* size in bits */ + size_t size = 1 + bits / BITS_PER_BYTE; /* size in bytes */ + chunk_t n = mpz_to_n(value, size); + + return asn1_wrap(ASN1_INTEGER, "m", n); +} + +/* + * determines if a character string is of type ASN.1 printableString + */ +bool +is_printablestring(chunk_t str) +{ + const char printablestring_charset[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?"; + u_int i; + + for (i = 0; i < str.len; i++) + { + if (strchr(printablestring_charset, str.ptr[i]) == NULL) + return FALSE; + } + return TRUE; +} + +/* + * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time + */ +time_t +asn1totime(const chunk_t *utctime, asn1_t type) +{ + struct tm t; + time_t tz_offset; + u_char *eot = NULL; + + if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL) + { + tz_offset = 0; /* Zulu time with a zero time zone offset */ + } + else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL) + { + int tz_hour, tz_min; + + sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min); + tz_offset = 3600*tz_hour + 60*tz_min; /* positive time zone offset */ + } + else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL) + { + int tz_hour, tz_min; + + sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min); + tz_offset = -3600*tz_hour - 60*tz_min; /* negative time zone offset */ + } + else + { + return 0; /* error in time format */ + } + + { + const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d": + "%4d%2d%2d%2d%2d"; + + sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday, + &t.tm_hour, &t.tm_min); + } + + /* is there a seconds field? */ + if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14)) + { + sscanf(eot-2, "%2d", &t.tm_sec); + } + else + { + t.tm_sec = 0; + } + + /* representation of year */ + if (t.tm_year >= 1900) + { + t.tm_year -= 1900; + } + else if (t.tm_year >= 100) + { + return 0; + } + else if (t.tm_year < 50) + { + t.tm_year += 100; + } + + /* representation of month 0..11*/ + t.tm_mon--; + + /* set daylight saving time to off */ + t.tm_isdst = 0; + + /* compensate timezone */ + + return mktime(&t) - timezone - tz_offset; +} + +/* + * convert a date into ASN.1 UTCTIME or GENERALIZEDTIME format + */ +chunk_t +timetoasn1(const time_t *time, asn1_t type) +{ + int offset; + const char *format; + char buf[TIMETOA_BUF]; + chunk_t formatted_time; + struct tm *t = gmtime(time); + + if (type == ASN1_GENERALIZEDTIME) + { + format = "%04d%02d%02d%02d%02d%02dZ"; + offset = 1900; + } + else /* ASN1_UTCTIME */ + { + format = "%02d%02d%02d%02d%02d%02dZ"; + offset = (t->tm_year < 100)? 0 : -100; + } + sprintf(buf, format, t->tm_year + offset, t->tm_mon + 1, t->tm_mday + , t->tm_hour, t->tm_min, t->tm_sec); + formatted_time.ptr = buf; + formatted_time.len = strlen(buf); + return asn1_simple_object(type, formatted_time); +} + + +/* + * Initializes the internal context of the ASN.1 parser + */ +void +asn1_init(asn1_ctx_t *ctx, chunk_t blob, u_int level0, + bool implicit, u_int cond) +{ + ctx->blobs[0] = blob; + ctx->level0 = level0; + ctx->implicit = implicit; + ctx->cond = cond; + memset(ctx->loopAddr, '\0', sizeof(ctx->loopAddr)); +} + +/* + * print the value of an ASN.1 simple object + */ +static void +debug_asn1_simple_object(chunk_t object, asn1_t type, u_int cond) +{ + int oid; + + switch (type) + { + case ASN1_OID: + oid = known_oid(object); + if (oid != OID_UNKNOWN) + { + DBG(DBG_PARSING, + DBG_log(" '%s'",oid_names[oid].name); + ) + return; + } + break; + case ASN1_UTF8STRING: + case ASN1_IA5STRING: + case ASN1_PRINTABLESTRING: + case ASN1_T61STRING: + case ASN1_VISIBLESTRING: + DBG(DBG_PARSING, + DBG_log(" '%.*s'", (int)object.len, object.ptr); + ) + return; + case ASN1_UTCTIME: + case ASN1_GENERALIZEDTIME: + DBG(DBG_PARSING, + time_t time = asn1totime(&object, type); + DBG_log(" '%s'", timetoa(&time, TRUE)); + ) + return; + default: + break; + } + DBG(cond, + DBG_dump_chunk("", object); + ) +} + +/* + * Parses and extracts the next ASN.1 object + */ +bool +extract_object(asn1Object_t const *objects, + u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx) +{ + asn1Object_t obj = objects[*objectID]; + chunk_t *blob; + chunk_t *blob1; + u_char *start_ptr; + + *object = empty_chunk; + + if (obj.flags & ASN1_END) /* end of loop or option found */ + { + if (ctx->loopAddr[obj.level] && ctx->blobs[obj.level+1].len > 0) + { + *objectID = ctx->loopAddr[obj.level]; /* another iteration */ + obj = objects[*objectID]; + } + else + { + ctx->loopAddr[obj.level] = 0; /* exit loop or option*/ + return TRUE; + } + } + + *level = ctx->level0 + obj.level; + blob = ctx->blobs + obj.level; + blob1 = blob + 1; + start_ptr = blob->ptr; + + /* handle ASN.1 defaults values */ + + if ((obj.flags & ASN1_DEF) + && (blob->len == 0 || *start_ptr != obj.type) ) + { + /* field is missing */ + DBG(DBG_PARSING, + DBG_log("L%d - %s:", *level, obj.name); + ) + if (obj.type & ASN1_CONSTRUCTED) + { + (*objectID)++ ; /* skip context-specific tag */ + } + return TRUE; + } + + /* handle ASN.1 options */ + + if ((obj.flags & ASN1_OPT) + && (blob->len == 0 || *start_ptr != obj.type)) + { + /* advance to end of missing option field */ + do + (*objectID)++; + while (!((objects[*objectID].flags & ASN1_END) + && (objects[*objectID].level == obj.level))); + return TRUE; + } + + /* an ASN.1 object must possess at least a tag and length field */ + + if (blob->len < 2) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s: ASN.1 object smaller than 2 octets", + *level, obj.name); + ) + return FALSE; + } + + blob1->len = asn1_length(blob); + + if (blob1->len == ASN1_INVALID_LENGTH || blob->len < blob1->len) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s: length of ASN.1 object invalid or too large", + *level, obj.name); + ) + return FALSE; + } + + blob1->ptr = blob->ptr; + blob->ptr += blob1->len; + blob->len -= blob1->len; + + /* return raw ASN.1 object without prior type checking */ + + if (obj.flags & ASN1_RAW) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s:", *level, obj.name); + ) + object->ptr = start_ptr; + object->len = (size_t)(blob->ptr - start_ptr); + return TRUE; + } + + if (*start_ptr != obj.type && !(ctx->implicit && *objectID == 0)) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + *level, obj.name, obj.type, *start_ptr); + DBG_dump("", start_ptr, (u_int)(blob->ptr - start_ptr)); + ) + return FALSE; + } + + DBG(DBG_PARSING, + DBG_log("L%d - %s:", ctx->level0+obj.level, obj.name); + ) + + /* In case of "SEQUENCE OF" or "SET OF" start a loop */ + + if (obj.flags & ASN1_LOOP) + { + if (blob1->len > 0) + { + /* at least one item, start the loop */ + ctx->loopAddr[obj.level] = *objectID + 1; + } + else + { + /* no items, advance directly to end of loop */ + do + (*objectID)++; + while (!((objects[*objectID].flags & ASN1_END) + && (objects[*objectID].level == obj.level))); + return TRUE; + } + } + + if (obj.flags & ASN1_OBJ) + { + object->ptr = start_ptr; + object->len = (size_t)(blob->ptr - start_ptr); + DBG(ctx->cond, + DBG_dump_chunk("", *object); + ) + } + else if (obj.flags & ASN1_BODY) + { + *object = *blob1; + debug_asn1_simple_object(*object, obj.type, ctx->cond); + } + return TRUE; +} + +/* + * parse an ASN.1 simple type + */ +bool +parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level +, const char* name) +{ + size_t len; + + /* an ASN.1 object must possess at least a tag and length field */ + if (object->len < 2) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s: ASN.1 object smaller than 2 octets", + level, name); + ) + return FALSE; + } + + if (*object->ptr != type) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + level, name, type, *object->ptr); + ) + return FALSE; + } + + len = asn1_length(object); + + if (len == ASN1_INVALID_LENGTH || object->len < len) + { + DBG(DBG_PARSING, + DBG_log("L%d - %s: length of ASN.1 object invalid or too large", + level, name); + ) + return FALSE; + } + + DBG(DBG_PARSING, + DBG_log("L%d - %s:", level, name); + ) + debug_asn1_simple_object(*object, type, DBG_RAW); + return TRUE; +} + +/* + * extracts an algorithmIdentifier + */ +int +parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int alg = OID_UNKNOWN; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < ALGORITHM_ID_ROOF) + { + if (!extract_object(algorithmIdentifierObjects, &objectID, &object, &level, &ctx)) + return OID_UNKNOWN; + + switch (objectID) + { + case ALGORITHM_ID_ALG: + alg = known_oid(object); + break; + case ALGORITHM_ID_PARAMETERS: + if (parameters != NULL) + *parameters = object; + break; + default: + break; + } + objectID++; + } + return alg; + } + +/* + * tests if a blob contains a valid ASN.1 set or sequence + */ +bool +is_asn1(chunk_t blob) +{ + u_int len; + u_char tag = *blob.ptr; + + if (tag != ASN1_SEQUENCE && tag != ASN1_SET) + { + DBG(DBG_PARSING, + DBG_log(" file content is not binary ASN.1"); + ) + return FALSE; + } + len = asn1_length(&blob); + if (len != blob.len) + { + DBG(DBG_PARSING, + DBG_log(" file size does not match ASN.1 coded length"); + ) + return FALSE; + } + return TRUE; +} diff --git a/src/pluto/asn1.h b/src/pluto/asn1.h new file mode 100644 index 000000000..2a3fb3e9e --- /dev/null +++ b/src/pluto/asn1.h @@ -0,0 +1,141 @@ +/* Simple ASN.1 parser + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: asn1.h,v 1.14 2005/12/06 22:50:10 as Exp $ + */ + +#ifndef _ASN1_H +#define _ASN1_H + +#include +#include + +#include "defs.h" + +/* Defines some primitive ASN1 types */ + +typedef enum { + ASN1_EOC = 0x00, + ASN1_BOOLEAN = 0x01, + ASN1_INTEGER = 0x02, + ASN1_BIT_STRING = 0x03, + ASN1_OCTET_STRING = 0x04, + ASN1_NULL = 0x05, + ASN1_OID = 0x06, + ASN1_ENUMERATED = 0x0A, + ASN1_UTF8STRING = 0x0C, + ASN1_NUMERICSTRING = 0x12, + ASN1_PRINTABLESTRING = 0x13, + ASN1_T61STRING = 0x14, + ASN1_VIDEOTEXSTRING = 0x15, + ASN1_IA5STRING = 0x16, + ASN1_UTCTIME = 0x17, + ASN1_GENERALIZEDTIME = 0x18, + ASN1_GRAPHICSTRING = 0x19, + ASN1_VISIBLESTRING = 0x1A, + ASN1_GENERALSTRING = 0x1B, + ASN1_UNIVERSALSTRING = 0x1C, + ASN1_BMPSTRING = 0x1E, + + ASN1_CONSTRUCTED = 0x20, + + ASN1_SEQUENCE = 0x30, + + ASN1_SET = 0x31, + + ASN1_CONTEXT_S_0 = 0x80, + ASN1_CONTEXT_S_1 = 0x81, + ASN1_CONTEXT_S_2 = 0x82, + ASN1_CONTEXT_S_3 = 0x83, + ASN1_CONTEXT_S_4 = 0x84, + ASN1_CONTEXT_S_5 = 0x85, + ASN1_CONTEXT_S_6 = 0x86, + ASN1_CONTEXT_S_7 = 0x87, + ASN1_CONTEXT_S_8 = 0x88, + + ASN1_CONTEXT_C_0 = 0xA0, + ASN1_CONTEXT_C_1 = 0xA1, + ASN1_CONTEXT_C_2 = 0xA2, + ASN1_CONTEXT_C_3 = 0xA3, + ASN1_CONTEXT_C_4 = 0xA4, + ASN1_CONTEXT_C_5 = 0xA5 +} asn1_t; + +/* Definition of ASN1 flags */ + +#define ASN1_NONE 0x00 +#define ASN1_DEF 0x01 +#define ASN1_OPT 0x02 +#define ASN1_LOOP 0x04 +#define ASN1_END 0x08 +#define ASN1_OBJ 0x10 +#define ASN1_BODY 0x20 +#define ASN1_RAW 0x40 + +#define ASN1_INVALID_LENGTH 0xffffffff + +/* definition of an ASN.1 object */ + +typedef struct { + u_int level; + const u_char *name; + asn1_t type; + u_char flags; +} asn1Object_t; + +#define ASN1_MAX_LEVEL 10 + +typedef struct { + bool implicit; + u_int cond; + u_int level0; + u_int loopAddr[ASN1_MAX_LEVEL+1]; + chunk_t blobs[ASN1_MAX_LEVEL+2]; +} asn1_ctx_t; + +/* some common prefabricated ASN.1 constants */ + +extern const chunk_t ASN1_INTEGER_0; +extern const chunk_t ASN1_INTEGER_1; +extern const chunk_t ASN1_INTEGER_2; + +/* some popular algorithmIdentifiers */ +extern const chunk_t ASN1_md5_id; +extern const chunk_t ASN1_sha1_id; +extern const chunk_t ASN1_rsaEncryption_id; +extern const chunk_t ASN1_md5WithRSA_id; +extern const chunk_t ASN1_sha1WithRSA_id; + +extern chunk_t asn1_algorithmIdentifier(int oid); +extern int known_oid(chunk_t object); +extern u_int asn1_length(chunk_t *blob); +extern void code_asn1_length(size_t length, chunk_t *code); +extern u_char* build_asn1_object(chunk_t *object, asn1_t type, size_t datalen); +extern chunk_t asn1_integer_from_mpz(const mpz_t value); +extern chunk_t asn1_simple_object(asn1_t tag, chunk_t content); +extern chunk_t asn1_wrap(asn1_t type, const char *mode, ...); +extern bool is_printablestring(chunk_t str); +extern time_t asn1totime(const chunk_t *utctime, asn1_t type); +extern chunk_t timetoasn1(const time_t *time, asn1_t type); +extern void asn1_init(asn1_ctx_t *ctx, chunk_t blob + , u_int level0, bool implicit, u_int cond); +extern bool extract_object(asn1Object_t const *objects + , u_int *objectID, chunk_t *object, u_int *level, asn1_ctx_t *ctx); +extern bool parse_asn1_simple_object(chunk_t *object, asn1_t type, u_int level + , const char* name); +extern int parse_algorithmIdentifier(chunk_t blob, int level0 + , chunk_t *parameters); +extern bool is_asn1(chunk_t blob); + +#endif /* _ASN1_H */ + diff --git a/src/pluto/ca.c b/src/pluto/ca.c new file mode 100644 index 000000000..d1be22e2f --- /dev/null +++ b/src/pluto/ca.c @@ -0,0 +1,694 @@ +/* Certification Authority (CA) support for IKE authentication + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ca.c,v 1.10 2005/12/25 12:29:55 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "x509.h" +#include "ca.h" +#include "certs.h" +#include "whack.h" +#include "fetch.h" + +/* chained list of X.509 authority certificates (ca, aa, and ocsp) */ + +static x509cert_t *x509authcerts = NULL; + +const ca_info_t empty_ca_info = { + NULL , /* next */ + NULL , /* name */ + UNDEFINED_TIME, + { NULL, 0 } , /* authName */ + { NULL, 0 } , /* authKeyID */ + { NULL, 0 } , /* authKey SerialNumber */ + NULL , /* ldaphost */ + NULL , /* ldapbase */ + NULL , /* ocspori */ + NULL , /* crluri */ + FALSE /* strictcrlpolicy */ +}; + +/* chained list of X.509 certification authority information records */ + +static ca_info_t *ca_infos = NULL; + +/* + * Checks if CA a is trusted by CA b + */ +bool +trusted_ca(chunk_t a, chunk_t b, int *pathlen) +{ + bool match = FALSE; + + /* no CA b specified -> any CA a is accepted */ + if (b.ptr == NULL) + { + *pathlen = (a.ptr == NULL)? 0 : MAX_CA_PATH_LEN; + return TRUE; + } + + /* no CA a specified -> trust cannot be established */ + if (a.ptr == NULL) + { + *pathlen = MAX_CA_PATH_LEN; + return FALSE; + } + + *pathlen = 0; + + /* CA a equals CA b -> we have a match */ + if (same_dn(a, b)) + return TRUE; + + /* CA a might be a subordinate CA of b */ + lock_authcert_list("trusted_ca"); + + while ((*pathlen)++ < MAX_CA_PATH_LEN) + { + x509cert_t *cacert = get_authcert(a, empty_chunk, empty_chunk, AUTH_CA); + + /* cacert not found or self-signed root cacert-> exit */ + if (cacert == NULL || same_dn(cacert->issuer, a)) + break; + + /* does the issuer of CA a match CA b? */ + match = same_dn(cacert->issuer, b); + + /* we have a match and exit the loop */ + if (match) + break; + + /* go one level up in the CA chain */ + a = cacert->issuer; + } + + unlock_authcert_list("trusted_ca"); + return match; +} + +/* + * does our CA match one of the requested CAs? + */ +bool +match_requested_ca(generalName_t *requested_ca, chunk_t our_ca, int *our_pathlen) +{ + /* if no ca is requested than any ca will match */ + if (requested_ca == NULL) + { + *our_pathlen = 0; + return TRUE; + } + + *our_pathlen = MAX_CA_PATH_LEN + 1; + + while (requested_ca != NULL) + { + int pathlen; + + if (trusted_ca(our_ca, requested_ca->name, &pathlen) + && pathlen < *our_pathlen) + *our_pathlen = pathlen; + requested_ca = requested_ca->next; + } + + return *our_pathlen <= MAX_CA_PATH_LEN; +} + +/* + * free the first authority certificate in the chain + */ +static void +free_first_authcert(void) +{ + x509cert_t *first = x509authcerts; + x509authcerts = first->next; + free_x509cert(first); +} + +/* + * free all CA certificates + */ +void +free_authcerts(void) +{ + lock_authcert_list("free_authcerts"); + + while (x509authcerts != NULL) + free_first_authcert(); + + unlock_authcert_list("free_authcerts"); +} + +/* + * get a X.509 authority certificate with a given subject or keyid + */ +x509cert_t* +get_authcert(chunk_t subject, chunk_t serial, chunk_t keyid, u_char auth_flags) +{ + x509cert_t *cert = x509authcerts; + x509cert_t *prev_cert = NULL; + + while (cert != NULL) + { + if (cert->authority_flags & auth_flags + && ((keyid.ptr != NULL) ? same_keyid(keyid, cert->subjectKeyID) + : (same_dn(subject, cert->subject) + && same_serial(serial, cert->serialNumber)))) + { + if (cert != x509authcerts) + { + /* bring the certificate up front */ + prev_cert->next = cert->next; + cert->next = x509authcerts; + x509authcerts = cert; + } + return cert; + } + prev_cert = cert; + cert = cert->next; + } + return NULL; +} + +/* + * add an authority certificate to the chained list + */ +bool +add_authcert(x509cert_t *cert, u_char auth_flags) +{ + x509cert_t *old_cert; + + /* set authority flags */ + cert->authority_flags |= auth_flags; + + lock_authcert_list("add_authcert"); + + old_cert = get_authcert(cert->subject, cert->serialNumber + , cert->subjectKeyID, auth_flags); + + if (old_cert != NULL) + { + if (same_x509cert(cert, old_cert)) + { + /* cert is already present, just add additional authority flags */ + old_cert->authority_flags |= cert->authority_flags; + DBG(DBG_CONTROL | DBG_PARSING , + DBG_log(" authcert is already present and identical") + ) + unlock_authcert_list("add_authcert"); + + free_x509cert(cert); + return FALSE; + } + else + { + /* cert is already present but will be replaced by new cert */ + free_first_authcert(); + DBG(DBG_CONTROL | DBG_PARSING , + DBG_log(" existing authcert deleted") + ) + } + } + + /* add new authcert to chained list */ + cert->next = x509authcerts; + x509authcerts = cert; + share_x509cert(cert); /* set count to one */ + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log(" authcert inserted") + ) + unlock_authcert_list("add_authcert"); + return TRUE; +} + +/* + * Loads authority certificates + */ +void +load_authcerts(const char *type, const char *path, u_char auth_flags) +{ + struct dirent **filelist; + u_char buf[BUF_LEN]; + u_char *save_dir; + int n; + + /* change directory to specified path */ + save_dir = getcwd(buf, BUF_LEN); + + if (chdir(path)) + { + plog("Could not change to directory '%s'", path); + } + else + { + plog("Changing to directory '%s'", path); + n = scandir(path, &filelist, file_select, alphasort); + + if (n < 0) + plog(" scandir() error"); + else + { + while (n--) + { + cert_t cert; + + if (load_cert(filelist[n]->d_name, type, &cert)) + add_authcert(cert.u.x509, auth_flags); + + free(filelist[n]); + } + free(filelist); + } + } + /* restore directory path */ + chdir(save_dir); +} + +/* + * list all X.509 authcerts with given auth flags in a chained list + */ +void +list_authcerts(const char *caption, u_char auth_flags, bool utc) +{ + lock_authcert_list("list_authcerts"); + list_x509cert_chain(caption, x509authcerts, auth_flags, utc); + unlock_authcert_list("list_authcerts"); +} + +/* + * get a cacert with a given subject or keyid from an alternative list + */ +static const x509cert_t* +get_alt_cacert(chunk_t subject, chunk_t serial, chunk_t keyid + , const x509cert_t *cert) +{ + while (cert != NULL) + { + if ((keyid.ptr != NULL) ? same_keyid(keyid, cert->subjectKeyID) + : (same_dn(subject, cert->subject) + && same_serial(serial, cert->serialNumber))) + { + return cert; + } + cert = cert->next; + } + return NULL; +} + +/* establish trust into a candidate authcert by going up the trust chain. + * validity and revocation status are not checked. + */ +bool +trust_authcert_candidate(const x509cert_t *cert, const x509cert_t *alt_chain) +{ + int pathlen; + + lock_authcert_list("trust_authcert_candidate"); + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + const x509cert_t *authcert = NULL; + u_char buf[BUF_LEN]; + + DBG(DBG_CONTROL, + dntoa(buf, BUF_LEN, cert->subject); + DBG_log("subject: '%s'",buf); + dntoa(buf, BUF_LEN, cert->issuer); + DBG_log("issuer: '%s'",buf); + if (cert->authKeyID.ptr != NULL) + { + datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':' + , buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + ) + + /* search in alternative chain first */ + authcert = get_alt_cacert(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID, alt_chain); + + if (authcert != NULL) + { + DBG(DBG_CONTROL, + DBG_log("issuer cacert found in alternative chain") + ) + } + else + { + /* search in trusted chain */ + authcert = get_authcert(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID, AUTH_CA); + + if (authcert != NULL) + { + DBG(DBG_CONTROL, + DBG_log("issuer cacert found") + ) + } + else + { + plog("issuer cacert not found"); + unlock_authcert_list("trust_authcert_candidate"); + return FALSE; + } + } + + if (!check_signature(cert->tbsCertificate, cert->signature + , cert->algorithm, cert->algorithm, authcert)) + { + plog("certificate signature is invalid"); + unlock_authcert_list("trust_authcert_candidate"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("certificate signature is valid") + ) + + /* check if cert is a self-signed root ca */ + if (pathlen > 0 && same_dn(cert->issuer, cert->subject)) + { + DBG(DBG_CONTROL, + DBG_log("reached self-signed root ca") + ) + unlock_authcert_list("trust_authcert_candidate"); + return TRUE; + } + + /* go up one step in the trust chain */ + cert = authcert; + } + plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + unlock_authcert_list("trust_authcert_candidate"); + return FALSE; +} + +/* + * get a CA info record with a given authName or authKeyID + */ +ca_info_t* +get_ca_info(chunk_t authname, chunk_t serial, chunk_t keyid) +{ + ca_info_t *ca= ca_infos; + + while (ca!= NULL) + { + if ((keyid.ptr != NULL) ? same_keyid(keyid, ca->authKeyID) + : (same_dn(authname, ca->authName) + && same_serial(serial, ca->authKeySerialNumber))) + { + return ca; + } + ca = ca->next; + } + return NULL; +} + + +/* + * free the dynamic memory used by a ca_info record + */ +static void +free_ca_info(ca_info_t* ca_info) +{ + if (ca_info == NULL) + return; + + pfreeany(ca_info->name); + pfreeany(ca_info->ldaphost); + pfreeany(ca_info->ldapbase); + pfreeany(ca_info->ocspuri); + + freeanychunk(ca_info->authName); + freeanychunk(ca_info->authKeyID); + freeanychunk(ca_info->authKeySerialNumber); + + free_generalNames(ca_info->crluri, TRUE); + + pfree(ca_info); +} + +/* + * free all CA certificates + */ +void +free_ca_infos(void) +{ + while (ca_infos != NULL) + { + ca_info_t *ca = ca_infos; + + ca_infos = ca_infos->next; + free_ca_info(ca); + } +} + +/* + * find a CA information record by name and optionally delete it + */ +bool +find_ca_info_by_name(const char *name, bool delete) +{ + ca_info_t **ca_p = &ca_infos; + ca_info_t *ca = *ca_p; + + while (ca != NULL) + { + /* is there already an entry? */ + if (streq(name, ca->name)) + { + if (delete) + { + lock_ca_info_list("find_ca_info_by_name"); + *ca_p = ca->next; + free_ca_info(ca); + plog("deleting ca description \"%s\"", name); + unlock_ca_info_list("find_ca_info_by_name"); + } + return TRUE; + } + ca_p = &ca->next; + ca = *ca_p; + } + return FALSE; +} + + + /* + * adds a CA description to a chained list + */ +void +add_ca_info(const whack_message_t *msg) +{ + smartcard_t *sc = NULL; + cert_t cert; + bool valid_cert = FALSE; + bool cached_cert = FALSE; + + if (find_ca_info_by_name(msg->name, FALSE)) + { + loglog(RC_DUPNAME, "attempt to redefine ca record \"%s\"", msg->name); + return; + } + + if (scx_on_smartcard(msg->cacert)) + { + /* load CA cert from smartcard */ + valid_cert = scx_load_cert(msg->cacert, &sc, &cert, &cached_cert); + } + else + { + /* load CA cert from file */ + valid_cert = load_ca_cert(msg->cacert, &cert); + } + + if (valid_cert) + { + char buf[BUF_LEN]; + x509cert_t *cacert = cert.u.x509; + ca_info_t *ca = NULL; + + /* does the authname already exist? */ + ca = get_ca_info(cacert->subject, cacert->serialNumber + , cacert->subjectKeyID); + + if (ca != NULL) + { + /* ca_info is already present */ + loglog(RC_DUPNAME, " duplicate ca information in record \"%s\" found," + "ignoring \"%s\"", ca->name, msg->name); + free_x509cert(cacert); + return; + } + + plog("added ca description \"%s\"", msg->name); + + /* create and initialize new ca_info record */ + ca = alloc_thing(ca_info_t, "ca info"); + *ca = empty_ca_info; + + /* name */ + ca->name = clone_str(msg->name, "ca name"); + + /* authName */ + clonetochunk(ca->authName, cacert->subject.ptr + , cacert->subject.len, "authName"); + dntoa(buf, BUF_LEN, ca->authName); + DBG(DBG_CONTROL, + DBG_log("authname: '%s'", buf) + ) + + /* authSerialNumber */ + clonetochunk(ca->authKeySerialNumber, cacert->serialNumber.ptr + , cacert->serialNumber.len, "authKeySerialNumber"); + + /* authKeyID */ + if (cacert->subjectKeyID.ptr != NULL) + { + clonetochunk(ca->authKeyID, cacert->subjectKeyID.ptr + , cacert->subjectKeyID.len, "authKeyID"); + datatot(cacert->subjectKeyID.ptr, cacert->subjectKeyID.len, ':' + , buf, BUF_LEN); + DBG(DBG_CONTROL | DBG_PARSING , + DBG_log("authkey: %s", buf) + ) + } + + /* ldaphost */ + ca->ldaphost = clone_str(msg->ldaphost, "ldaphost"); + + /* ldapbase */ + ca->ldapbase = clone_str(msg->ldapbase, "ldapbase"); + + /* ocspuri */ + if (msg->ocspuri != NULL) + { + if (strncasecmp(msg->ocspuri, "http", 4) == 0) + ca->ocspuri = clone_str(msg->ocspuri, "ocspuri"); + else + plog(" ignoring ocspuri with unkown protocol"); + } + + /* crluri2*/ + if (msg->crluri2 != NULL) + { + generalName_t gn = + { NULL, GN_URI, {msg->crluri2, strlen(msg->crluri2)} }; + + add_distribution_points(&gn, &ca->crluri); + } + + /* crluri */ + if (msg->crluri != NULL) + { + generalName_t gn = + { NULL, GN_URI, {msg->crluri, strlen(msg->crluri)} }; + + add_distribution_points(&gn, &ca->crluri); + } + + /* strictrlpolicy */ + ca->strictcrlpolicy = msg->whack_strict; + + /* insert ca_info record into the chained list */ + lock_ca_info_list("add_ca_info"); + + ca->next = ca_infos; + ca_infos = ca; + ca->installed = time(NULL); + + unlock_ca_info_list("add_ca_info"); + + /* add cacert to list of authcerts */ + if (!cached_cert) + { + if (add_authcert(cacert, AUTH_CA) && sc != NULL) + { + if (sc->last_cert.type == CERT_X509_SIGNATURE) + sc->last_cert.u.x509->count--; + sc->last_cert = cert; + share_cert(sc->last_cert); + } + } + if (sc != NULL) + time(&sc->last_load); + } +} + +/* + * list all ca_info records in the chained list + */ +void +list_ca_infos(bool utc) +{ + ca_info_t *ca = ca_infos; + + if (ca != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of X.509 CA Information Records:"); + whack_log(RC_COMMENT, " "); + } + + while (ca != NULL) + { + u_char buf[BUF_LEN]; + + /* strictpolicy per CA not supported yet + * + whack_log(RC_COMMENT, "%s, \"%s\", strictcrlpolicy: %s" + , timetoa(&ca->installed, utc), ca->name + , ca->strictcrlpolicy? "yes":"no"); + */ + whack_log(RC_COMMENT, "%s, \"%s\"", timetoa(&ca->installed, utc), ca->name); + dntoa(buf, BUF_LEN, ca->authName); + whack_log(RC_COMMENT, " authname: '%s'", buf); + if (ca->ldaphost != NULL) + whack_log(RC_COMMENT, " ldaphost: '%s'", ca->ldaphost); + if (ca->ldapbase != NULL) + whack_log(RC_COMMENT, " ldapbase: '%s'", ca->ldapbase); + if (ca->ocspuri != NULL) + whack_log(RC_COMMENT, " ocspuri: '%s'", ca->ocspuri); + + list_distribution_points(ca->crluri); + + if (ca->authKeyID.ptr != NULL) + { + datatot(ca->authKeyID.ptr, ca->authKeyID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authkey: %s", buf); + } + if (ca->authKeySerialNumber.ptr != NULL) + { + datatot(ca->authKeySerialNumber.ptr, ca->authKeySerialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " aserial: %s", buf); + } + ca = ca->next; + } +} + + diff --git a/src/pluto/ca.h b/src/pluto/ca.h new file mode 100644 index 000000000..8d4602dc6 --- /dev/null +++ b/src/pluto/ca.h @@ -0,0 +1,70 @@ +/* Certification Authority (CA) support for IKE authentication + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ca.h,v 1.5 2005/12/25 12:28:40 as Exp $ + */ + +#ifndef _CA_H +#define _CA_H + +#include "x509.h" +#include "whack.h" + +#define MAX_CA_PATH_LEN 7 + +/* authority flags */ + +#define AUTH_NONE 0x00 /* no authorities */ +#define AUTH_CA 0x01 /* certification authority */ +#define AUTH_AA 0x02 /* authorization authority */ +#define AUTH_OCSP 0x04 /* ocsp signing authority */ + +/* CA info structures */ + +typedef struct ca_info ca_info_t; + +struct ca_info { + ca_info_t *next; + char *name; + time_t installed; + chunk_t authName; + chunk_t authKeyID; + chunk_t authKeySerialNumber; + char *ldaphost; + char *ldapbase; + char *ocspuri; + generalName_t *crluri; + bool strictcrlpolicy; +}; + +extern bool trusted_ca(chunk_t a, chunk_t b, int *pathlen); +extern bool match_requested_ca(generalName_t *requested_ca + , chunk_t our_ca, int *our_pathlen); +extern x509cert_t* get_authcert(chunk_t subject, chunk_t serial, chunk_t keyid + , u_char auth_flags); +extern void load_authcerts(const char *type, const char *path + , u_char auth_flags); +extern bool add_authcert(x509cert_t *cert, u_char auth_flags); +extern void free_authcerts(void); +extern void list_authcerts(const char *caption, u_char auth_flags, bool utc); +extern bool trust_authcert_candidate(const x509cert_t *cert + , const x509cert_t *alt_chain); +extern ca_info_t* get_ca_info(chunk_t name, chunk_t serial, chunk_t keyid); +extern bool find_ca_info_by_name(const char *name, bool delete); +extern void add_ca_info(const whack_message_t *msg); +extern void delete_ca_info(const char *name); +extern void free_ca_infos(void); +extern void list_ca_infos(bool utc); + +#endif /* _CA_H */ + diff --git a/src/pluto/certs.c b/src/pluto/certs.c new file mode 100644 index 000000000..779646a98 --- /dev/null +++ b/src/pluto/certs.c @@ -0,0 +1,287 @@ +/* Certificate support for IKE authentication + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: certs.c,v 1.8 2005/11/06 22:55:41 as Exp $ + */ + +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "asn1.h" +#include "id.h" +#include "x509.h" +#include "pgp.h" +#include "pem.h" +#include "certs.h" +#include "pkcs1.h" + +/* + * used for initializatin of certs + */ +const cert_t empty_cert = {CERT_NONE, {NULL}}; + +/* + * extracts the certificate to be sent to the peer + */ +chunk_t +get_mycert(cert_t cert) +{ + switch (cert.type) + { + case CERT_PGP: + return cert.u.pgp->certificate; + case CERT_X509_SIGNATURE: + return cert.u.x509->certificate; + default: + return empty_chunk; + } +} + +/* load a coded key or certificate file with autodetection + * of binary DER or base64 PEM ASN.1 formats and armored PGP format + */ +bool +load_coded_file(const char *filename, prompt_pass_t *pass, const char *type +, chunk_t *blob, bool *pgp) +{ + err_t ugh = NULL; + + FILE *fd = fopen(filename, "r"); + + if (fd) + { + int bytes; + fseek(fd, 0, SEEK_END ); + blob->len = ftell(fd); + rewind(fd); + blob->ptr = alloc_bytes(blob->len, type); + bytes = fread(blob->ptr, 1, blob->len, fd); + fclose(fd); + plog(" loaded %s file '%s' (%d bytes)", type, filename, bytes); + + *pgp = FALSE; + + /* try DER format */ + if (is_asn1(*blob)) + { + DBG(DBG_PARSING, + DBG_log(" file coded in DER format"); + ) + return TRUE; + } + + /* try PEM format */ + ugh = pemtobin(blob, pass, filename, pgp); + + if (ugh == NULL) + { + if (*pgp) + { + DBG(DBG_PARSING, + DBG_log(" file coded in armored PGP format"); + ) + return TRUE; + } + if (is_asn1(*blob)) + { + DBG(DBG_PARSING, + DBG_log(" file coded in PEM format"); + ) + return TRUE; + } + ugh = "file coded in unknown format, discarded"; + } + + /* a conversion error has occured */ + plog(" %s", ugh); + pfree(blob->ptr); + *blob = empty_chunk; + } + else + { + plog(" could not open %s file '%s'", type, filename); + } + return FALSE; +} + +/* + * Loads a PKCS#1 or PGP private RSA key file + */ +err_t +load_rsa_private_key(const char* filename, prompt_pass_t *pass +, RSA_private_key_t *key) +{ + err_t ugh = NULL; + bool pgp = FALSE; + chunk_t blob = empty_chunk; + + const char *path = concatenate_paths(PRIVATE_KEY_PATH, filename); + + if (load_coded_file(path, pass, "private key", &blob, &pgp)) + { + if (pgp) + { + if (!parse_pgp(blob, NULL, key)) + ugh = "syntax error in PGP private key file"; + } + else + { + if (!pkcs1_parse_private_key(blob, key)) + ugh = "syntax error in PKCS#1 private key file"; + } + pfree(blob.ptr); + } + else + ugh = "error loading RSA private key file"; + + return ugh; +} +/* + * Loads a X.509 or OpenPGP certificate + */ +bool +load_cert(const char *filename, const char *label, cert_t *cert) +{ + bool pgp = FALSE; + chunk_t blob = empty_chunk; + + /* initialize cert struct */ + cert->type = CERT_NONE; + cert->u.x509 = NULL; + + if (load_coded_file(filename, NULL, label, &blob, &pgp)) + { + if (pgp) + { + pgpcert_t *pgpcert = alloc_thing(pgpcert_t, "pgpcert"); + *pgpcert = empty_pgpcert; + if (parse_pgp(blob, pgpcert, NULL)) + { + cert->type = CERT_PGP; + cert->u.pgp = pgpcert; + return TRUE; + } + else + { + plog(" error in OpenPGP certificate"); + free_pgpcert(pgpcert); + return FALSE; + } + } + else + { + x509cert_t *x509cert = alloc_thing(x509cert_t, "x509cert"); + *x509cert = empty_x509cert; + if (parse_x509cert(blob, 0, x509cert)) + { + cert->type = CERT_X509_SIGNATURE; + cert->u.x509 = x509cert; + return TRUE; + } + else + { + plog(" error in X.509 certificate"); + free_x509cert(x509cert); + return FALSE; + } + } + } + return FALSE; +} + +/* + * Loads a host certificate + */ +bool +load_host_cert(const char *filename, cert_t *cert) +{ + const char *path = concatenate_paths(HOST_CERT_PATH, filename); + + return load_cert(path, "host cert", cert); +} + +/* + * Loads a CA certificate + */ +bool +load_ca_cert(const char *filename, cert_t *cert) +{ + const char *path = concatenate_paths(CA_CERT_PATH, filename); + + return load_cert(path, "CA cert", cert); +} + +/* + * establish equality of two certificates + */ +bool +same_cert(const cert_t *a, const cert_t *b) +{ + return a->type == b->type && a->u.x509 == b->u.x509; +} + +/* for each link pointing to the certif icate + " increase the count by one + */ +void +share_cert(cert_t cert) +{ + switch (cert.type) + { + case CERT_PGP: + share_pgpcert(cert.u.pgp); + break; + case CERT_X509_SIGNATURE: + share_x509cert(cert.u.x509); + break; + default: + break; + } +} + +/* release of a certificate decreases the count by one + " the certificate is freed when the counter reaches zero + */ +void +release_cert(cert_t cert) +{ + switch (cert.type) + { + case CERT_PGP: + release_pgpcert(cert.u.pgp); + break; + case CERT_X509_SIGNATURE: + release_x509cert(cert.u.x509); + break; + default: + break; + } +} + +/* + * list all X.509 and OpenPGP end certificates + */ +void +list_certs(bool utc) +{ + list_x509_end_certs(utc); + list_pgp_end_certs(utc); +} + diff --git a/src/pluto/certs.h b/src/pluto/certs.h new file mode 100644 index 000000000..ca5acd35a --- /dev/null +++ b/src/pluto/certs.h @@ -0,0 +1,80 @@ +/* Certificate support for IKE authentication + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: certs.h,v 1.7 2005/11/06 22:55:41 as Exp $ + */ + +#ifndef _CERTS_H +#define _CERTS_H + +#include "pkcs1.h" +#include "x509.h" +#include "pgp.h" + +/* path definitions for private keys, end certs, + * cacerts, attribute certs and crls + */ +#define PRIVATE_KEY_PATH IPSEC_CONFDIR "/ipsec.d/private" +#define HOST_CERT_PATH IPSEC_CONFDIR "/ipsec.d/certs" +#define CA_CERT_PATH IPSEC_CONFDIR "/ipsec.d/cacerts" +#define A_CERT_PATH IPSEC_CONFDIR "/ipsec.d/acerts" +#define AA_CERT_PATH IPSEC_CONFDIR "/ipsec.d/aacerts" +#define OCSP_CERT_PATH IPSEC_CONFDIR "/ipsec.d/ocspcerts" +#define CRL_PATH IPSEC_CONFDIR "/ipsec.d/crls" +#define REQ_PATH IPSEC_CONFDIR "/ipsec.d/reqs" + +/* advance warning of imminent expiry of + * cacerts, public keys, and crls + */ +#define CA_CERT_WARNING_INTERVAL 30 /* days */ +#define OCSP_CERT_WARNING_INTERVAL 30 /* days */ +#define PUBKEY_WARNING_INTERVAL 7 /* days */ +#define CRL_WARNING_INTERVAL 7 /* days */ +#define ACERT_WARNING_INTERVAL 1 /* day */ + +/* certificate access structure + * currently X.509 and OpenPGP certificates are supported + */ +typedef struct { + u_char type; + union { + x509cert_t *x509; + pgpcert_t *pgp; + } u; +} cert_t; + +/* used for initialization */ +extern const cert_t empty_cert; + +/* do not send certificate requests + * flag set in plutomain.c and used in ipsec_doi.c + */ +extern bool no_cr_send; + +extern err_t load_rsa_private_key(const char* filename, prompt_pass_t *pass + , RSA_private_key_t *key); +extern chunk_t get_mycert(cert_t cert); +extern bool load_coded_file(const char *filename, prompt_pass_t *pass + , const char *type, chunk_t *blob, bool *pgp); +extern bool load_cert(const char *filename, const char *label + , cert_t *cert); +extern bool load_host_cert(const char *filename, cert_t *cert); +extern bool load_ca_cert(const char *filename, cert_t *cert); +extern bool same_cert(const cert_t *a, const cert_t *b); +extern void share_cert(cert_t cert); +extern void release_cert(cert_t cert); +extern void list_certs(bool utc); + +#endif /* _CERTS_H */ + + diff --git a/src/pluto/connections.c b/src/pluto/connections.c new file mode 100644 index 000000000..0d02b979c --- /dev/null +++ b/src/pluto/connections.c @@ -0,0 +1,4406 @@ +/* information about connections between hosts and clients + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: connections.c,v 1.43 2006/04/29 18:16:02 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* missing from on old systems */ +#include + +#include +#include +#include "kameipsec.h" + +#include "constants.h" +#include "defs.h" +#include "id.h" +#include "x509.h" +#include "ca.h" +#include "crl.h" +#include "pgp.h" +#include "certs.h" +#include "ac.h" +#include "smartcard.h" +#include "fetch.h" +#include "connections.h" +#include "foodgroups.h" +#include "demux.h" +#include "state.h" +#include "timer.h" +#include "ipsec_doi.h" /* needs demux.h and state.h */ +#include "server.h" +#include "kernel.h" +#include "log.h" +#include "keys.h" +#include "adns.h" /* needs */ +#include "dnskey.h" /* needs keys.h and adns.h */ +#include "whack.h" +#include "alg_info.h" +#include "ike_alg.h" +#include "nat_traversal.h" +#include "virtual.h" + +static void flush_pending_by_connection(struct connection *c); /* forward */ + +static struct connection *connections = NULL; + +/* struct host_pair: a nexus of information about a pair of hosts. + * A host is an IP address, UDP port pair. This is a debatable choice: + * - should port be considered (no choice of port in standard)? + * - should ID be considered (hard because not always known)? + * - should IP address matter on our end (we don't know our end)? + * Only oriented connections are registered. + * Unoriented connections are kept on the unoriented_connections + * linked list (using hp_next). For them, host_pair is NULL. + */ + +struct host_pair { + struct { + ip_address addr; + u_int16_t port; /* host order */ + } me, him; + bool initial_connection_sent; + struct connection *connections; /* connections with this pair */ + struct pending *pending; /* awaiting Keying Channel */ + struct host_pair *next; +}; + +static struct host_pair *host_pairs = NULL; + +static struct connection *unoriented_connections = NULL; + +/* check to see that Ids of peers match */ +bool +same_peer_ids(const struct connection *c, const struct connection *d +, const struct id *his_id) +{ + return same_id(&c->spd.this.id, &d->spd.this.id) + && same_id(his_id == NULL? &c->spd.that.id : his_id, &d->spd.that.id); +} + +static struct host_pair * +find_host_pair(const ip_address *myaddr, u_int16_t myport +, const ip_address *hisaddr, u_int16_t hisport) +{ + struct host_pair *p, *prev; + + /* default hisaddr to an appropriate any */ + if (hisaddr == NULL) + hisaddr = aftoinfo(addrtypeof(myaddr))->any; + + if (nat_traversal_enabled) + { + /** + * port is not relevant in host_pair. with nat_traversal we + * always use pluto_port (500) + */ + myport = pluto_port; + hisport = pluto_port; + } + + for (prev = NULL, p = host_pairs; p != NULL; prev = p, p = p->next) + { + if (sameaddr(&p->me.addr, myaddr) && p->me.port == myport + && sameaddr(&p->him.addr, hisaddr) && p->him.port == hisport) + { + if (prev != NULL) + { + prev->next = p->next; /* remove p from list */ + p->next = host_pairs; /* and stick it on front */ + host_pairs = p; + } + break; + } + } + return p; +} + +/* find head of list of connections with this pair of hosts */ +static struct connection * +find_host_pair_connections(const ip_address *myaddr, u_int16_t myport +, const ip_address *hisaddr, u_int16_t hisport) +{ + struct host_pair *hp = find_host_pair(myaddr, myport, hisaddr, hisport); + + if (nat_traversal_enabled && hp && hisaddr) + { + struct connection *c; + + for (c = hp->connections; c != NULL; c = c->hp_next) + { + if (c->spd.this.host_port == myport && c->spd.that.host_port == hisport) + return c; + } + return NULL; + } + return hp == NULL? NULL : hp->connections; +} + +static void +connect_to_host_pair(struct connection *c) +{ + if (oriented(*c)) + { + struct host_pair *hp = find_host_pair(&c->spd.this.host_addr, c->spd.this.host_port + , &c->spd.that.host_addr, c->spd.that.host_port); + + if (hp == NULL) + { + /* no suitable host_pair -- build one */ + hp = alloc_thing(struct host_pair, "host_pair"); + hp->me.addr = c->spd.this.host_addr; + hp->him.addr = c->spd.that.host_addr; + hp->me.port = nat_traversal_enabled ? pluto_port : c->spd.this.host_port; + hp->him.port = nat_traversal_enabled ? pluto_port : c->spd.that.host_port; + hp->initial_connection_sent = FALSE; + hp->connections = NULL; + hp->pending = NULL; + hp->next = host_pairs; + host_pairs = hp; + } + c->host_pair = hp; + c->hp_next = hp->connections; + hp->connections = c; + } + else + { + /* since this connection isn't oriented, we place it + * in the unoriented_connections list instead. + */ + c->host_pair = NULL; + c->hp_next = unoriented_connections; + unoriented_connections = c; + } +} + +/* find a connection by name. + * If strict, don't accept a CK_INSTANCE. + * Move the winner (if any) to the front. + * If none is found, and strict, a diagnostic is logged to whack. + */ +struct connection * +con_by_name(const char *nm, bool strict) +{ + struct connection *p, *prev; + + for (prev = NULL, p = connections; ; prev = p, p = p->ac_next) + { + if (p == NULL) + { + if (strict) + whack_log(RC_UNKNOWN_NAME + , "no connection named \"%s\"", nm); + break; + } + if (streq(p->name, nm) + && (!strict || p->kind != CK_INSTANCE)) + { + if (prev != NULL) + { + prev->ac_next = p->ac_next; /* remove p from list */ + p->ac_next = connections; /* and stick it on front */ + connections = p; + } + break; + } + } + return p; +} + +void +release_connection(struct connection *c, bool relations) +{ + if (c->kind == CK_INSTANCE) + { + /* This does everything we need. + * Note that we will be called recursively by delete_connection, + * but kind will be CK_GOING_AWAY. + */ + delete_connection(c, relations); + } + else + { + flush_pending_by_connection(c); + delete_states_by_connection(c, relations); + unroute_connection(c); + } +} + +/* Delete a connection */ + +#define list_rm(etype, enext, e, ehead) { \ + etype **ep; \ + for (ep = &(ehead); *ep != (e); ep = &(*ep)->enext) \ + passert(*ep != NULL); /* we must not come up empty-handed */ \ + *ep = (e)->enext; \ + } + + +void +delete_connection(struct connection *c, bool relations) +{ + struct connection *old_cur_connection + = cur_connection == c? NULL : cur_connection; +#ifdef DEBUG + lset_t old_cur_debugging = cur_debugging; +#endif + + set_cur_connection(c); + + /* Must be careful to avoid circularity: + * we mark c as going away so it won't get deleted recursively. + */ + passert(c->kind != CK_GOING_AWAY); + if (c->kind == CK_INSTANCE) + { + plog("deleting connection \"%s\" instance with peer %s {isakmp=#%lu/ipsec=#%lu}" + , c->name + , ip_str(&c->spd.that.host_addr) + , c->newest_isakmp_sa, c->newest_ipsec_sa); + c->kind = CK_GOING_AWAY; + } + else + { + plog("deleting connection"); + } + release_connection(c, relations); /* won't delete c */ + + if (c->kind == CK_GROUP) + delete_group(c); + + /* free up any logging resources */ + perpeer_logfree(c); + + /* find and delete c from connections list */ + list_rm(struct connection, ac_next, c, connections); + cur_connection = old_cur_connection; + + /* find and delete c from the host pair list */ + if (c->host_pair == NULL) + { + if (c->ikev1) + list_rm(struct connection, hp_next, c, unoriented_connections); + } + else + { + struct host_pair *hp = c->host_pair; + + list_rm(struct connection, hp_next, c, hp->connections); + c->host_pair = NULL; /* redundant, but safe */ + + /* if there are no more connections with this host_pair + * and we haven't even made an initial contact, let's delete + * this guy in case we were created by an attempted DOS attack. + */ + if (hp->connections == NULL + && !hp->initial_connection_sent) + { + passert(hp->pending == NULL); /* ??? must deal with this! */ + list_rm(struct host_pair, next, hp, host_pairs); + pfree(hp); + } + } + + if (c->kind != CK_GOING_AWAY) + pfreeany(c->spd.that.virt); + +#ifdef DEBUG + cur_debugging = old_cur_debugging; +#endif + pfreeany(c->name); + free_id_content(&c->spd.this.id); + pfreeany(c->spd.this.updown); + freeanychunk(c->spd.this.ca); + free_ietfAttrList(c->spd.this.groups); + free_id_content(&c->spd.that.id); + pfreeany(c->spd.that.updown); + freeanychunk(c->spd.that.ca); + free_ietfAttrList(c->spd.that.groups); + free_generalNames(c->requested_ca, TRUE); + gw_delref(&c->gw_info); + + lock_certs_and_keys("delete_connection"); + release_cert(c->spd.this.cert); + scx_release(c->spd.this.sc); + release_cert(c->spd.that.cert); + scx_release(c->spd.that.sc); + unlock_certs_and_keys("delete_connection"); + + alg_info_delref((struct alg_info **)&c->alg_info_esp); + alg_info_delref((struct alg_info **)&c->alg_info_ike); + + pfree(c); +} + +/* Delete connections with the specified name */ +void +delete_connections_by_name(const char *name, bool strict) +{ + struct connection *c = con_by_name(name, strict); + + for (; c != NULL; c = con_by_name(name, FALSE)) + delete_connection(c, FALSE); +} + +void +delete_every_connection(void) +{ + while (connections != NULL) + delete_connection(connections, TRUE); +} + +void +release_dead_interfaces(void) +{ + struct host_pair *hp; + + for (hp = host_pairs; hp != NULL; hp = hp->next) + { + struct connection **pp + , *p; + + for (pp = &hp->connections; (p = *pp) != NULL; ) + { + if (p->interface->change == IFN_DELETE) + { + /* this connection's interface is going away */ + enum connection_kind k = p->kind; + + release_connection(p, TRUE); + + if (k <= CK_PERMANENT) + { + /* The connection should have survived release: + * move it to the unoriented_connections list. + */ + passert(p == *pp); + + p->interface = NULL; + + *pp = p->hp_next; /* advance *pp */ + p->host_pair = NULL; + p->hp_next = unoriented_connections; + unoriented_connections = p; + } + else + { + /* The connection should have vanished, + * but the previous connection remains. + */ + passert(p != *pp); + } + } + else + { + pp = &p->hp_next; /* advance pp */ + } + } + } +} + +/* adjust orientations of connections to reflect newly added interfaces */ +void +check_orientations(void) +{ + /* try to orient all the unoriented connections */ + { + struct connection *c = unoriented_connections; + + unoriented_connections = NULL; + + while (c != NULL) + { + struct connection *nxt = c->hp_next; + + (void)orient(c); + connect_to_host_pair(c); + c = nxt; + } + } + + /* Check that no oriented connection has become double-oriented. + * In other words, the far side must not match one of our new interfaces. + */ + { + struct iface *i; + + for (i = interfaces; i != NULL; i = i->next) + { + if (i->change == IFN_ADD) + { + struct host_pair *hp; + + for (hp = host_pairs; hp != NULL; hp = hp->next) + { + if (sameaddr(&hp->him.addr, &i->addr) + && (!no_klips || hp->him.port == pluto_port)) + { + /* bad news: the whole chain of connections + * hanging off this host pair has both sides + * matching an interface. + * We'll get rid of them, using orient and + * connect_to_host_pair. But we'll be lazy + * and not ditch the host_pair itself (the + * cost of leaving it is slight and cannot + * be induced by a foe). + */ + struct connection *c = hp->connections; + + hp->connections = NULL; + while (c != NULL) + { + struct connection *nxt = c->hp_next; + + c->interface = NULL; + (void)orient(c); + connect_to_host_pair(c); + c = nxt; + } + } + } + } + } + } +} + +static err_t +default_end(struct end *e, ip_address *dflt_nexthop) +{ + err_t ugh = NULL; + const struct af_info *afi = aftoinfo(addrtypeof(&e->host_addr)); + + if (afi == NULL) + return "unknown address family in default_end"; + + /* default ID to IP (but only if not NO_IP -- WildCard) */ + if (e->id.kind == ID_NONE && !isanyaddr(&e->host_addr)) + { + e->id.kind = afi->id_addr; + e->id.ip_addr = e->host_addr; + e->has_id_wildcards = FALSE; + } + + /* default nexthop to other side */ + if (isanyaddr(&e->host_nexthop)) + e->host_nexthop = *dflt_nexthop; + + /* default client to subnet containing only self + * XXX This may mean that the client's address family doesn't match + * tunnel_addr_family. + */ + if (!e->has_client) + ugh = addrtosubnet(&e->host_addr, &e->client); + + return ugh; +} + +/* Format the topology of a connection end, leaving out defaults. + * Largest left end looks like: client === host : port [ host_id ] --- hop + * Note: if that==NULL, skip nexthop + * Returns strlen of formated result (length excludes NUL at end). + */ +size_t +format_end(char *buf +, size_t buf_len +, const struct end *this +, const struct end *that +, bool is_left +, lset_t policy) +{ + char client[SUBNETTOT_BUF]; + const char *client_sep = ""; + char protoport[sizeof(":255/65535")]; + const char *host = NULL; + char host_space[ADDRTOT_BUF]; + char host_port[sizeof(":65535")]; + char host_id[BUF_LEN + 2]; + char hop[ADDRTOT_BUF]; + const char *hop_sep = ""; + const char *open_brackets = ""; + const char *close_brackets = ""; + + if (isanyaddr(&this->host_addr)) + { + switch (policy & (POLICY_GROUP | POLICY_OPPO)) + { + case POLICY_GROUP: + host = "%group"; + break; + case POLICY_OPPO: + host = "%opportunistic"; + break; + case POLICY_GROUP | POLICY_OPPO: + host = "%opportunisticgroup"; + break; + default: + host = "%any"; + break; + } + } + + client[0] = '\0'; + + if (is_virtual_end(this) && isanyaddr(&this->host_addr)) + { + host = "%virtual"; + } + + /* [client===] */ + if (this->has_client) + { + ip_address client_net, client_mask; + + networkof(&this->client, &client_net); + maskof(&this->client, &client_mask); + client_sep = "==="; + + /* {client_subnet_wildcard} */ + if (this->has_client_wildcard) + { + open_brackets = "{"; + close_brackets = "}"; + } + + if (isanyaddr(&client_net) && isanyaddr(&client_mask) + && (policy & (POLICY_GROUP | POLICY_OPPO))) + client_sep = ""; /* boring case */ + else if (subnetisnone(&this->client)) + strcpy(client, "?"); + else + subnettot(&this->client, 0, client, sizeof(client)); + } + else if (this->modecfg && isanyaddr(&this->host_srcip)) + { + /* we are mode config client */ + client_sep = "==="; + strcpy(client, "%modecfg"); + } + + /* host */ + if (host == NULL) + { + addrtot(&this->host_addr, 0, host_space, sizeof(host_space)); + host = host_space; + } + + host_port[0] = '\0'; + if (this->host_port != IKE_UDP_PORT) + snprintf(host_port, sizeof(host_port), ":%u" + , this->host_port); + + /* payload portocol and port */ + protoport[0] = '\0'; + if (this->has_port_wildcard) + snprintf(protoport, sizeof(protoport), ":%u/%%any", this->protocol); + else if (this->port || this->protocol) + snprintf(protoport, sizeof(protoport), ":%u/%u", this->protocol + , this->port); + + /* id, if different from host */ + host_id[0] = '\0'; + if (this->id.kind == ID_MYID) + { + strcpy(host_id, "[%myid]"); + } + else if (!(this->id.kind == ID_NONE + || (id_is_ipaddr(&this->id) && sameaddr(&this->id.ip_addr, &this->host_addr)))) + { + int len = idtoa(&this->id, host_id+1, sizeof(host_id)-2); + + host_id[0] = '['; + strcpy(&host_id[len < 0? (ptrdiff_t)sizeof(host_id)-2 : 1 + len], "]"); + } + + /* [---hop] */ + hop[0] = '\0'; + hop_sep = ""; + if (that != NULL && !sameaddr(&this->host_nexthop, &that->host_addr)) + { + addrtot(&this->host_nexthop, 0, hop, sizeof(hop)); + hop_sep = "---"; + } + + if (is_left) + snprintf(buf, buf_len, "%s%s%s%s%s%s%s%s%s%s" + , open_brackets, client, close_brackets + , client_sep, host, host_port, host_id + , protoport, hop_sep, hop); + else + snprintf(buf, buf_len, "%s%s%s%s%s%s%s%s%s%s" + , hop, hop_sep, host, host_port, host_id + , protoport, client_sep + , open_brackets, client, close_brackets); + return strlen(buf); +} + +/* format topology of a connection. + * Two symmetric ends separated by ... + */ +#define CONNECTION_BUF (2 * (END_BUF - 1) + 4) + +static size_t +format_connection(char *buf, size_t buf_len + , const struct connection *c + , struct spd_route *sr) +{ + size_t w = format_end(buf, buf_len, &sr->this, &sr->that, TRUE, LEMPTY); + + w += snprintf(buf + w, buf_len - w, "..."); + return w + format_end(buf + w, buf_len - w, &sr->that, &sr->this, FALSE, c->policy); +} + +static void +unshare_connection_strings(struct connection *c) +{ + c->name = clone_str(c->name, "connection name"); + + unshare_id_content(&c->spd.this.id); + c->spd.this.updown = clone_str(c->spd.this.updown, "updown"); + scx_share(c->spd.this.sc); + share_cert(c->spd.this.cert); + if (c->spd.this.ca.ptr != NULL) + clonetochunk(c->spd.this.ca, c->spd.this.ca.ptr, c->spd.this.ca.len, "ca string"); + + unshare_id_content(&c->spd.that.id); + c->spd.that.updown = clone_str(c->spd.that.updown, "updown"); + scx_share(c->spd.that.sc); + share_cert(c->spd.that.cert); + if (c->spd.that.ca.ptr != NULL) + clonetochunk(c->spd.that.ca, c->spd.that.ca.ptr, c->spd.that.ca.len, "ca string"); + + /* increment references to algo's */ + alg_info_addref((struct alg_info *)c->alg_info_esp); + alg_info_addref((struct alg_info *)c->alg_info_ike); +} + +static void +load_end_certificate(const char *filename, struct end *dst) +{ + time_t valid_until; + cert_t cert; + bool valid_cert = FALSE; + bool cached_cert = FALSE; + + /* initialize end certificate */ + dst->cert.type = CERT_NONE; + dst->cert.u.x509 = NULL; + + /* initialize smartcard info record */ + dst->sc = NULL; + + if (filename != NULL) + { + if (scx_on_smartcard(filename)) + { + /* load cert from smartcard */ + valid_cert = scx_load_cert(filename, &dst->sc, &cert, &cached_cert); + } + else + { + /* load cert from file */ + valid_cert = load_host_cert(filename, &cert); + } + } + + if (valid_cert) + { + err_t ugh = NULL; + + switch (cert.type) + { + case CERT_PGP: + select_pgpcert_id(cert.u.pgp, &dst->id); + + if (cached_cert) + dst->cert = cert; + else + { + valid_until = cert.u.pgp->until; + add_pgp_public_key(cert.u.pgp, cert.u.pgp->until, DAL_LOCAL); + dst->cert.type = cert.type; + dst->cert.u.pgp = add_pgpcert(cert.u.pgp); + } + break; + case CERT_X509_SIGNATURE: + select_x509cert_id(cert.u.x509, &dst->id); + + if (cached_cert) + dst->cert = cert; + else + { + /* check validity of cert */ + valid_until = cert.u.x509->notAfter; + ugh = check_validity(cert.u.x509, &valid_until); + if (ugh != NULL) + { + plog(" %s", ugh); + free_x509cert(cert.u.x509); + break; + } + + DBG(DBG_CONTROL, + DBG_log("certificate is valid") + ) + add_x509_public_key(cert.u.x509, valid_until, DAL_LOCAL); + dst->cert.type = cert.type; + dst->cert.u.x509 = add_x509cert(cert.u.x509); + } + /* if no CA is defined, use issuer as default */ + if (dst->ca.ptr == NULL) + dst->ca = dst->cert.u.x509->issuer; + break; + default: + break; + } + + /* cache the certificate that was last retrieved from the smartcard */ + if (dst->sc != NULL) + { + if (!same_cert(&dst->sc->last_cert, &dst->cert)) + { + lock_certs_and_keys("load_end_certificates"); + release_cert(dst->sc->last_cert); + dst->sc->last_cert = dst->cert; + share_cert(dst->cert); + unlock_certs_and_keys("load_end_certificates"); + } + time(&dst->sc->last_load); + } + } +} + +static bool +extract_end(struct end *dst, const whack_end_t *src, const char *which) +{ + bool same_ca = FALSE; + + /* decode id, if any */ + if (src->id == NULL) + { + dst->id.kind = ID_NONE; + } + else + { + err_t ugh = atoid(src->id, &dst->id, TRUE); + + if (ugh != NULL) + { + loglog(RC_BADID, "bad %s --id: %s (ignored)", which, ugh); + dst->id = empty_id; /* ignore bad one */ + } + } + + dst->ca = empty_chunk; + + /* decode CA distinguished name, if any */ + if (src->ca != NULL) + { + if streq(src->ca, "%same") + same_ca = TRUE; + else if (!streq(src->ca, "%any")) + { + err_t ugh; + + dst->ca.ptr = temporary_cyclic_buffer(); + ugh = atodn(src->ca, &dst->ca); + if (ugh != NULL) + { + plog("bad CA string '%s': %s (ignored)", src->ca, ugh); + dst->ca = empty_chunk; + } + } + } + + /* load local end certificate and extract ID, if any */ + load_end_certificate(src->cert, dst); + + /* does id has wildcards? */ + dst->has_id_wildcards = id_count_wildcards(&dst->id) > 0; + + /* decode group attributes, if any */ + decode_groups(src->groups, &dst->groups); + + /* the rest is simple copying of corresponding fields */ + dst->host_addr = src->host_addr; + dst->host_nexthop = src->host_nexthop; + dst->host_srcip = src->host_srcip; + dst->has_natip = src->has_natip; + dst->client = src->client; + dst->protocol = src->protocol; + dst->port = src->port; + dst->has_port_wildcard = src->has_port_wildcard; + dst->key_from_DNS_on_demand = src->key_from_DNS_on_demand; + dst->has_client = src->has_client; + dst->has_client_wildcard = src->has_client_wildcard; + dst->modecfg = src->modecfg; + dst->hostaccess = src->hostaccess; + dst->sendcert = src->sendcert; + dst->updown = src->updown; + dst->host_port = src->host_port; + + /* if host sourceip is defined but no client is present + * behind the host then set client to sourceip/32 + */ + if (addrbytesptr(&dst->host_srcip, NULL) + && !isanyaddr(&dst->host_srcip) + && !dst->has_natip + && !dst->has_client) + { + err_t ugh = addrtosubnet(&dst->host_srcip, &dst->client); + + if (ugh != NULL) + plog("could not assign host sourceip to client subnet"); + else + dst->has_client = TRUE; + } + return same_ca; +} + +static bool +check_connection_end(const whack_end_t *this, const whack_end_t *that +, const whack_message_t *wm) +{ + if (wm->addr_family != addrtypeof(&this->host_addr) + || wm->addr_family != addrtypeof(&this->host_nexthop) + || (this->has_client? wm->tunnel_addr_family : wm->addr_family) + != subnettypeof(&this->client) + || subnettypeof(&this->client) != subnettypeof(&that->client)) + { + /* this should have been diagnosed by whack, so we need not be clear + * !!! overloaded use of RC_CLASH + */ + loglog(RC_CLASH, "address family inconsistency in connection"); + return FALSE; + } + + if (isanyaddr(&that->host_addr)) + { + /* other side is wildcard: we must check if other conditions met */ + if (isanyaddr(&this->host_addr)) + { + loglog(RC_ORIENT, "connection must specify host IP address for our side"); + return FALSE; + } + } + + if (this->virt && (!isanyaddr(&this->host_addr) || this->has_client)) + { + loglog(RC_CLASH, + "virtual IP must only be used with %%any and without client"); + return FALSE; + } + + return TRUE; /* happy */ +} + +struct connection * +find_connection_by_reqid(uint32_t reqid) +{ + struct connection *c; + + reqid &= ~3; + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->spd.reqid == reqid) + return c; + } + + return NULL; +} + +static uint32_t +gen_reqid(void) +{ + uint32_t start; + static uint32_t reqid = IPSEC_MANUAL_REQID_MAX & ~3; + + start = reqid; + do { + reqid += 4; + if (reqid == 0) + reqid = (IPSEC_MANUAL_REQID_MAX & ~3) + 4; + if (!find_connection_by_reqid(reqid)) + return reqid; + } while (reqid != start); + + exit_log("unable to allocate reqid"); +} + +void +add_connection(const whack_message_t *wm) +{ + if (con_by_name(wm->name, FALSE) != NULL) + { + loglog(RC_DUPNAME, "attempt to redefine connection \"%s\"", wm->name); + } + else if (wm->right.protocol != wm->left.protocol) + { + /* this should haven been diagnosed by whack + * !!! overloaded use of RC_CLASH + */ + loglog(RC_CLASH, "the protocol must be the same for leftport and rightport"); + } + else if (check_connection_end(&wm->right, &wm->left, wm) + && check_connection_end(&wm->left, &wm->right, wm)) + { + bool same_rightca, same_leftca; + struct connection *c = alloc_thing(struct connection, "struct connection"); + + c->name = wm->name; + c->ikev1 = wm->ikev1; + c->policy = wm->policy; + + if ((c->policy & POLICY_COMPRESS) && !can_do_IPcomp) + loglog(RC_COMMENT + , "ignoring --compress in \"%s\" because KLIPS is not configured to do IPCOMP" + , c->name); + + if (wm->esp) + { + const char *ugh; + + DBG(DBG_CONTROL, + DBG_log("from whack: got --esp=%s", wm->esp ? wm->esp: "NULL") + ) + c->alg_info_esp= alg_info_esp_create_from_str(wm->esp? wm->esp : "", &ugh); + + DBG(DBG_CRYPT|DBG_CONTROL, + static char buf[256]=""; + + if (c->alg_info_esp) + alg_info_snprint(buf, sizeof(buf) + ,(struct alg_info *)c->alg_info_esp); + DBG_log("esp string values: %s", buf); + ) + if (c->alg_info_esp) + { + if (c->alg_info_esp->alg_info_cnt==0) + loglog(RC_LOG_SERIOUS + , "got 0 transforms for esp=\"%s\"", wm->esp); + } + else + { + loglog(RC_LOG_SERIOUS + , "esp string error: %s", ugh? ugh : "Unknown"); + } + } + + if (wm->ike) + { + const char *ugh; + + DBG(DBG_CONTROL, + DBG_log("from whack: got --ike=%s", wm->ike ? wm->ike: "NULL") + ) + c->alg_info_ike= alg_info_ike_create_from_str(wm->ike? wm->ike : "", &ugh); + + DBG(DBG_CRYPT|DBG_CONTROL, + static char buf[256]=""; + + if (c->alg_info_ike) + alg_info_snprint(buf, sizeof(buf) + , (struct alg_info *)c->alg_info_ike); + DBG_log("ike string values: %s", buf); + ) + if (c->alg_info_ike) + { + if (c->alg_info_ike->alg_info_cnt==0) + loglog(RC_LOG_SERIOUS + , "got 0 transforms for ike=\"%s\"", wm->ike); + } + else + { + loglog(RC_LOG_SERIOUS + , "ike string error: %s", ugh? ugh : "Unknown"); + } + } + + c->sa_ike_life_seconds = wm->sa_ike_life_seconds; + c->sa_ipsec_life_seconds = wm->sa_ipsec_life_seconds; + c->sa_rekey_margin = wm->sa_rekey_margin; + c->sa_rekey_fuzz = wm->sa_rekey_fuzz; + c->sa_keying_tries = wm->sa_keying_tries; + + /* RFC 3706 DPD */ + c->dpd_delay = wm->dpd_delay; + c->dpd_timeout = wm->dpd_timeout; + c->dpd_action = wm->dpd_action; + + c->addr_family = wm->addr_family; + c->tunnel_addr_family = wm->tunnel_addr_family; + + c->requested_ca = NULL; + + same_leftca = extract_end(&c->spd.this, &wm->left, "left"); + same_rightca = extract_end(&c->spd.that, &wm->right, "right"); + + if (same_rightca) + c->spd.that.ca = c->spd.this.ca; + else if (same_leftca) + c->spd.this.ca = c->spd.that.ca; + + default_end(&c->spd.this, &c->spd.that.host_addr); + default_end(&c->spd.that, &c->spd.this.host_addr); + + /* force any wildcard host IP address, any wildcard subnet + * or any wildcard ID to that end + */ + if (isanyaddr(&c->spd.this.host_addr) || c->spd.this.has_client_wildcard + || c->spd.this.has_port_wildcard || c->spd.this.has_id_wildcards) + { + struct end t = c->spd.this; + + c->spd.this = c->spd.that; + c->spd.that = t; + } + + c->spd.next = NULL; + c->spd.reqid = gen_reqid(); + + /* set internal fields */ + c->instance_serial = 0; + c->ac_next = connections; + connections = c; + c->interface = NULL; + c->spd.routing = RT_UNROUTED; + c->newest_isakmp_sa = SOS_NOBODY; + c->newest_ipsec_sa = SOS_NOBODY; + c->spd.eroute_owner = SOS_NOBODY; + + if (c->policy & POLICY_GROUP) + { + c->kind = CK_GROUP; + add_group(c); + } + else if ((isanyaddr(&c->spd.that.host_addr) && !NEVER_NEGOTIATE(c->policy)) + || c->spd.that.has_client_wildcard || c->spd.that.has_port_wildcard + || c->spd.that.has_id_wildcards) + { + /* Opportunistic or Road Warrior or wildcard client subnet + * or wildcard ID */ + c->kind = CK_TEMPLATE; + } + else + { + c->kind = CK_PERMANENT; + } + set_policy_prio(c); /* must be after kind is set */ + +#ifdef DEBUG + c->extra_debugging = wm->debugging; +#endif + + c->gw_info = NULL; + + passert(!(wm->left.virt && wm->right.virt)); + if (wm->left.virt || wm->right.virt) + { + passert(isanyaddr(&c->spd.that.host_addr)); + c->spd.that.virt = create_virtual(c, + wm->left.virt ? wm->left.virt : wm->right.virt); + if (c->spd.that.virt) + c->spd.that.has_client = TRUE; + } + + unshare_connection_strings(c); + (void)orient(c); + + if (c->ikev1) + connect_to_host_pair(c); + + /* log all about this connection */ + plog("added connection description \"%s\"", c->name); + DBG(DBG_CONTROL, + char topo[CONNECTION_BUF]; + + (void) format_connection(topo, sizeof(topo), c, &c->spd); + + DBG_log("%s", topo); + + /* Make sure that address families can be correctly inferred + * from printed ends. + */ + passert(c->addr_family == addrtypeof(&c->spd.this.host_addr) + && c->addr_family == addrtypeof(&c->spd.this.host_nexthop) + && (c->spd.this.has_client? c->tunnel_addr_family : c->addr_family) + == subnettypeof(&c->spd.this.client) + + && c->addr_family == addrtypeof(&c->spd.that.host_addr) + && c->addr_family == addrtypeof(&c->spd.that.host_nexthop) + && (c->spd.that.has_client? c->tunnel_addr_family : c->addr_family) + == subnettypeof(&c->spd.that.client)); + + DBG_log("ike_life: %lus; ipsec_life: %lus; rekey_margin: %lus;" + " rekey_fuzz: %lu%%; keyingtries: %lu; policy: %s" + , (unsigned long) c->sa_ike_life_seconds + , (unsigned long) c->sa_ipsec_life_seconds + , (unsigned long) c->sa_rekey_margin + , (unsigned long) c->sa_rekey_fuzz + , (unsigned long) c->sa_keying_tries + , prettypolicy(c->policy)); + ); + } +} + +/* Derive a template connection from a group connection and target. + * Similar to instantiate(). Happens at whack --listen. + * Returns name of new connection. May be NULL. + * Caller is responsible for pfreeing. + */ +char * +add_group_instance(struct connection *group, const ip_subnet *target) +{ + char namebuf[100] + , targetbuf[SUBNETTOT_BUF]; + struct connection *t; + char *name = NULL; + + passert(group->kind == CK_GROUP); + passert(oriented(*group)); + + /* manufacture a unique name for this template */ + subnettot(target, 0, targetbuf, sizeof(targetbuf)); + snprintf(namebuf, sizeof(namebuf), "%s#%s", group->name, targetbuf); + + if (con_by_name(namebuf, FALSE) != NULL) + { + loglog(RC_DUPNAME, "group name + target yields duplicate name \"%s\"" + , namebuf); + } + else + { + t = clone_thing(*group, "group instance"); + t->name = namebuf; + unshare_connection_strings(t); + name = clone_str(t->name, "group instance name"); + t->spd.that.client = *target; + t->policy &= ~(POLICY_GROUP | POLICY_GROUTED); + t->kind = isanyaddr(&t->spd.that.host_addr) && !NEVER_NEGOTIATE(t->policy) + ? CK_TEMPLATE : CK_INSTANCE; + + /* reset log file info */ + t->log_file_name = NULL; + t->log_file = NULL; + t->log_file_err = FALSE; + + t->spd.reqid = gen_reqid(); + + if (t->spd.that.virt) + { + DBG_log("virtual_ip not supported in group instance"); + t->spd.that.virt = NULL; + } + + /* add to connections list */ + t->ac_next = connections; + connections = t; + + /* same host_pair as parent: stick after parent on list */ + group->hp_next = t; + + /* route if group is routed */ + if (group->policy & POLICY_GROUTED) + { + if (!trap_connection(t)) + whack_log(RC_ROUTE, "could not route"); + } + } + return name; +} + +/* an old target has disappeared for a group: delete instance */ +void +remove_group_instance(const struct connection *group USED_BY_DEBUG +, const char *name) +{ + passert(group->kind == CK_GROUP); + passert(oriented(*group)); + + delete_connections_by_name(name, FALSE); +} + +/* Common part of instantiating a Road Warrior or Opportunistic connection. + * his_id can be used to carry over an ID discovered in Phase 1. + * It must not disagree with the one in c, but if that is unspecified, + * the new connection will use his_id. + * If his_id is NULL, and c.that.id is uninstantiated (ID_NONE), the + * new connection will continue to have an uninstantiated that.id. + * Note: instantiation does not affect port numbers. + * + * Note that instantiate can only deal with a single SPD/eroute. + */ +static struct connection * +instantiate(struct connection *c, const ip_address *him +, u_int16_t his_port +, const struct id *his_id) +{ + struct connection *d; + int wildcards; + + passert(c->kind == CK_TEMPLATE); + passert(c->spd.next == NULL); + + c->instance_serial++; + d = clone_thing(*c, "temporary connection"); + if (his_id != NULL) + { + passert(match_id(his_id, &d->spd.that.id, &wildcards)); + d->spd.that.id = *his_id; + d->spd.that.has_id_wildcards = FALSE; + } + unshare_connection_strings(d); + unshare_ietfAttrList(&d->spd.this.groups); + unshare_ietfAttrList(&d->spd.that.groups); + d->kind = CK_INSTANCE; + + passert(oriented(*d)); + d->spd.that.host_addr = *him; + setportof(htons(c->spd.that.port), &d->spd.that.host_addr); + + if (his_port) d->spd.that.host_port = his_port; + + default_end(&d->spd.that, &d->spd.this.host_addr); + + /* We cannot guess what our next_hop should be, but if it was + * explicitly specified as 0.0.0.0, we set it to be him. + * (whack will not allow nexthop to be elided in RW case.) + */ + default_end(&d->spd.this, &d->spd.that.host_addr); + d->spd.next = NULL; + d->spd.reqid = gen_reqid(); + + /* set internal fields */ + d->ac_next = connections; + connections = d; + d->spd.routing = RT_UNROUTED; + d->newest_isakmp_sa = SOS_NOBODY; + d->newest_ipsec_sa = SOS_NOBODY; + d->spd.eroute_owner = SOS_NOBODY; + + /* reset log file info */ + d->log_file_name = NULL; + d->log_file = NULL; + d->log_file_err = FALSE; + + connect_to_host_pair(d); + + return d; +} + +struct connection * +rw_instantiate(struct connection *c, const ip_address *him, u_int16_t his_port +, const ip_subnet *his_net, const struct id *his_id) +{ + struct connection *d = instantiate(c, him, his_port, his_id); + + if (d && his_net && is_virtual_connection(c)) + { + d->spd.that.client = *his_net; + d->spd.that.virt = NULL; + if (subnetishost(his_net) && addrinsubnet(him, his_net)) + d->spd.that.has_client = FALSE; + } + + if (d->policy & POLICY_OPPO) + { + /* This must be before we know the client addresses. + * Fill in one that is impossible. This prevents anyone else from + * trying to use this connection to get to a particular client + */ + d->spd.that.client = *aftoinfo(subnettypeof(&d->spd.that.client))->none; + } + DBG(DBG_CONTROL + , DBG_log("instantiated \"%s\" for %s" , d->name, ip_str(him))); + return d; +} + +struct connection * +oppo_instantiate(struct connection *c +, const ip_address *him +, const struct id *his_id +, struct gw_info *gw +, const ip_address *our_client USED_BY_DEBUG +, const ip_address *peer_client) +{ + struct connection *d = instantiate(c, him, 0, his_id); + + passert(d->spd.next == NULL); + + /* fill in our client side */ + if (d->spd.this.has_client) + { + /* there was a client in the abstract connection + * so we demand that the required client is within that subnet. + */ + passert(addrinsubnet(our_client, &d->spd.this.client)); + happy(addrtosubnet(our_client, &d->spd.this.client)); + /* opportunistic connections do not use port selectors */ + setportof(0, &d->spd.this.client.addr); + } + else + { + /* there was no client in the abstract connection + * so we demand that the required client be the host + */ + passert(sameaddr(our_client, &d->spd.this.host_addr)); + } + + /* fill in peer's client side. + * If the client is the peer, excise the client from the connection. + */ + passert((d->policy & POLICY_OPPO) + && addrinsubnet(peer_client, &d->spd.that.client)); + happy(addrtosubnet(peer_client, &d->spd.that.client)); + /* opportunistic connections do not use port selectors */ + setportof(0, &d->spd.that.client.addr); + + if (sameaddr(peer_client, &d->spd.that.host_addr)) + d->spd.that.has_client = FALSE; + + passert(d->gw_info == NULL); + gw_addref(gw); + d->gw_info = gw; + + /* Adjust routing if something is eclipsing c. + * It must be a %hold for us (hard to passert this). + * If there was another instance eclipsing, we'd be using it. + */ + if (c->spd.routing == RT_ROUTED_ECLIPSED) + d->spd.routing = RT_ROUTED_PROSPECTIVE; + + /* Remember if the template is routed: + * if so, this instance applies for initiation + * even if it is created for responding. + */ + if (routed(c->spd.routing)) + d->instance_initiation_ok = TRUE; + + DBG(DBG_CONTROL, + char topo[CONNECTION_BUF]; + + (void) format_connection(topo, sizeof(topo), d, &d->spd); + DBG_log("instantiated \"%s\": %s", d->name, topo); + ); + return d; +} + +/* priority formatting */ +void +fmt_policy_prio(policy_prio_t pp, char buf[POLICY_PRIO_BUF]) +{ + if (pp == BOTTOM_PRIO) + snprintf(buf, POLICY_PRIO_BUF, "0"); + else + snprintf(buf, POLICY_PRIO_BUF, "%lu,%lu" + , pp>>16, (pp & ~(~(policy_prio_t)0 << 16)) >> 8); +} + +/* Format any information needed to identify an instance of a connection. + * Fills any needed information into buf which MUST be big enough. + * Road Warrior: peer's IP address + * Opportunistic: [" " myclient "==="] " ..." peer ["===" hisclient] '\0' + */ +static size_t +fmt_client(const ip_subnet *client, const ip_address *gw, const char *prefix, char buf[ADDRTOT_BUF]) +{ + if (subnetisaddr(client, gw)) + { + buf[0] = '\0'; /* compact denotation for "self" */ + } + else + { + char *ap; + + strcpy(buf, prefix); + ap = buf + strlen(prefix); + if (subnetisnone(client)) + strcpy(ap, "?"); /* unknown */ + else + subnettot(client, 0, ap, SUBNETTOT_BUF); + } + return strlen(buf); +} + +void +fmt_conn_instance(const struct connection *c, char buf[CONN_INST_BUF]) +{ + char *p = buf; + + *p = '\0'; + + if (c->kind == CK_INSTANCE) + { + if (c->instance_serial != 0) + { + snprintf(p, CONN_INST_BUF, "[%lu]", c->instance_serial); + p += strlen(p); + } + + if (c->policy & POLICY_OPPO) + { + size_t w = fmt_client(&c->spd.this.client, &c->spd.this.host_addr, " ", p); + + p += w; + + strcpy(p, w == 0? " ..." : "=== ..."); + p += strlen(p); + + addrtot(&c->spd.that.host_addr, 0, p, ADDRTOT_BUF); + p += strlen(p); + + (void) fmt_client(&c->spd.that.client, &c->spd.that.host_addr, "===", p); + } + else + { + *p++ = ' '; + addrtot(&c->spd.that.host_addr, 0, p, ADDRTOT_BUF); +# + if (c->spd.that.host_port != pluto_port) + { + p += strlen(p); + sprintf(p, ":%d", c->spd.that.host_port); + } + } + } +} + +/* Find an existing connection for a trapped outbound packet. + * This is attempted before we bother with gateway discovery. + * + this connection is routed or instance_of_routed_template + * (i.e. approved for on-demand) + * + this subnet contains our_client (or we are our_client) + * + that subnet contains peer_client (or peer is peer_client) + * + don't care about Phase 1 IDs (we don't know) + * Note: result may still need to be instantiated. + * The winner has the highest policy priority. + * + * If there are several with that priority, we give preference to + * the first one that is an instance. + * + * See also build_outgoing_opportunistic_connection. + */ +struct connection * +find_connection_for_clients(struct spd_route **srp, + const ip_address *our_client, + const ip_address *peer_client, + int transport_proto) +{ + struct connection *c = connections, *best = NULL; + policy_prio_t best_prio = BOTTOM_PRIO; + struct spd_route *sr; + struct spd_route *best_sr = NULL; + int our_port = ntohs(portof(our_client)); + int peer_port = ntohs(portof(peer_client)); + + passert(!isanyaddr(our_client) && !isanyaddr(peer_client)); +#ifdef DEBUG + if (DBGP(DBG_CONTROL)) + { + char ocb[ADDRTOT_BUF], pcb[ADDRTOT_BUF]; + + addrtot(our_client, 0, ocb, sizeof(ocb)); + addrtot(peer_client, 0, pcb, sizeof(pcb)); + DBG_log("find_connection: " + "looking for policy for connection: %s:%d/%d -> %s:%d/%d" + , ocb, transport_proto, our_port, pcb, transport_proto, peer_port); + } +#endif /* DEBUG */ + + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->kind == CK_GROUP) + continue; + + for (sr = &c->spd; best!=c && sr; sr = sr->next) + { + if ((routed(sr->routing) || c->instance_initiation_ok) + && addrinsubnet(our_client, &sr->this.client) + && addrinsubnet(peer_client, &sr->that.client) + && addrinsubnet(peer_client, &sr->that.client) + && (!sr->this.protocol || transport_proto == sr->this.protocol) + && (!sr->this.port || our_port == sr->this.port) + && (!sr->that.port || peer_port == sr->that.port)) + { + char cib[CONN_INST_BUF]; + char cib2[CONN_INST_BUF]; + + policy_prio_t prio = 8 * (c->prio + (c->kind == CK_INSTANCE)) + + 2 * (sr->this.port == our_port) + + 2 * (sr->that.port == peer_port) + + (sr->this.protocol == transport_proto); + +#ifdef DEBUG + if (DBGP(DBG_CONTROL|DBG_CONTROLMORE)) + { + char c_ocb[SUBNETTOT_BUF], c_pcb[SUBNETTOT_BUF]; + + subnettot(&c->spd.this.client, 0, c_ocb, sizeof(c_ocb)); + subnettot(&c->spd.that.client, 0, c_pcb, sizeof(c_pcb)); + DBG_log("find_connection: conn \"%s\"%s has compatible peers: %s->%s [pri: %ld]" + , c->name + , (fmt_conn_instance(c, cib), cib) + , c_ocb, c_pcb, prio); + } +#endif /* DEBUG */ + + if (best == NULL) + { + best = c; + best_sr = sr; + best_prio = prio; + } + + DBG(DBG_CONTROLMORE, + DBG_log("find_connection: " + "comparing best \"%s\"%s [pri:%ld]{%p} (child %s) to \"%s\"%s [pri:%ld]{%p} (child %s)" + , best->name + , (fmt_conn_instance(best, cib), cib) + , best_prio + , best + , (best->policy_next ? best->policy_next->name : "none") + , c->name + , (fmt_conn_instance(c, cib2), cib2) + , prio + , c + , (c->policy_next ? c->policy_next->name : "none"))); + + if (prio > best_prio) + { + best = c; + best_sr = sr; + best_prio = prio; + } + } + } + } + + if (best!= NULL && NEVER_NEGOTIATE(best->policy)) + best = NULL; + + if (srp != NULL && best != NULL) + *srp = best_sr; + +#ifdef DEBUG + if (DBGP(DBG_CONTROL)) + { + if (best) + { + char cib[CONN_INST_BUF]; + DBG_log("find_connection: concluding with \"%s\"%s [pri:%ld]{%p} kind=%s" + , best->name + , (fmt_conn_instance(best, cib), cib) + , best_prio + , best + , enum_name(&connection_kind_names, best->kind)); + } else { + DBG_log("find_connection: concluding with empty"); + } + } +#endif /* DEBUG */ + + return best; +} + +/* Find and instantiate a connection for an outgoing Opportunistic connection. + * We've already discovered its gateway. + * We look for a the connection such that: + * + this is one of our interfaces + * + this subnet contains our_client (or we are our_client) + * (we will specialize the client). We prefer the smallest such subnet. + * + that subnet contains peer_clent (we will specialize the client). + * We prefer the smallest such subnet. + * + is opportunistic + * + that peer is NO_IP + * + don't care about Phase 1 IDs (probably should be default) + * We could look for a connection that already had the desired peer + * (rather than NO_IP) specified, but it doesn't seem worth the + * bother. + * + * We look for the routed policy applying to the narrowest subnets. + * We only succeed if we find such a policy AND it is satisfactory. + * + * The body of the inner loop is a lot like that in + * find_connection_for_clients. In this case, we know the gateways + * that we need to instantiate an opportunistic connection. + */ +struct connection * +build_outgoing_opportunistic_connection(struct gw_info *gw + ,const ip_address *our_client + ,const ip_address *peer_client) +{ + struct iface *p; + struct connection *best = NULL; + struct spd_route *sr, *bestsr; + char ocb[ADDRTOT_BUF], pcb[ADDRTOT_BUF]; + + addrtot(our_client, 0, ocb, sizeof(ocb)); + addrtot(peer_client, 0, pcb, sizeof(pcb)); + + passert(!isanyaddr(our_client) && !isanyaddr(peer_client)); + + /* We don't know his ID yet, so gw id must be an ipaddr */ + passert(gw->key != NULL); + passert(id_is_ipaddr(&gw->gw_id)); + + /* for each of our addresses... */ + for (p = interfaces; p != NULL; p = p->next) + { + /* go through those connections with our address and NO_IP as hosts + * We cannot know what port the peer would use, so we assume + * that it is pluto_port (makes debugging easier). + */ + struct connection *c = find_host_pair_connections(&p->addr + , pluto_port, (ip_address *)NULL, pluto_port); + + for (; c != NULL; c = c->hp_next) + { + DBG(DBG_OPPO, + DBG_log("checking %s", c->name)); + if (c->kind == CK_GROUP) + { + continue; + } + + for (sr = &c->spd; best!=c && sr; sr = sr->next) + { + if (routed(sr->routing) + && addrinsubnet(our_client, &sr->this.client) + && addrinsubnet(peer_client, &sr->that.client)) + { + if (best == NULL) + { + best = c; + break; + } + + DBG(DBG_OPPO, + DBG_log("comparing best %s to %s" + , best->name, c->name)); + + for (bestsr = &best->spd; best!=c && bestsr; bestsr=bestsr->next) + { + if (!subnetinsubnet(&bestsr->this.client, &sr->this.client) + || (samesubnet(&bestsr->this.client, &sr->this.client) + && !subnetinsubnet(&bestsr->that.client + , &sr->that.client))) + { + best = c; + } + } + } + } + } + } + + if (best == NULL + || NEVER_NEGOTIATE(best->policy) + || (best->policy & POLICY_OPPO) == LEMPTY + || best->kind != CK_TEMPLATE) + return NULL; + else + return oppo_instantiate(best, &gw->gw_id.ip_addr, NULL, gw + , our_client, peer_client); +} + +bool +orient(struct connection *c) +{ + struct spd_route *sr; + + if (!oriented(*c)) + { + struct iface *p; + + for (sr = &c->spd; sr; sr = sr->next) + { + /* Note: this loop does not stop when it finds a match: + * it continues checking to catch any ambiguity. + */ + for (p = interfaces; p != NULL; p = p->next) + { + if (p->ike_float) + continue; + + for (;;) + { + /* check if this interface matches this end */ + if (sameaddr(&sr->this.host_addr, &p->addr) + && (!no_klips || sr->this.host_port == pluto_port)) + { + if (oriented(*c)) + { + if (c->interface == p) + loglog(RC_LOG_SERIOUS + , "both sides of \"%s\" are our interface %s!" + , c->name, p->rname); + else + loglog(RC_LOG_SERIOUS, "two interfaces match \"%s\" (%s, %s)" + , c->name, c->interface->rname, p->rname); + c->interface = NULL; /* withdraw orientation */ + return FALSE; + } + c->interface = p; + } + + /* done with this interface if it doesn't match that end */ + if (!(sameaddr(&sr->that.host_addr, &p->addr) + && (!no_klips || sr->that.host_port == pluto_port))) + break; + + /* swap ends and try again. + * It is a little tricky to see that this loop will stop. + * Only continue if the far side matches. + * If both sides match, there is an error-out. + */ + { + struct end t = sr->this; + + sr->this = sr->that; + sr->that = t; + } + } + } + } + } + return oriented(*c); +} + +void +initiate_connection(const char *name, int whackfd) +{ + struct connection *c = con_by_name(name, TRUE); + + if (c != NULL && c->ikev1) + { + set_cur_connection(c); + if (!oriented(*c)) + { + loglog(RC_ORIENT, "we have no ipsecN interface for either end of this connection"); + } + else if (NEVER_NEGOTIATE(c->policy)) + { + loglog(RC_INITSHUNT + , "cannot initiate an authby=never connection"); + } + else if (c->kind != CK_PERMANENT) + { + if (isanyaddr(&c->spd.that.host_addr)) + loglog(RC_NOPEERIP, "cannot initiate connection without knowing peer IP address"); + else + loglog(RC_WILDCARD, "cannot initiate connection with ID wildcards"); + } + else + { + /* We will only request an IPsec SA if policy isn't empty + * (ignoring Main Mode items). + * This is a fudge, but not yet important. + * If we are to proceed asynchronously, whackfd will be NULL_FD. + */ + c->policy |= POLICY_UP; + /* do we have to prompt for a PIN code? */ + if (c->spd.this.sc != NULL && !c->spd.this.sc->valid && whackfd != NULL_FD) + scx_get_pin(c->spd.this.sc, whackfd); + + if (c->spd.this.sc != NULL && !c->spd.this.sc->valid) + { + loglog(RC_NOVALIDPIN, "cannot initiate connection without valid PIN"); + } + else + { + ipsecdoi_initiate(whackfd, c, c->policy, 1, SOS_NOBODY); + whackfd = NULL_FD; /* protect from close */ + } + } + reset_cur_connection(); + } + close_any(whackfd); +} + +/* (Possibly) Opportunistic Initiation: + * Knowing clients (single IP addresses), try to build an tunnel. + * This may involve discovering a gateway and instantiating an + * Opportunistic connection. Called when a packet is caught by + * a %trap, or when whack --oppohere --oppothere is used. + * It may turn out that an existing or non-opporunistic connnection + * can handle the traffic. + * + * Most of the code will be restarted if an ADNS request is made + * to discover the gateway. The only difference between the first + * and second entry is whether gateways_from_dns is NULL or not. + * initiate_opportunistic: initial entrypoint + * continue_oppo: where we pickup when ADNS result arrives + * initiate_opportunistic_body: main body shared by above routines + * cannot_oppo: a helper function to log a diagnostic + * This structure repeats a lot of code when the ADNS result arrives. + * This seems like a waste, but anything learned the first time through + * may no longer be true! + * + * After the first IKE message is sent, the regular state machinery + * carries negotiation forward. + */ + +enum find_oppo_step { + fos_start, + fos_myid_ip_txt, + fos_myid_hostname_txt, + fos_myid_ip_key, + fos_myid_hostname_key, + fos_our_client, + fos_our_txt, +#ifdef USE_KEYRR + fos_our_key, +#endif /* USE_KEYRR */ + fos_his_client, + fos_done +}; + +#ifdef DEBUG +static const char *const oppo_step_name[] = { + "fos_start", + "fos_myid_ip_txt", + "fos_myid_hostname_txt", + "fos_myid_ip_key", + "fos_myid_hostname_key", + "fos_our_client", + "fos_our_txt", +#ifdef USE_KEYRR + "fos_our_key", +#endif /* USE_KEYRR */ + "fos_his_client", + "fos_done" +}; +#endif /* DEBUG */ + +struct find_oppo_bundle { + enum find_oppo_step step; + err_t want; + bool failure_ok; /* if true, continue_oppo should not die on DNS failure */ + ip_address our_client; /* not pointer! */ + ip_address peer_client; + int transport_proto; + bool held; + policy_prio_t policy_prio; + ipsec_spi_t failure_shunt; /* in host order! 0 for delete. */ + int whackfd; +}; + +struct find_oppo_continuation { + struct adns_continuation ac; /* common prefix */ + struct find_oppo_bundle b; +}; + +static void +cannot_oppo(struct connection *c + , struct find_oppo_bundle *b + , err_t ugh) +{ + char pcb[ADDRTOT_BUF]; + char ocb[ADDRTOT_BUF]; + + addrtot(&b->peer_client, 0, pcb, sizeof(pcb)); + addrtot(&b->our_client, 0, ocb, sizeof(ocb)); + + DBG(DBG_DNS | DBG_OPPO, DBG_log("Can't Opportunistically initiate for %s to %s: %s" + , ocb, pcb, ugh)); + + whack_log(RC_OPPOFAILURE + , "Can't Opportunistically initiate for %s to %s: %s" + , ocb, pcb, ugh); + + if (c != NULL && c->policy_next != NULL) + { + /* there is some policy that comes afterwards */ + struct spd_route *shunt_spd; + struct connection *nc = c->policy_next; + struct state *st; + + passert(c->kind == CK_TEMPLATE); + passert(c->policy_next->kind == CK_PERMANENT); + + DBG(DBG_OPPO, DBG_log("OE failed for %s to %s, but %s overrides shunt" + , ocb, pcb, c->policy_next->name)); + + /* + * okay, here we need add to the "next" policy, which is ought + * to be an instance. + * We will add another entry to the spd_route list for the specific + * situation that we have. + */ + + shunt_spd = clone_thing(nc->spd, "shunt eroute policy"); + + shunt_spd->next = nc->spd.next; + nc->spd.next = shunt_spd; + + happy(addrtosubnet(&b->peer_client, &shunt_spd->that.client)); + + if (sameaddr(&b->peer_client, &shunt_spd->that.host_addr)) + shunt_spd->that.has_client = FALSE; + + /* + * override the tunnel destination with the one from the secondaried + * policy + */ + shunt_spd->that.host_addr = nc->spd.that.host_addr; + + /* now, lookup the state, and poke it up. + */ + + st = state_with_serialno(nc->newest_ipsec_sa); + + /* XXX what to do if the IPSEC SA has died? */ + passert(st != NULL); + + /* link the new connection instance to the state's list of + * connections + */ + + DBG(DBG_OPPO, DBG_log("installing state: %ld for %s to %s" + , nc->newest_ipsec_sa + , ocb, pcb)); + +#ifdef DEBUG + if (DBGP(DBG_OPPO | DBG_CONTROLMORE)) + { + char state_buf[LOG_WIDTH]; + char state_buf2[LOG_WIDTH]; + time_t n = now(); + + fmt_state(FALSE, st, n + , state_buf, sizeof(state_buf) + , state_buf2, sizeof(state_buf2)); + DBG_log("cannot_oppo, failure SA1: %s", state_buf); + DBG_log("cannot_oppo, failure SA2: %s", state_buf2); + } +#endif /* DEBUG */ + + if (!route_and_eroute(c, shunt_spd, st)) + { + whack_log(RC_OPPOFAILURE + , "failed to instantiate shunt policy %s for %s to %s" + , c->name + , ocb, pcb); + } + return; + } + +#ifdef KLIPS + if (b->held) + { + /* Replace HOLD with b->failure_shunt. + * If no b->failure_shunt specified, use SPI_PASS -- THIS MAY CHANGE. + */ + if (b->failure_shunt == 0) + { + DBG(DBG_OPPO, DBG_log("no explicit failure shunt for %s to %s; installing %%pass" + , ocb, pcb)); + } + + (void) replace_bare_shunt(&b->our_client, &b->peer_client + , b->policy_prio + , b->failure_shunt + , b->failure_shunt != 0 + , b->transport_proto + , ugh); + } +#endif +} + +static void initiate_opportunistic_body(struct find_oppo_bundle *b + , struct adns_continuation *ac, err_t ac_ugh); /* forward */ + +void +initiate_opportunistic(const ip_address *our_client +, const ip_address *peer_client +, int transport_proto +, bool held +, int whackfd) +{ + struct find_oppo_bundle b; + + b.want = (whackfd == NULL_FD ? "whack" : "acquire"); + b.failure_ok = FALSE; + b.our_client = *our_client; + b.peer_client = *peer_client; + b.transport_proto = transport_proto; + b.held = held; + b.policy_prio = BOTTOM_PRIO; + b.failure_shunt = 0; + b.whackfd = whackfd; + b.step = fos_start; + initiate_opportunistic_body(&b, NULL, NULL); +} + +static void +continue_oppo(struct adns_continuation *acr, err_t ugh) +{ + struct find_oppo_continuation *cr = (void *)acr; /* inherit, damn you! */ + struct connection *c; + bool was_held = cr->b.held; + int whackfd = cr->b.whackfd; + + /* note: cr->id has no resources; cr->sgw_id is id_none: + * neither need freeing. + */ + whack_log_fd = whackfd; + +#ifdef KLIPS + /* Discover and record whether %hold has gone away. + * This could have happened while we were awaiting DNS. + * We must check BEFORE any call to cannot_oppo. + */ + if (was_held) + cr->b.held = has_bare_hold(&cr->b.our_client, &cr->b.peer_client + , cr->b.transport_proto); +#endif + +#ifdef DEBUG + /* if we're going to ignore the error, at least note it in debugging log */ + if (cr->b.failure_ok && ugh != NULL) + { + DBG(DBG_CONTROL | DBG_DNS, + { + char ocb[ADDRTOT_BUF]; + char pcb[ADDRTOT_BUF]; + + addrtot(&cr->b.our_client, 0, ocb, sizeof(ocb)); + addrtot(&cr->b.peer_client, 0, pcb, sizeof(pcb)); + DBG_log("continuing from failed DNS lookup for %s, %s to %s: %s" + , cr->b.want, ocb, pcb, ugh); + }); + } +#endif + + if (!cr->b.failure_ok && ugh != NULL) + { + c = find_connection_for_clients(NULL, &cr->b.our_client, &cr->b.peer_client + , cr->b.transport_proto); + cannot_oppo(c, &cr->b + , builddiag("%s: %s", cr->b.want, ugh)); + } + else if (was_held && !cr->b.held) + { + /* was_held indicates we were started due to a %trap firing + * (as opposed to a "whack --oppohere --oppothere"). + * Since the %hold has gone, we can assume that somebody else + * has beaten us to the punch. We can go home. But lets log it. + */ + char ocb[ADDRTOT_BUF]; + char pcb[ADDRTOT_BUF]; + + addrtot(&cr->b.our_client, 0, ocb, sizeof(ocb)); + addrtot(&cr->b.peer_client, 0, pcb, sizeof(pcb)); + + loglog(RC_COMMENT + , "%%hold otherwise handled during DNS lookup for Opportunistic Initiation for %s to %s" + , ocb, pcb); + } + else + { + initiate_opportunistic_body(&cr->b, &cr->ac, ugh); + whackfd = NULL_FD; /* was handed off */ + } + + whack_log_fd = NULL_FD; + close_any(whackfd); +} + +#ifdef USE_KEYRR +static err_t +check_key_recs(enum myid_state try_state +, const struct connection *c +, struct adns_continuation *ac) +{ + /* Check if KEY lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + enum myid_state old_myid_state = myid_state; + const struct RSA_private_key *our_RSA_pri; + err_t ugh = NULL; + + myid_state = try_state; + + if (old_myid_state != myid_state + && old_myid_state == MYID_SPECIFIED) + { + ugh = "%myid was specified while we were guessing"; + } + else if ((our_RSA_pri = get_RSA_private_key(c)) == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + pubkey_list_t *kr; + + ugh = "no KEY RR found for us"; + for (kr = ac->keys_from_dns; kr != NULL; kr = kr->next) + { + ugh = "all our KEY RRs have the wrong public key"; + if (kr->key->alg == PUBKEY_ALG_RSA + && same_RSA_public_key(&our_RSA_pri->pub, &kr->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + if (ugh != NULL) + myid_state = old_myid_state; + return ugh; +} +#endif /* USE_KEYRR */ + +static err_t +check_txt_recs(enum myid_state try_state +, const struct connection *c +, struct adns_continuation *ac) +{ + /* Check if TXT lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + enum myid_state old_myid_state = myid_state; + const struct RSA_private_key *our_RSA_pri; + err_t ugh = NULL; + + myid_state = try_state; + + if (old_myid_state != myid_state + && old_myid_state == MYID_SPECIFIED) + { + ugh = "%myid was specified while we were guessing"; + } + else if ((our_RSA_pri = get_RSA_private_key(c)) == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + struct gw_info *gwp; + + ugh = "no TXT RR found for us"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + ugh = "all our TXT RRs have the wrong public key"; + if (gwp->key->alg == PUBKEY_ALG_RSA + && same_RSA_public_key(&our_RSA_pri->pub, &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + if (ugh != NULL) + myid_state = old_myid_state; + return ugh; +} + + +/* note: gateways_from_dns must be NULL iff this is the first call */ +static void +initiate_opportunistic_body(struct find_oppo_bundle *b +, struct adns_continuation *ac +, err_t ac_ugh) +{ + struct connection *c; + struct spd_route *sr; + + /* What connection shall we use? + * First try for one that explicitly handles the clients. + */ + DBG(DBG_CONTROL, + { + char ours[ADDRTOT_BUF]; + char his[ADDRTOT_BUF]; + int ourport; + int hisport; + + addrtot(&b->our_client, 0, ours, sizeof(ours)); + addrtot(&b->peer_client, 0, his, sizeof(his)); + ourport = ntohs(portof(&b->our_client)); + hisport = ntohs(portof(&b->peer_client)); + DBG_log("initiate on demand from %s:%d to %s:%d proto=%d state: %s because: %s" + , ours, ourport, his, hisport, b->transport_proto + , oppo_step_name[b->step], b->want); + }); + if (isanyaddr(&b->our_client) || isanyaddr(&b->peer_client)) + { + cannot_oppo(NULL, b, "impossible IP address"); + } + else if ((c = find_connection_for_clients(&sr + , &b->our_client + , &b->peer_client + , b->transport_proto)) == NULL) + { + /* No connection explicitly handles the clients and there + * are no Opportunistic connections -- whine and give up. + * The failure policy cannot be gotten from a connection; we pick %pass. + */ + cannot_oppo(NULL, b, "no routed Opportunistic template covers this pair"); + } + else if (c->kind != CK_TEMPLATE) + { + /* We've found a connection that can serve. + * Do we have to initiate it? + * Not if there is currently an IPSEC SA. + * But if there is an IPSEC SA, then KLIPS would not + * have generated the acquire. So we assume that there isn't one. + * This may be redundant if a non-opportunistic + * negotiation is already being attempted. + */ + + /* If we are to proceed asynchronously, b->whackfd will be NULL_FD. */ + + if(c->kind == CK_INSTANCE) + { + char cib[CONN_INST_BUF]; + /* there is already an instance being negotiated, no nothing */ + DBG(DBG_CONTROL, DBG_log("found existing instance \"%s\"%s, rekeying it" + , c->name + , (fmt_conn_instance(c, cib), cib))); + /* XXX-mcr - return; */ + } + + /* otherwise, there is some kind of static conn that can handle + * this connection, so we initiate it */ + +#ifdef KLIPS + if (b->held) + { + /* what should we do on failure? */ + (void) assign_hold(c, sr, b->transport_proto, &b->our_client, &b->peer_client); + } +#endif + ipsecdoi_initiate(b->whackfd, c, c->policy, 1, SOS_NOBODY); + b->whackfd = NULL_FD; /* protect from close */ + } + else + { + /* We are handling an opportunistic situation. + * This involves several DNS lookup steps that require suspension. + * Note: many facts might change while we're suspended. + * Here be dragons. + * + * The first chunk of code handles the result of the previous + * DNS query (if any). It also selects the kind of the next step. + * The second chunk initiates the next DNS query (if any). + */ + enum find_oppo_step next_step; + err_t ugh = ac_ugh; + char mycredentialstr[BUF_LEN]; + char cib[CONN_INST_BUF]; + + DBG(DBG_CONTROL, DBG_log("creating new instance from \"%s\"%s" + , c->name + , (fmt_conn_instance(c, cib), cib))); + + + idtoa(&sr->this.id, mycredentialstr, sizeof(mycredentialstr)); + + passert(c->policy & POLICY_OPPO); /* can't initiate Road Warrior connections */ + + /* handle any DNS answer; select next step */ + + switch (b->step) + { + case fos_start: + /* just starting out: select first query step */ + next_step = fos_myid_ip_txt; + break; + + case fos_myid_ip_txt: /* TXT for our default IP address as %myid */ + ugh = check_txt_recs(MYID_IP, c, ac); + if (ugh != NULL) + { + /* cannot use our IP as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our IP (%s:TXT) as identity: %s" + , myid_str[MYID_IP] + , ugh)); + if (!logged_myid_ip_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our IP (%s:TXT) as identity: %s" + , myid_str[MYID_IP] + , ugh); + logged_myid_ip_txt_warning = TRUE; + } + + next_step = fos_myid_hostname_txt; + ugh = NULL; /* failure can be recovered from */ + } + else + { + /* we can use our IP as OE identity for initiation */ + if (!logged_myid_ip_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "using our IP (%s:TXT) as identity!" + , myid_str[MYID_IP]); + logged_myid_ip_txt_warning = TRUE; + } + + next_step = fos_our_client; + } + break; + + case fos_myid_hostname_txt: /* TXT for our hostname as %myid */ + ugh = check_txt_recs(MYID_HOSTNAME, c, ac); + if (ugh != NULL) + { + /* cannot use our hostname as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our hostname (%s:TXT) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh)); + if (!logged_myid_fqdn_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our hostname (%s:TXT) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh); + logged_myid_fqdn_txt_warning = TRUE; + } +#ifdef USE_KEYRR + next_step = fos_myid_ip_key; + ugh = NULL; /* failure can be recovered from */ +#endif + } + else + { + /* we can use our hostname as OE identity for initiation */ + if (!logged_myid_fqdn_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "using our hostname (%s:TXT) as identity!" + , myid_str[MYID_HOSTNAME]); + logged_myid_fqdn_txt_warning = TRUE; + } + next_step = fos_our_client; + } + break; + +#ifdef USE_KEYRR + case fos_myid_ip_key: /* KEY for our default IP address as %myid */ + ugh = check_key_recs(MYID_IP, c, ac); + if (ugh != NULL) + { + /* cannot use our IP as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our IP (%s:KEY) as identity: %s" + , myid_str[MYID_IP] + , ugh)); + if (!logged_myid_ip_key_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our IP (%s:KEY) as identity: %s" + , myid_str[MYID_IP] + , ugh); + logged_myid_ip_key_warning = TRUE; + } + + next_step = fos_myid_hostname_key; + ugh = NULL; /* failure can be recovered from */ + } + else + { + /* we can use our IP as OE identity for initiation */ + if (!logged_myid_ip_key_warning) + { + loglog(RC_LOG_SERIOUS + , "using our IP (%s:KEY) as identity!" + , myid_str[MYID_IP]); + logged_myid_ip_key_warning = TRUE; + } + next_step = fos_our_client; + } + break; + + case fos_myid_hostname_key: /* KEY for our hostname as %myid */ + ugh = check_key_recs(MYID_HOSTNAME, c, ac); + if (ugh != NULL) + { + /* cannot use our IP as OE identitiy for initiation */ + DBG(DBG_OPPO, DBG_log("can not use our hostname (%s:KEY) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh)); + if (!logged_myid_fqdn_key_warning) + { + loglog(RC_LOG_SERIOUS + , "can not use our hostname (%s:KEY) as identity: %s" + , myid_str[MYID_HOSTNAME] + , ugh); + logged_myid_fqdn_key_warning = TRUE; + } + + next_step = fos_myid_hostname_key; + ugh = NULL; /* failure can be recovered from */ + } + else + { + /* we can use our IP as OE identity for initiation */ + if (!logged_myid_fqdn_key_warning) + { + loglog(RC_LOG_SERIOUS + , "using our hostname (%s:KEY) as identity!" + , myid_str[MYID_HOSTNAME]); + logged_myid_fqdn_key_warning = TRUE; + } + next_step = fos_our_client; + } + break; +#endif + + case fos_our_client: /* TXT for our client */ + { + /* Our client is not us: we must check the TXT records. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + const struct RSA_private_key *our_RSA_pri = get_RSA_private_key(c); + + next_step = fos_his_client; /* normal situation */ + + passert(sr != NULL); + + if (our_RSA_pri == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (sameaddr(&sr->this.host_addr, &b->our_client)) + { + /* this wasn't true when we started -- bail */ + ugh = "our IP address changed underfoot"; + } + else if (!same_id(&ac->sgw_id, &sr->this.id)) + { + /* this wasn't true when we started -- bail */ + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in quick_inI1_outR1_tail + * for checking the other side. + */ + struct gw_info *gwp; + + ugh = "no TXT RR for our client delegates us"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + passert(same_id(&gwp->gw_id, &sr->this.id)); + + ugh = "TXT RR for our client has wrong key"; + /* If there is a key from the TXT record, + * we count it as a win if we match the key. + * If there was no key, we have a tentative win: + * we need to check our KEY record to be sure. + */ + if (!gwp->gw_key_present) + { + /* Success, but the TXT had no key + * so we must check our our own KEY records. + */ + next_step = fos_our_txt; + ugh = NULL; /* good! */ + break; + } + if (same_RSA_public_key(&our_RSA_pri->pub, &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + } + break; + + case fos_our_txt: /* TXT for us */ + { + /* Check if TXT lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + const struct RSA_private_key *our_RSA_pri = get_RSA_private_key(c); + + next_step = fos_his_client; /* unless we decide to look for KEY RR */ + + if (our_RSA_pri == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + struct gw_info *gwp; + + ugh = "no TXT RR for us"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + passert(same_id(&gwp->gw_id, &sr->this.id)); + + ugh = "TXT RR for us has wrong key"; + if (gwp->gw_key_present + && same_RSA_public_key(&our_RSA_pri->pub, &gwp->key->u.rsa)) + { + DBG(DBG_CONTROL, + DBG_log("initiate on demand found TXT with right public key at: %s" + , mycredentialstr)); + ugh = NULL; + break; + } + } +#ifdef USE_KEYRR + if (ugh != NULL) + { + /* if no TXT with right key, try KEY */ + DBG(DBG_CONTROL, + DBG_log("will try for KEY RR since initiate on demand found %s: %s" + , ugh, mycredentialstr)); + next_step = fos_our_key; + ugh = NULL; + } +#endif + } + } + break; + +#ifdef USE_KEYRR + case fos_our_key: /* KEY for us */ + { + /* Check if KEY lookup yielded good results. + * Looking up based on our ID. Used if + * client is ourself, or if TXT had no public key. + * Note: if c is different this time, there is + * a chance that we did the wrong query. + * If so, treat as a kind of failure. + */ + const struct RSA_private_key *our_RSA_pri = get_RSA_private_key(c); + + next_step = fos_his_client; /* always */ + + if (our_RSA_pri == NULL) + { + ugh = "we don't know our own RSA key"; + } + else if (!same_id(&ac->id, &c->spd.this.id)) + { + ugh = "our ID changed underfoot"; + } + else + { + /* Similar to code in RSA_check_signature + * for checking the other side. + */ + pubkey_list_t *kr; + + ugh = "no KEY RR found for us (and no good TXT RR)"; + for (kr = ac->keys_from_dns; kr != NULL; kr = kr->next) + { + ugh = "all our KEY RRs have the wrong public key (and no good TXT RR)"; + if (kr->key->alg == PUBKEY_ALG_RSA + && same_RSA_public_key(&our_RSA_pri->pub, &kr->key->u.rsa)) + { + /* do this only once a day */ + if (!logged_txt_warning) + { + loglog(RC_LOG_SERIOUS + , "found KEY RR but not TXT RR for %s. See http://www.freeswan.org/err/txt-change.html." + , mycredentialstr); + logged_txt_warning = TRUE; + } + ugh = NULL; /* good! */ + break; + } + } + } + } + break; +#endif /* USE_KEYRR */ + + case fos_his_client: /* TXT for his client */ + { + /* We've finished last DNS queries: TXT for his client. + * Using the information, try to instantiate a connection + * and start negotiating. + * We now know the peer. The chosing of "c" ignored this, + * so we will disregard its current value. + * !!! We need to randomize the entry in gw that we choose. + */ + next_step = fos_done; /* no more queries */ + + c = build_outgoing_opportunistic_connection(ac->gateways_from_dns + , &b->our_client + , &b->peer_client); + + if (c == NULL) + { + /* We cannot seem to instantiate a suitable connection: + * complain clearly. + */ + char ocb[ADDRTOT_BUF] + , pcb[ADDRTOT_BUF] + , pb[ADDRTOT_BUF]; + + addrtot(&b->our_client, 0, ocb, sizeof(ocb)); + addrtot(&b->peer_client, 0, pcb, sizeof(pcb)); + passert(id_is_ipaddr(&ac->gateways_from_dns->gw_id)); + addrtot(&ac->gateways_from_dns->gw_id.ip_addr, 0, pb, sizeof(pb)); + loglog(RC_OPPOFAILURE + , "no suitable connection for opportunism" + " between %s and %s with %s as peer" + , ocb, pcb, pb); + +#ifdef KLIPS + if (b->held) + { + /* Replace HOLD with PASS. + * The type of replacement *ought* to be + * specified by policy. + */ + (void) replace_bare_shunt(&b->our_client, &b->peer_client + , BOTTOM_PRIO + , SPI_PASS /* fail into PASS */ + , TRUE, b->transport_proto + , "no suitable connection"); + } +#endif + } + else + { + /* If we are to proceed asynchronously, b->whackfd will be NULL_FD. */ + passert(c->kind == CK_INSTANCE); + passert(c->gw_info != NULL); + passert(HAS_IPSEC_POLICY(c->policy)); + passert(LHAS(LELEM(RT_UNROUTED) | LELEM(RT_ROUTED_PROSPECTIVE), c->spd.routing)); +#ifdef KLIPS + if (b->held) + { + /* what should we do on failure? */ + (void) assign_hold(c, &c->spd + , b->transport_proto + , &b->our_client, &b->peer_client); + } +#endif + c->gw_info->key->last_tried_time = now(); + ipsecdoi_initiate(b->whackfd, c, c->policy, 1, SOS_NOBODY); + b->whackfd = NULL_FD; /* protect from close */ + } + } + break; + + default: + bad_case(b->step); + } + + /* the second chunk: initiate the next DNS query (if any) */ + DBG(DBG_CONTROL, + { + char ours[ADDRTOT_BUF]; + char his[ADDRTOT_BUF]; + + addrtot(&b->our_client, 0, ours, sizeof(ours)); + addrtot(&b->peer_client, 0, his, sizeof(his)); + DBG_log("initiate on demand from %s to %s new state: %s with ugh: %s" + , ours, his, oppo_step_name[b->step], ugh ? ugh : "ok"); + }); + + if (ugh != NULL) + { + b->policy_prio = c->prio; + b->failure_shunt = shunt_policy_spi(c, FALSE); + cannot_oppo(c, b, ugh); + } + else if (next_step == fos_done) + { + /* nothing to do */ + } + else + { + /* set up the next query */ + struct find_oppo_continuation *cr = alloc_thing(struct find_oppo_continuation + , "opportunistic continuation"); + struct id id; + + b->policy_prio = c->prio; + b->failure_shunt = shunt_policy_spi(c, FALSE); + cr->b = *b; /* copy; start hand off of whackfd */ + cr->b.failure_ok = FALSE; + cr->b.step = next_step; + + for (sr = &c->spd + ; sr!=NULL && !sameaddr(&sr->this.host_addr, &b->our_client) + ; sr = sr->next) + ; + + if (sr == NULL) + sr = &c->spd; + + /* If a %hold shunt has replaced the eroute for this template, + * record this fact. + */ + if (b->held + && sr->routing == RT_ROUTED_PROSPECTIVE && eclipsable(sr)) + { + sr->routing = RT_ROUTED_ECLIPSED; + eclipse_count++; + } + + /* Switch to issue next query. + * A case may turn out to be unnecessary. If so, it falls + * through to the next case. + * Figuring out what %myid can stand for must be done before + * our client credentials are looked up: we must know what + * the client credentials may use to identify us. + * On the other hand, our own credentials should be looked + * up after our clients in case our credentials are not + * needed at all. + * XXX this is a wasted effort if we don't have credentials + * BUT they are not needed. + */ + switch (next_step) + { + case fos_myid_ip_txt: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { + cr->b.failure_ok = TRUE; + cr->b.want = b->want = "TXT record for IP address as %myid"; + ugh = start_adns_query(&myids[MYID_IP] + , &myids[MYID_IP] + , T_TXT + , continue_oppo + , &cr->ac); + break; + } + cr->b.step = fos_myid_hostname_txt; + /* fall through */ + + case fos_myid_hostname_txt: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { +#ifdef USE_KEYRR + cr->b.failure_ok = TRUE; +#else + cr->b.failure_ok = FALSE; +#endif + cr->b.want = b->want = "TXT record for hostname as %myid"; + ugh = start_adns_query(&myids[MYID_HOSTNAME] + , &myids[MYID_HOSTNAME] + , T_TXT + , continue_oppo + , &cr->ac); + break; + } + +#ifdef USE_KEYRR + cr->b.step = fos_myid_ip_key; + /* fall through */ + + case fos_myid_ip_key: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { + cr->b.failure_ok = TRUE; + cr->b.want = b->want = "KEY record for IP address as %myid (no good TXT)"; + ugh = start_adns_query(&myids[MYID_IP] + , (const struct id *) NULL /* security gateway meaningless */ + , T_KEY + , continue_oppo + , &cr->ac); + break; + } + cr->b.step = fos_myid_hostname_key; + /* fall through */ + + case fos_myid_hostname_key: + if (c->spd.this.id.kind == ID_MYID + && myid_state != MYID_SPECIFIED) + { + cr->b.failure_ok = FALSE; /* last attempt! */ + cr->b.want = b->want = "KEY record for hostname as %myid (no good TXT)"; + ugh = start_adns_query(&myids[MYID_HOSTNAME] + , (const struct id *) NULL /* security gateway meaningless */ + , T_KEY + , continue_oppo + , &cr->ac); + break; + } +#endif + cr->b.step = fos_our_client; + /* fall through */ + + case fos_our_client: /* TXT for our client */ + if (!sameaddr(&c->spd.this.host_addr, &b->our_client)) + { + /* Check that at least one TXT(reverse(b->our_client)) is workable. + * Note: {unshare|free}_id_content not needed for id: ephemeral. + */ + cr->b.want = b->want = "our client's TXT record"; + iptoid(&b->our_client, &id); + ugh = start_adns_query(&id + , &c->spd.this.id /* we are the security gateway */ + , T_TXT + , continue_oppo + , &cr->ac); + break; + } + cr->b.step = fos_our_txt; + /* fall through */ + + case fos_our_txt: /* TXT for us */ + cr->b.failure_ok = b->failure_ok = TRUE; + cr->b.want = b->want = "our TXT record"; + ugh = start_adns_query(&sr->this.id + , &sr->this.id /* we are the security gateway XXX - maybe ignore? mcr */ + , T_TXT + , continue_oppo + , &cr->ac); + break; + +#ifdef USE_KEYRR + case fos_our_key: /* KEY for us */ + cr->b.want = b->want = "our KEY record"; + cr->b.failure_ok = b->failure_ok = FALSE; + ugh = start_adns_query(&sr->this.id + , (const struct id *) NULL /* security gateway meaningless */ + , T_KEY + , continue_oppo + , &cr->ac); + break; +#endif /* USE_KEYRR */ + + case fos_his_client: /* TXT for his client */ + /* note: {unshare|free}_id_content not needed for id: ephemeral */ + cr->b.want = b->want = "target's TXT record"; + cr->b.failure_ok = b->failure_ok = FALSE; + iptoid(&b->peer_client, &id); + ugh = start_adns_query(&id + , (const struct id *) NULL /* security gateway unconstrained */ + , T_TXT + , continue_oppo + , &cr->ac); + break; + + default: + bad_case(next_step); + } + + if (ugh == NULL) + b->whackfd = NULL_FD; /* complete hand-off */ + else + cannot_oppo(c, b, ugh); + } + } + close_any(b->whackfd); +} + +void +terminate_connection(const char *nm) +{ + /* Loop because more than one may match (master and instances) + * But at least one is required (enforced by con_by_name). + */ + struct connection *c = con_by_name(nm, TRUE); + + if (c == NULL || !c->ikev1) + return; + + do + { + struct connection *n = c->ac_next; /* grab this before c might disappear */ + + if (streq(c->name, nm) + && c->kind >= CK_PERMANENT + && !NEVER_NEGOTIATE(c->policy)) + { + set_cur_connection(c); + plog("terminating SAs using this connection"); + c->policy &= ~POLICY_UP; + flush_pending_by_connection(c); + delete_states_by_connection(c, FALSE); + reset_cur_connection(); + } + c = n; + } while (c != NULL); +} + +/* check nexthop safety + * Our nexthop must not be within a routed client subnet, and vice versa. + * Note: we don't think this is true. We think that KLIPS will + * not process a packet output by an eroute. + */ +#ifdef NEVER +//bool +//check_nexthop(const struct connection *c) +//{ +// struct connection *d; +// +// if (addrinsubnet(&c->spd.this.host_nexthop, &c->spd.that.client)) +// { +// loglog(RC_LOG_SERIOUS, "cannot perform routing for connection \"%s\"" +// " because nexthop is within peer's client network", +// c->name); +// return FALSE; +// } +// +// for (d = connections; d != NULL; d = d->next) +// { +// if (d->routing != RT_UNROUTED) +// { +// if (addrinsubnet(&c->spd.this.host_nexthop, &d->spd.that.client)) +// { +// loglog(RC_LOG_SERIOUS, "cannot do routing for connection \"%s\" +// " because nexthop is contained in" +// " existing routing for connection \"%s\"", +// c->name, d->name); +// return FALSE; +// } +// if (addrinsubnet(&d->spd.this.host_nexthop, &c->spd.that.client)) +// { +// loglog(RC_LOG_SERIOUS, "cannot do routing for connection \"%s\" +// " because it contains nexthop of" +// " existing routing for connection \"%s\"", +// c->name, d->name); +// return FALSE; +// } +// } +// } +// return TRUE; +//} +#endif /* NEVER */ + +/* an ISAKMP SA has been established. + * Note the serial number, and release any connections with + * the same peer ID but different peer IP address. + */ +bool uniqueIDs = FALSE; /* --uniqueids? */ + +void +ISAKMP_SA_established(struct connection *c, so_serial_t serial) +{ + c->newest_isakmp_sa = serial; + + /* the connection is now oriented so that we are able to determine + * whether we are a mode config server with a virtual IP to send. + */ + if (!isanyaddr(&c->spd.that.host_srcip) && !c->spd.that.has_natip) + c->spd.that.modecfg = TRUE; + + if (uniqueIDs) + { + /* for all connections: if the same Phase 1 IDs are used + * for a different IP address, unorient that connection. + */ + struct connection *d; + + for (d = connections; d != NULL; ) + { + struct connection *next = d->ac_next; /* might move underneath us */ + + if (d->kind >= CK_PERMANENT + && same_id(&c->spd.this.id, &d->spd.this.id) + && same_id(&c->spd.that.id, &d->spd.that.id) + && (!sameaddr(&c->spd.that.host_addr, &d->spd.that.host_addr) || + (c->spd.that.host_port != d->spd.that.host_port))) + { + release_connection(d, FALSE); + } + d = next; + } + } +} + +/* Find the connection to connection c's peer's client with the + * largest value of .routing. All other things being equal, + * preference is given to c. If none is routed, return NULL. + * + * If erop is non-null, set *erop to a connection sharing both + * our client subnet and peer's client subnet with the largest value + * of .routing. If none is erouted, set *erop to NULL. + * + * The return value is used to find other connections sharing a route. + * *erop is used to find other connections sharing an eroute. + */ +struct connection * +route_owner(struct connection *c + , struct spd_route **srp + , struct connection **erop + , struct spd_route **esrp) +{ + struct connection *d + , *best_ro = c + , *best_ero = c; + struct spd_route *srd, *src; + struct spd_route *best_sr, *best_esr; + enum routing_t best_routing, best_erouting; + + passert(oriented(*c)); + best_sr = NULL; + best_esr = NULL; + best_routing = c->spd.routing; + best_erouting = best_routing; + + for (d = connections; d != NULL; d = d->ac_next) + { + for (srd = &d->spd; srd; srd = srd->next) + { + if (srd->routing == RT_UNROUTED) + continue; + + for (src = &c->spd; src; src=src->next) + { + if (!samesubnet(&src->that.client, &srd->that.client)) + continue; + if (src->that.protocol != srd->that.protocol) + continue; + if (src->that.port != srd->that.port) + continue; + passert(oriented(*d)); + if (srd->routing > best_routing) + { + best_ro = d; + best_sr = srd; + best_routing = srd->routing; + } + + if (!samesubnet(&src->this.client, &srd->this.client)) + continue; + if (src->this.protocol != srd->this.protocol) + continue; + if (src->this.port != srd->this.port) + continue; + if (srd->routing > best_erouting) + { + best_ero = d; + best_esr = srd; + best_erouting = srd->routing; + } + } + } + } + + DBG(DBG_CONTROL, + { + char cib[CONN_INST_BUF]; + err_t m = builddiag("route owner of \"%s\"%s %s:" + , c->name + , (fmt_conn_instance(c, cib), cib) + , enum_name(&routing_story, c->spd.routing)); + + if (!routed(best_ro->spd.routing)) + m = builddiag("%s NULL", m); + else if (best_ro == c) + m = builddiag("%s self", m); + else + m = builddiag("%s \"%s\"%s %s", m + , best_ro->name + , (fmt_conn_instance(best_ro, cib), cib) + , enum_name(&routing_story, best_ro->spd.routing)); + + if (erop != NULL) + { + m = builddiag("%s; eroute owner:", m); + if (!erouted(best_ero->spd.routing)) + m = builddiag("%s NULL", m); + else if (best_ero == c) + m = builddiag("%s self", m); + else + m = builddiag("%s \"%s\"%s %s", m + , best_ero->name + , (fmt_conn_instance(best_ero, cib), cib) + , enum_name(&routing_story, best_ero->spd.routing)); + } + + DBG_log("%s", m); + }); + + if (erop != NULL) + *erop = erouted(best_erouting)? best_ero : NULL; + + if (srp != NULL ) + { + *srp = best_sr; + if (esrp != NULL ) + *esrp = best_esr; + } + + return routed(best_routing)? best_ro : NULL; +} + +/* Find a connection that owns the shunt eroute between subnets. + * There ought to be only one. + * This might get to be a bottleneck -- try hashing if it does. + */ +struct connection * +shunt_owner(const ip_subnet *ours, const ip_subnet *his) +{ + struct connection *c; + struct spd_route *sr; + + for (c = connections; c != NULL; c = c->ac_next) + { + for (sr = &c->spd; sr; sr = sr->next) + { + if (shunt_erouted(sr->routing) + && samesubnet(ours, &sr->this.client) + && samesubnet(his, &sr->that.client)) + return c; + } + } + return NULL; +} + +/* Find some connection with this pair of hosts. + * We don't know enough to chose amongst those available. + * ??? no longer usefully different from find_host_pair_connections + */ +struct connection * +find_host_connection(const ip_address *me, u_int16_t my_port +, const ip_address *him, u_int16_t his_port, lset_t policy) +{ + struct connection *c = find_host_pair_connections(me, my_port, him, his_port); + + if (policy != LEMPTY) + { + lset_t auth_requested = policy & POLICY_ID_AUTH_MASK; + + /* if we have requirements for the policy, + * choose the first matching connection. + */ + while (c != NULL) + { + if (c->policy & auth_requested) + { + break; + } + c = c->hp_next; + } + } + return c; +} + +/* given an up-until-now satisfactory connection, find the best connection + * now that we just got the Phase 1 Id Payload from the peer. + * + * Comments in the code describe the (tricky!) matching criteria. + * Although this routine could handle the initiator case, + * it isn't currently called in this case. + * If it were, it could "upgrade" an Opportunistic Connection + * to a Road Warrior Connection if a suitable Peer ID were found. + * + * In RFC 2409 "The Internet Key Exchange (IKE)", + * in 5.1 "IKE Phase 1 Authenticated With Signatures", describing Main + * Mode: + * + * Initiator Responder + * ----------- ----------- + * HDR, SA --> + * <-- HDR, SA + * HDR, KE, Ni --> + * <-- HDR, KE, Nr + * HDR*, IDii, [ CERT, ] SIG_I --> + * <-- HDR*, IDir, [ CERT, ] SIG_R + * + * In 5.4 "Phase 1 Authenticated With a Pre-Shared Key": + * + * HDR, SA --> + * <-- HDR, SA + * HDR, KE, Ni --> + * <-- HDR, KE, Nr + * HDR*, IDii, HASH_I --> + * <-- HDR*, IDir, HASH_R + * + * refine_host_connection could be called in two case: + * + * - the Responder receives the IDii payload: + * + [PSK] after using PSK to decode this message + * + before sending its IDir payload + * + before using its ID in HASH_R computation + * + [DSig] before using its private key to sign SIG_R + * + before using the Initiator's ID in HASH_I calculation + * + [DSig] before using the Initiator's public key to check SIG_I + * + * - the Initiator receives the IDir payload: + * + [PSK] after using PSK to encode previous message and decode this message + * + after sending its IDii payload + * + after using its ID in HASH_I computation + * + [DSig] after using its private key to sign SIG_I + * + before using the Responder's ID to compute HASH_R + * + [DSig] before using Responder's public key to check SIG_R + * + * refine_host_connection can choose a different connection, as long as + * nothing already used is changed. + * + * In the Initiator case, the particular connection might have been + * specified by whatever provoked Pluto to initiate. For example: + * whack --initiate connection-name + * The advantages of switching connections when we're the Initiator seem + * less important than the disadvantages, so after FreeS/WAN 1.9, we + * don't do this. + */ +struct connection * +refine_host_connection(const struct state *st, const struct id *peer_id +, chunk_t peer_ca) +{ + struct connection *c = st->st_connection; + u_int16_t auth = st->st_oakley.auth; + struct connection *d; + struct connection *best_found = NULL; + lset_t auth_policy; + const chunk_t *psk = NULL; + bool wcpip; /* wildcard Peer IP? */ + + int wildcards, our_pathlen, peer_pathlen; + int best_wildcards = MAX_WILDCARDS; + int best_our_pathlen = MAX_CA_PATH_LEN; + int best_peer_pathlen = MAX_CA_PATH_LEN; + + if (same_id(&c->spd.that.id, peer_id) + && trusted_ca(peer_ca, c->spd.that.ca, &peer_pathlen) + && peer_pathlen == 0 + && match_requested_ca(c->requested_ca, c->spd.this.ca, &our_pathlen) + && our_pathlen == 0) + { + DBG(DBG_CONTROL, + DBG_log("current connection is a full match" + " -- no need to look further"); + ) + return c; + } + + switch (auth) + { + case OAKLEY_PRESHARED_KEY: + auth_policy = POLICY_PSK; + psk = get_preshared_secret(c); + /* It should be virtually impossible to fail to find PSK: + * we just used it to decode the current message! + */ + if (psk == NULL) + return NULL; /* cannot determine PSK! */ + break; + case XAUTHInitPreShared: + case XAUTHRespPreShared: + auth_policy = POLICY_XAUTH_PSK; + psk = get_preshared_secret(c); + if (psk == NULL) + return NULL; /* cannot determine PSK! */ + break; + case OAKLEY_RSA_SIG: + auth_policy = POLICY_RSASIG; + break; + case XAUTHInitRSA: + case XAUTHRespRSA: + auth_policy = POLICY_XAUTH_RSASIG; + break; + default: + bad_case(auth); + } + + /* The current connection won't do: search for one that will. + * First search for one with the same pair of hosts. + * If that fails, search for a suitable Road Warrior or Opportunistic + * connection (i.e. wildcard peer IP). + * We need to match: + * - peer_id (slightly complicated by instantiation) + * - if PSK auth, the key must not change (we used it to decode message) + * - policy-as-used must be acceptable to new connection + */ + d = c->host_pair->connections; + for (wcpip = FALSE; ; wcpip = TRUE) + { + for (; d != NULL; d = d->hp_next) + { + const char *match_name[] = {"no", "ok"}; + + bool matching_id = match_id(peer_id + , &d->spd.that.id, &wildcards); + bool matching_auth = (d->policy & auth_policy) != LEMPTY; + + bool matching_trust = trusted_ca(peer_ca + , d->spd.that.ca, &peer_pathlen); + bool matching_request = match_requested_ca(c->requested_ca + , d->spd.this.ca, &our_pathlen); + bool match = matching_id && matching_auth && + matching_trust && matching_request; + + DBG(DBG_CONTROLMORE, + DBG_log("%s: %s match (id: %s, auth: %s, trust: %s, request: %s)" + , d->name + , match ? "full":" no" + , match_name[matching_id] + , match_name[matching_auth] + , match_name[matching_trust] + , match_name[matching_request]) + ) + + /* do we have a match? */ + if (!match) + continue; + + /* ignore group connections */ + if (d->policy & POLICY_GROUP) + continue; + + if (c->spd.that.host_port != d->spd.that.host_port + && d->kind == CK_INSTANCE) + { + continue; + } + + switch (auth) + { + case OAKLEY_PRESHARED_KEY: + case XAUTHInitPreShared: + case XAUTHRespPreShared: + /* secret must match the one we already used */ + { + const chunk_t *dpsk = get_preshared_secret(d); + + if (dpsk == NULL) + continue; /* no secret */ + + if (psk != dpsk) + if (psk->len != dpsk->len + || memcmp(psk->ptr, dpsk->ptr, psk->len) != 0) + continue; /* different secret */ + } + break; + + case OAKLEY_RSA_SIG: + case XAUTHInitRSA: + case XAUTHRespRSA: + /* + * We must at least be able to find our private key + .*/ + if (d->spd.this.sc == NULL /* no smartcard */ + && get_RSA_private_key(d) == NULL) /* no private key */ + continue; + break; + + default: + bad_case(auth); + } + + /* d has passed all the tests. + * We'll go with it if the Peer ID was an exact match. + */ + if (match && wildcards == 0 && peer_pathlen == 0 && our_pathlen == 0) + return d; + + /* We'll remember it as best_found in case an exact + * match doesn't come along. + */ + if (best_found == NULL || wildcards < best_wildcards + || ((wildcards == best_wildcards && peer_pathlen < best_peer_pathlen) + || (peer_pathlen == best_peer_pathlen && our_pathlen < best_our_pathlen))) + { + best_found = d; + best_wildcards = wildcards; + best_peer_pathlen = peer_pathlen; + best_our_pathlen = our_pathlen; + } + } + if (wcpip) + return best_found; /* been around twice already */ + + /* Starting second time around. + * We're willing to settle for a connection that needs Peer IP + * instantiated: Road Warrior or Opportunistic. + * Look on list of connections for host pair with wildcard Peer IP + */ + d = find_host_pair_connections(&c->spd.this.host_addr, c->spd.this.host_port + , (ip_address *)NULL, c->spd.that.host_port); + } +} + +/** + * With virtual addressing, we must not allow someone to use an already + * used (by another id) addr/net. + */ +static bool +is_virtual_net_used(const ip_subnet *peer_net, const struct id *peer_id) +{ + struct connection *d; + + for (d = connections; d != NULL; d = d->ac_next) + { + switch (d->kind) + { + case CK_PERMANENT: + case CK_INSTANCE: + if ((subnetinsubnet(peer_net,&d->spd.that.client) || + subnetinsubnet(&d->spd.that.client,peer_net)) + && !same_id(&d->spd.that.id, peer_id)) + { + char buf[BUF_LEN]; + char client[SUBNETTOT_BUF]; + + subnettot(peer_net, 0, client, sizeof(client)); + idtoa(&d->spd.that.id, buf, sizeof(buf)); + plog("Virtual IP %s is already used by '%s'", client, buf); + idtoa(peer_id, buf, sizeof(buf)); + plog("Your ID is '%s'", buf); + return TRUE; /* already used by another one */ + } + break; + case CK_GOING_AWAY: + default: + break; + } + } + return FALSE; /* you can safely use it */ +} + +/* find_client_connection: given a connection suitable for ISAKMP + * (i.e. the hosts match), find a one suitable for IPSEC + * (i.e. with matching clients). + * + * If we don't find an exact match (not even our current connection), + * we try for one that still needs instantiation. Try Road Warrior + * abstract connections and the Opportunistic abstract connections. + * This requires inverse instantiation: abstraction. + * + * After failing to find an exact match, we abstract the peer + * to be NO_IP (the wildcard value). This enables matches with + * Road Warrior and Opportunistic abstract connections. + * + * After failing that search, we also abstract the Phase 1 peer ID + * if possible. If the peer's ID was the peer's IP address, we make + * it NO_ID; instantiation will make it the peer's IP address again. + * + * If searching for a Road Warrior abstract connection fails, + * and conditions are suitable, we search for the best Opportunistic + * abstract connection. + * + * Note: in the end, both Phase 1 IDs must be preserved, after any + * instantiation. They are the IDs that have been authenticated. + */ + +#define PATH_WEIGHT 1 +#define WILD_WEIGHT (MAX_CA_PATH_LEN+1) +#define PRIO_WEIGHT (MAX_WILDCARDS+1)*WILD_WEIGHT + +/* fc_try: a helper function for find_client_connection */ +static struct connection * +fc_try(const struct connection *c +, struct host_pair *hp +, const struct id *peer_id +, const ip_subnet *our_net +, const ip_subnet *peer_net +, const u_int8_t our_protocol +, const u_int16_t our_port +, const u_int8_t peer_protocol +, const u_int16_t peer_port +, chunk_t peer_ca +, const ietfAttrList_t *peer_list) +{ + struct connection *d; + struct connection *best = NULL; + policy_prio_t best_prio = BOTTOM_PRIO; + int wildcards, pathlen; + + const bool peer_net_is_host = subnetisaddr(peer_net, &c->spd.that.host_addr); + + for (d = hp->connections; d != NULL; d = d->hp_next) + { + struct spd_route *sr; + + if (d->policy & POLICY_GROUP) + continue; + + if (!(same_id(&c->spd.this.id, &d->spd.this.id) + && match_id(&c->spd.that.id, &d->spd.that.id, &wildcards) + && trusted_ca(peer_ca, d->spd.that.ca, &pathlen) + && group_membership(peer_list, d->name, d->spd.that.groups))) + continue; + + /* compare protocol and ports */ + if (d->spd.this.protocol != our_protocol + || d->spd.this.port != our_port + || d->spd.that.protocol != peer_protocol + || (d->spd.that.port != peer_port && !d->spd.that.has_port_wildcard)) + continue; + + /* non-Opportunistic case: + * our_client must match. + * + * So must peer_client, but the testing is complicated + * by the fact that the peer might be a wildcard + * and if so, the default value of that.client + * won't match the default peer_net. The appropriate test: + * + * If d has a peer client, it must match peer_net. + * If d has no peer client, peer_net must just have peer itself. + */ + + for (sr = &d->spd; best != d && sr != NULL; sr = sr->next) + { + policy_prio_t prio; +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF]; + char s3[SUBNETTOT_BUF],d3[SUBNETTOT_BUF]; + + subnettot(our_net, 0, s1, sizeof(s1)); + subnettot(peer_net, 0, d1, sizeof(d1)); + subnettot(&sr->this.client, 0, s3, sizeof(s3)); + subnettot(&sr->that.client, 0, d3, sizeof(d3)); + DBG_log(" fc_try trying " + "%s:%s:%d/%d -> %s:%d/%d vs %s:%s:%d/%d -> %s:%d/%d" + , c->name, s1, c->spd.this.protocol, c->spd.this.port + , d1, c->spd.that.protocol, c->spd.that.port + , d->name, s3, sr->this.protocol, sr->this.port + , d3, sr->that.protocol, sr->that.port); + } +#endif /* DEBUG */ + + if (!samesubnet(&sr->this.client, our_net)) + continue; + + if (sr->that.has_client) + { + if (sr->that.has_client_wildcard) + { + if (!subnetinsubnet(peer_net, &sr->that.client)) + continue; + } + else + { + if (!samesubnet(&sr->that.client, peer_net) && !is_virtual_connection(d)) + continue; + if (is_virtual_connection(d) + && (!is_virtual_net_allowed(d, peer_net, &c->spd.that.host_addr) + || is_virtual_net_used(peer_net, peer_id?peer_id:&c->spd.that.id))) + continue; + } + } + else + { + if (!peer_net_is_host) + continue; + } + + /* We've run the gauntlet -- success: + * We've got an exact match of subnets. + * The connection is feasible, but we continue looking for the best. + * The highest priority wins, implementing eroute-like rule. + * - a routed connection is preferrred + * - given that, the smallest number of ID wildcards are preferred + * - given that, the shortest CA pathlength is preferred + */ + prio = PRIO_WEIGHT * routed(sr->routing) + + WILD_WEIGHT * (MAX_WILDCARDS - wildcards) + + PATH_WEIGHT * (MAX_CA_PATH_LEN - pathlen) + + 1; + if (prio > best_prio) + { + best = d; + best_prio = prio; + } + } + } + + if (best != NULL && NEVER_NEGOTIATE(best->policy)) + best = NULL; + + DBG(DBG_CONTROLMORE, + DBG_log(" fc_try concluding with %s [%ld]" + , (best ? best->name : "none"), best_prio) + ) + return best; +} + +static struct connection * +fc_try_oppo(const struct connection *c +, struct host_pair *hp +, const ip_subnet *our_net +, const ip_subnet *peer_net +, const u_int8_t our_protocol +, const u_int16_t our_port +, const u_int8_t peer_protocol +, const u_int16_t peer_port +, chunk_t peer_ca +, const ietfAttrList_t *peer_list) +{ + struct connection *d; + struct connection *best = NULL; + policy_prio_t best_prio = BOTTOM_PRIO; + int wildcards, pathlen; + + for (d = hp->connections; d != NULL; d = d->hp_next) + { + struct spd_route *sr; + policy_prio_t prio; + + if (d->policy & POLICY_GROUP) + continue; + + if (!(same_id(&c->spd.this.id, &d->spd.this.id) + && match_id(&c->spd.that.id, &d->spd.that.id, &wildcards) + && trusted_ca(peer_ca, d->spd.that.ca, &pathlen) + && group_membership(peer_list, d->name, d->spd.that.groups))) + continue; + + /* compare protocol and ports */ + if (d->spd.this.protocol != our_protocol + || d->spd.this.port != our_port + || d->spd.that.protocol != peer_protocol + || (d->spd.that.port != peer_port && !d->spd.that.has_port_wildcard)) + continue; + + /* Opportunistic case: + * our_net must be inside d->spd.this.client + * and peer_net must be inside d->spd.that.client + * Note: this host_pair chain also has shunt + * eroute conns (clear, drop), but they won't + * be marked as opportunistic. + */ + for (sr = &d->spd; sr != NULL; sr = sr->next) + { +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF]; + char s3[SUBNETTOT_BUF],d3[SUBNETTOT_BUF]; + + subnettot(our_net, 0, s1, sizeof(s1)); + subnettot(peer_net, 0, d1, sizeof(d1)); + subnettot(&sr->this.client, 0, s3, sizeof(s3)); + subnettot(&sr->that.client, 0, d3, sizeof(d3)); + DBG_log(" fc_try_oppo trying %s:%s -> %s vs %s:%s -> %s" + , c->name, s1, d1, d->name, s3, d3); + } +#endif /* DEBUG */ + + if (!subnetinsubnet(our_net, &sr->this.client) + || !subnetinsubnet(peer_net, &sr->that.client)) + continue; + + /* The connection is feasible, but we continue looking for the best. + * The highest priority wins, implementing eroute-like rule. + * - our smallest client subnet is preferred (longest mask) + * - given that, his smallest client subnet is preferred + * - given that, a routed connection is preferrred + * - given that, the smallest number of ID wildcards are preferred + * - given that, the shortest CA pathlength is preferred + */ + prio = PRIO_WEIGHT * (d->prio + routed(sr->routing)) + + WILD_WEIGHT * (MAX_WILDCARDS - wildcards) + + PATH_WEIGHT * (MAX_CA_PATH_LEN - pathlen); + if (prio > best_prio) + { + best = d; + best_prio = prio; + } + } + } + + /* if the best wasn't opportunistic, we fail: it must be a shunt */ + if (best != NULL + && (NEVER_NEGOTIATE(best->policy) + || (best->policy & POLICY_OPPO) == LEMPTY)) + { + best = NULL; + } + + DBG(DBG_CONTROLMORE, + DBG_log(" fc_try_oppo concluding with %s [%ld]" + , (best ? best->name : "none"), best_prio) + ) + return best; + +} + +/* + * get the peer's CA and group attributes + */ +chunk_t +get_peer_ca_and_groups(struct connection *c, const ietfAttrList_t **peer_list) +{ + struct state *p1st = find_phase1_state(c, ISAKMP_SA_ESTABLISHED_STATES); + + *peer_list = NULL; + + if (p1st != NULL + && p1st->st_peer_pubkey != NULL + && p1st->st_peer_pubkey->issuer.ptr != NULL) + { + x509acert_t *ac = get_x509acert(p1st->st_peer_pubkey->issuer + , p1st->st_peer_pubkey->serial);; + + if (ac != NULL && verify_x509acert(ac, strict_crl_policy)) + *peer_list = ac->groups; + else + { + DBG(DBG_CONTROL, + DBG_log("no valid attribute cert found") + ) + } + return p1st->st_peer_pubkey->issuer; + } + return empty_chunk; +} + +struct connection * +find_client_connection(struct connection *c +, const ip_subnet *our_net, const ip_subnet *peer_net +, const u_int8_t our_protocol, const u_int16_t our_port +, const u_int8_t peer_protocol, const u_int16_t peer_port) +{ + struct connection *d; + struct spd_route *sr; + + const ietfAttrList_t *peer_list = NULL; + chunk_t peer_ca = get_peer_ca_and_groups(c, &peer_list); + +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s1[SUBNETTOT_BUF],d1[SUBNETTOT_BUF]; + + subnettot(our_net, 0, s1, sizeof(s1)); + subnettot(peer_net, 0, d1, sizeof(d1)); + + DBG_log("find_client_connection starting with %s" + , (c ? c->name : "(none)")); + DBG_log(" looking for %s:%d/%d -> %s:%d/%d" + , s1, our_protocol, our_port + , d1, peer_protocol, peer_port); + } +#endif /* DEBUG */ + + /* give priority to current connection + * but even greater priority to a routed concrete connection + */ + { + struct connection *unrouted = NULL; + int srnum = -1; + + for (sr = &c->spd; unrouted == NULL && sr != NULL; sr = sr->next) + { + srnum++; + +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s2[SUBNETTOT_BUF],d2[SUBNETTOT_BUF]; + + subnettot(&sr->this.client, 0, s2, sizeof(s2)); + subnettot(&sr->that.client, 0, d2, sizeof(d2)); + DBG_log(" concrete checking against sr#%d %s -> %s" + , srnum, s2, d2); + } +#endif /* DEBUG */ + + if (samesubnet(&sr->this.client, our_net) + && samesubnet(&sr->that.client, peer_net) + && sr->this.protocol == our_protocol + && sr->this.port == our_port + && sr->that.protocol == peer_protocol + && sr->that.port == peer_port + && group_membership(peer_list, c->name, sr->that.groups)) + { + passert(oriented(*c)); + if (routed(sr->routing)) + return c; + + unrouted = c; + } + } + + /* exact match? */ + d = fc_try(c, c->host_pair, NULL, our_net, peer_net + , our_protocol, our_port, peer_protocol, peer_port + , peer_ca, peer_list); + + DBG(DBG_CONTROLMORE, + DBG_log(" fc_try %s gives %s" + , c->name + , (d ? d->name : "none")) + ) + + if (d == NULL) + d = unrouted; + } + + if (d == NULL) + { + /* look for an abstract connection to match */ + struct spd_route *sr; + struct host_pair *hp = NULL; + + for (sr = &c->spd; hp==NULL && sr != NULL; sr = sr->next) + { + hp = find_host_pair(&sr->this.host_addr + , sr->this.host_port + , NULL + , sr->that.host_port); +#ifdef DEBUG + if (DBGP(DBG_CONTROLMORE)) + { + char s2[SUBNETTOT_BUF],d2[SUBNETTOT_BUF]; + + subnettot(&sr->this.client, 0, s2, sizeof(s2)); + subnettot(&sr->that.client, 0, d2, sizeof(d2)); + + DBG_log(" checking hostpair %s -> %s is %s" + , s2, d2 + , (hp ? "found" : "not found")); + } +#endif /* DEBUG */ + } + + if (hp != NULL) + { + /* RW match with actual peer_id or abstract peer_id? */ + d = fc_try(c, hp, NULL, our_net, peer_net + , our_protocol, our_port, peer_protocol, peer_port + , peer_ca, peer_list); + + if (d == NULL + && subnetishost(our_net) + && subnetishost(peer_net)) + { + /* Opportunistic match? + * Always use abstract peer_id. + * Note that later instantiation will result in the same peer_id. + */ + d = fc_try_oppo(c, hp, our_net, peer_net + , our_protocol, our_port, peer_protocol, peer_port + , peer_ca, peer_list); + } + } + } + + DBG(DBG_CONTROLMORE, + DBG_log(" concluding with d = %s" + , (d ? d->name : "none")) + ) + return d; +} + +int +connection_compare(const struct connection *ca +, const struct connection *cb) +{ + int ret; + + /* DBG_log("comparing %s to %s", ca->name, cb->name); */ + + ret = strcasecmp(ca->name, cb->name); + if (ret != 0) + return ret; + + ret = ca->kind - cb->kind; /* note: enum connection_kind behaves like int */ + if (ret != 0) + return ret; + + /* same name, and same type */ + switch (ca->kind) + { + case CK_INSTANCE: + return ca->instance_serial < cb->instance_serial ? -1 + : ca->instance_serial > cb->instance_serial ? 1 + : 0; + + default: + return ca->prio < cb->prio ? -1 + : ca->prio > cb->prio ? 1 + : 0; + } +} + +static int +connection_compare_qsort(const void *a, const void *b) +{ + return connection_compare(*(const struct connection *const *)a + , *(const struct connection *const *)b); +} + +void +show_connections_status(bool all, const char *name) +{ + struct connection *c; + int count, i; + struct connection **array; + + /* make an array of connections, and sort it */ + count = 0; + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->ikev1 && (name == NULL || streq(c->name, name))) + count++; + } + array = alloc_bytes(sizeof(struct connection *)*count, "connection array"); + + count=0; + for (c = connections; c != NULL; c = c->ac_next) + { + if (c->ikev1 && (name == NULL || streq(c->name, name))) + array[count++]=c; + } + + /* sort it! */ + qsort(array, count, sizeof(struct connection *), connection_compare_qsort); + + for (i = 0; i < count; i++) + { + const char *ifn; + char instance[1 + 10 + 1]; + char prio[POLICY_PRIO_BUF]; + + c = array[i]; + + ifn = oriented(*c)? c->interface->rname : ""; + + instance[0] = '\0'; + if (c->kind == CK_INSTANCE && c->instance_serial != 0) + snprintf(instance, sizeof(instance), "[%lu]", c->instance_serial); + + /* show topology */ + { + char topo[CONNECTION_BUF]; + struct spd_route *sr = &c->spd; + int num=0; + + while (sr != NULL) + { + (void) format_connection(topo, sizeof(topo), c, sr); + whack_log(RC_COMMENT, "\"%s\"%s: %s; %s; eroute owner: #%lu" + , c->name, instance, topo + , enum_name(&routing_story, sr->routing) + , sr->eroute_owner); + sr = sr->next; + num++; + } + } + + if (all) + { + /* show CAs if defined */ + if (c->spd.this.ca.ptr != NULL || c->spd.that.ca.ptr != NULL) + { + char this_ca[BUF_LEN], that_ca[BUF_LEN]; + + dntoa_or_null(this_ca, BUF_LEN, c->spd.this.ca, "%any"); + dntoa_or_null(that_ca, BUF_LEN, c->spd.that.ca, "%any"); + + whack_log(RC_COMMENT + , "\"%s\"%s: CAs: '%s'...'%s'" + , c->name + , instance + , this_ca + , that_ca); + } + + /* show group attributes if defined */ + if (c->spd.that.groups != NULL) + { + char buf[BUF_LEN]; + + format_groups(c->spd.that.groups, buf, BUF_LEN); + whack_log(RC_COMMENT + , "\"%s\"%s: groups: %s" + , c->name + , instance + , buf); + } + + whack_log(RC_COMMENT + , "\"%s\"%s: ike_life: %lus; ipsec_life: %lus;" + " rekey_margin: %lus; rekey_fuzz: %lu%%; keyingtries: %lu" + , c->name + , instance + , (unsigned long) c->sa_ike_life_seconds + , (unsigned long) c->sa_ipsec_life_seconds + , (unsigned long) c->sa_rekey_margin + , (unsigned long) c->sa_rekey_fuzz + , (unsigned long) c->sa_keying_tries); + + /* show DPD parameters if defined */ + + if (c->dpd_action != DPD_ACTION_NONE) + whack_log(RC_COMMENT + , "\"%s\"%s: dpd_action: %s;" + " dpd_delay: %lus; dpd_timeout: %lus;" + , c->name + , instance + , enum_show(&dpd_action_names, c->dpd_action) + , (unsigned long) c->dpd_delay + , (unsigned long) c->dpd_timeout); + + if (c->policy_next) + { + whack_log(RC_COMMENT + , "\"%s\"%s: policy_next: %s" + , c->name, instance, c->policy_next->name); + } + + /* Note: we display key_from_DNS_on_demand as if policy [lr]KOD */ + fmt_policy_prio(c->prio, prio); + whack_log(RC_COMMENT + , "\"%s\"%s: policy: %s%s%s; prio: %s; interface: %s; " + , c->name + , instance + , prettypolicy(c->policy) + , c->spd.this.key_from_DNS_on_demand? "+lKOD" : "" + , c->spd.that.key_from_DNS_on_demand? "+rKOD" : "" + , prio + , ifn); + } + + whack_log(RC_COMMENT + , "\"%s\"%s: newest ISAKMP SA: #%ld; newest IPsec SA: #%ld; " + , c->name + , instance + , c->newest_isakmp_sa + , c->newest_ipsec_sa); + + if (all) + { + ike_alg_show_connection(c, instance); + kernel_alg_show_connection(c, instance); + } + } + if (count > 0) + whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ + + pfree(array); +} + +/* struct pending, the structure representing Quick Mode + * negotiations delayed until a Keying Channel has been negotiated. + * Essentially, a pending call to quick_outI1. + */ + +struct pending { + int whack_sock; + struct state *isakmp_sa; + struct connection *connection; + lset_t policy; + unsigned long try; + so_serial_t replacing; + + struct pending *next; +}; + +/* queue a Quick Mode negotiation pending completion of a suitable Main Mode */ +void +add_pending(int whack_sock +, struct state *isakmp_sa +, struct connection *c +, lset_t policy +, unsigned long try +, so_serial_t replacing) +{ + bool already_queued = FALSE; + struct pending *p = c->host_pair->pending; + + while (p != NULL) + { + if (streq(c->name, p->connection->name)) + { + already_queued = TRUE; + break; + } + p = p->next; + } + DBG(DBG_CONTROL, + DBG_log("Queuing pending Quick Mode with %s \"%s\"%s" + , ip_str(&c->spd.that.host_addr) + , c->name + , already_queued? " already done" : "") + ) + if (already_queued) + return; + + p = alloc_thing(struct pending, "struct pending"); + p->whack_sock = whack_sock; + p->isakmp_sa = isakmp_sa; + p->connection = c; + p->policy = policy; + p->try = try; + p->replacing = replacing; + p->next = c->host_pair->pending; + c->host_pair->pending = p; +} + +/* Release all the whacks awaiting the completion of this state. + * This is accomplished by closing all the whack socket file descriptors. + * We go to a lot of trouble to tell each whack, but to not tell it twice. + */ +void +release_pending_whacks(struct state *st, err_t story) +{ + struct pending *p; + struct stat stst; + + if (st->st_whack_sock == NULL_FD || fstat(st->st_whack_sock, &stst) != 0) + zero(&stst); /* resulting st_dev/st_ino ought to be distinct */ + + release_whack(st); + + for (p = st->st_connection->host_pair->pending; p != NULL; p = p->next) + { + if (p->isakmp_sa == st && p->whack_sock != NULL_FD) + { + struct stat pst; + + if (fstat(p->whack_sock, &pst) == 0 + && (stst.st_dev != pst.st_dev || stst.st_ino != pst.st_ino)) + { + passert(whack_log_fd == NULL_FD); + whack_log_fd = p->whack_sock; + whack_log(RC_COMMENT + , "%s for ISAKMP SA, but releasing whack for pending IPSEC SA" + , story); + whack_log_fd = NULL_FD; + } + close(p->whack_sock); + p->whack_sock = NULL_FD; + } + } +} + +static void +delete_pending(struct pending **pp) +{ + struct pending *p = *pp; + + *pp = p->next; + if (p->connection != NULL) + connection_discard(p->connection); + close_any(p->whack_sock); + pfree(p); +} + +void +unpend(struct state *st) +{ + struct pending **pp + , *p; + + for (pp = &st->st_connection->host_pair->pending; (p = *pp) != NULL; ) + { + if (p->isakmp_sa == st) + { + DBG(DBG_CONTROL, DBG_log("unqueuing pending Quick Mode with %s \"%s\"" + , ip_str(&p->connection->spd.that.host_addr) + , p->connection->name)); + (void) quick_outI1(p->whack_sock, st, p->connection, p->policy + , p->try, p->replacing); + p->whack_sock = NULL_FD; /* ownership transferred */ + p->connection = NULL; /* ownership transferred */ + delete_pending(pp); + } + else + { + pp = &p->next; + } + } +} + +/* a Main Mode negotiation has been replaced; update any pending */ +void +update_pending(struct state *os, struct state *ns) +{ + struct pending *p; + + for (p = os->st_connection->host_pair->pending; p != NULL; p = p->next) + { + if (p->isakmp_sa == os) + p->isakmp_sa = ns; + if (p->connection->spd.this.host_port != ns->st_connection->spd.this.host_port) + { + p->connection->spd.this.host_port = ns->st_connection->spd.this.host_port; + p->connection->spd.that.host_port = ns->st_connection->spd.that.host_port; + } + } +} + +/* a Main Mode negotiation has failed; discard any pending */ +void +flush_pending_by_state(struct state *st) +{ + struct host_pair *hp = st->st_connection->host_pair; + + if (hp != NULL) + { + struct pending **pp + , *p; + + for (pp = &hp->pending; (p = *pp) != NULL; ) + { + if (p->isakmp_sa == st) + delete_pending(pp); + else + pp = &p->next; + } + } +} + +/* a connection has been deleted; discard any related pending */ +static void +flush_pending_by_connection(struct connection *c) +{ + if (c->host_pair != NULL) + { + struct pending **pp + , *p; + + for (pp = &c->host_pair->pending; (p = *pp) != NULL; ) + { + if (p->connection == c) + { + p->connection = NULL; /* prevent delete_pending from releasing */ + delete_pending(pp); + } + else + { + pp = &p->next; + } + } + } +} + +void +show_pending_phase2(const struct host_pair *hp, const struct state *st) +{ + const struct pending *p; + + for (p = hp->pending; p != NULL; p = p->next) + { + if (p->isakmp_sa == st) + { + /* connection-name state-number [replacing state-number] */ + char cip[CONN_INST_BUF]; + + fmt_conn_instance(p->connection, cip); + whack_log(RC_COMMENT, "#%lu: pending Phase 2 for \"%s\"%s replacing #%lu" + , p->isakmp_sa->st_serialno + , p->connection->name + , cip + , p->replacing); + } + } +} + +/* Delete a connection if it is an instance and it is no longer in use. + * We must be careful to avoid circularity: + * we don't touch it if it is CK_GOING_AWAY. + */ +void +connection_discard(struct connection *c) +{ + if (c->kind == CK_INSTANCE) + { + /* see if it is being used by a pending */ + struct pending *p; + + for (p = c->host_pair->pending; p != NULL; p = p->next) + if (p->connection == c) + return; /* in use, so we're done */ + + if (!states_use_connection(c)) + delete_connection(c, FALSE); + } +} + + +/* A template connection's eroute can be eclipsed by + * either a %hold or an eroute for an instance iff + * the template is a /32 -> /32. This requires some special casing. + */ + +long eclipse_count = 0; + +struct connection * +eclipsed(struct connection *c, struct spd_route **esrp) +{ + struct connection *ue; + struct spd_route *sr1 = &c->spd; + + ue = NULL; + + while (sr1 != NULL && ue != NULL) + { + for (ue = connections; ue != NULL; ue = ue->ac_next) + { + struct spd_route *srue = &ue->spd; + + while (srue != NULL + && srue->routing == RT_ROUTED_ECLIPSED + && !(samesubnet(&sr1->this.client, &srue->this.client) + && samesubnet(&sr1->that.client, &srue->that.client))) + { + srue = srue->next; + } + if (srue != NULL && srue->routing==RT_ROUTED_ECLIPSED) + { + *esrp = srue; + break; + } + } + } + return ue; +} + +/* + * Local Variables: + * c-basic-offset:4 + * c-style: pluto + * End: + */ diff --git a/src/pluto/connections.h b/src/pluto/connections.h new file mode 100644 index 000000000..df3af9dd4 --- /dev/null +++ b/src/pluto/connections.h @@ -0,0 +1,367 @@ +/* information about connections between hosts and clients + * Copyright (C) 1998-2001 D. Hugh Redelmeier + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: connections.h,v 1.18 2006/04/22 21:59:20 as Exp $ + */ + +#ifndef _CONNECTIONS_H +#define _CONNECTIONS_H + +#include + +#include "id.h" +#include "certs.h" +#include "ac.h" +#include "smartcard.h" +#include "whack.h" + +/* There are two kinds of connections: + * - ISAKMP connections, between hosts (for IKE communication) + * - IPsec connections, between clients (for secure IP communication) + * + * An ISAKMP connection looks like: + * host<--->host + * + * An IPsec connection looks like: + * client-subnet<-->host<->nexthop<--->nexthop<->host<-->client-subnet + * + * For the connection to be relevant to this instance of Pluto, + * exactly one of the hosts must be a public interface of our machine + * known to this instance. + * + * The client subnet might simply be the host -- this is a + * representation of "host mode". + * + * Each nexthop defaults to the neighbouring host's IP address. + * The nexthop is a property of the pair of hosts, not each + * individually. It is only needed for IPsec because of the + * way IPsec is mixed into the kernel routing logic. Furthermore, + * only this end's nexthop is actually used. Eventually, nexthop + * will be unnecessary. + * + * Other information represented: + * - each connection has a name: a chunk of uninterpreted text + * that is unique for each connection. + * - security requirements (currently just the "policy" flags from + * the whack command to initiate the connection, but eventually + * much more. Different for ISAKMP and IPsec connections. + * - rekeying parameters: + * + time an SA may live + * + time before SA death that a rekeying should be attempted + * (only by the initiator) + * + number of times to attempt rekeying + * - With the current KLIPS, we must route packets for a client + * subnet through the ipsec interface (ipsec0). Only one + * gateway can get traffic for a specific (client) subnet. + * Furthermore, if the routing isn't in place, packets will + * be sent in the clear. + * "routing" indicates whether the routing has been done for + * this connection. Note that several connections may claim + * the same routing, as long as they agree about where the + * packets are to be sent. + * - With the current KLIPS, only one outbound IPsec SA bundle can be + * used for a particular client. This is due to a limitation + * of using only routing for selection. So only one IPsec state (SA) + * may "own" the eroute. "eroute_owner" is the serial number of + * this state, SOS_NOBODY if there is none. "routing" indicates + * what kind of erouting has been done for this connection, if any. + * + * Details on routing is in constants.h + * + * Operations on Connections: + * + * - add a new connection (with all details) [whack command] + * - delete a connection (by name) [whack command] + * - initiate a connection (by name) [whack command] + * - find a connection (by IP addresses of hosts) + * [response to peer request; finding ISAKMP connection for IPsec connection] + * + * Some connections are templates, missing the address of the peer + * (represented by INADDR_ANY). These are always arranged so that the + * missing end is "that" (there can only be one missing end). These can + * be instantiated (turned into real connections) by Pluto in one of two + * different ways: Road Warrior Instantiation or Opportunistic + * Instantiation. A template connection is marked for Opportunistic + * Instantiation by specifying the peer client as 0.0.0.0/32 (or the IPV6 + * equivalent). Otherwise, it is suitable for Road Warrior Instantiation. + * + * Instantiation creates a new temporary connection, with the missing + * details filled in. The resulting template lasts only as long as there + * is a state that uses it. + */ + +/* connection policy priority: how important this policy is + * - used to implement eroute-like precedence (augmented by a small + * bonus for a routed connection). + * - a whole number + * - larger is more important + * - three subcomponents. In order of decreasing significance: + * + length of source subnet mask (8 bits) + * + length of destination subnet mask (8 bits) + * + bias (8 bit) + * - a bias of 1 is added to allow prio BOTTOM_PRIO to be less than all + * normal priorities + * - other bias values are created on the fly to give mild preference + * to certaion conditions (eg. routedness) + * - priority is inherited -- an instance of a policy has the same priority + * as the original policy, even though its subnets might be smaller. + * - display format: n,m + */ +typedef unsigned long policy_prio_t; +#define BOTTOM_PRIO ((policy_prio_t)0) /* smaller than any real prio */ +#define set_policy_prio(c) { (c)->prio = \ + ((policy_prio_t)(c)->spd.this.client.maskbits << 16) \ + | ((policy_prio_t)(c)->spd.that.client.maskbits << 8) \ + | (policy_prio_t)1; } +#define POLICY_PRIO_BUF (3+1+3+1) +extern void fmt_policy_prio(policy_prio_t pp, char buf[POLICY_PRIO_BUF]); + +struct virtual_t; + +struct end { + struct id id; + ip_address + host_addr, + host_nexthop, + host_srcip; + ip_subnet client; + + bool key_from_DNS_on_demand; + bool has_client; + bool has_client_wildcard; + bool has_port_wildcard; + bool has_id_wildcards; + bool has_natip; + char *updown; + u_int16_t host_port; /* host order */ + u_int16_t port; /* host order */ + u_int8_t protocol; + cert_t cert; /* end certificate */ + chunk_t ca; /* CA distinguished name */ + struct ietfAttrList *groups;/* access control groups */ + smartcard_t *sc; /* smartcard reader and key info */ + struct virtual_t *virt; + bool modecfg; /* this end: request local address from server */ + /* that end: give local addresses to clients */ + bool hostaccess; /* allow access to host via iptables INPUT/OUTPUT */ + /* rules if client behind host is a subnet */ + certpolicy_t sendcert; /* whether or not to send the certificate */ +}; + +struct spd_route { + struct spd_route *next; + struct end this; + struct end that; + so_serial_t eroute_owner; + enum routing_t routing; /* level of routing in place */ + uint32_t reqid; +}; + +struct connection { + char *name; + bool ikev1; + + lset_t policy; + time_t sa_ike_life_seconds; + time_t sa_ipsec_life_seconds; + time_t sa_rekey_margin; + unsigned long sa_rekey_fuzz; + unsigned long sa_keying_tries; + + /* RFC 3706 DPD */ + time_t dpd_delay; + time_t dpd_timeout; + dpd_action_t dpd_action; + + char *log_file_name; /* name of log file */ + FILE *log_file; /* possibly open FILE */ + CIRCLEQ_ENTRY(connection) log_link; /* linked list of open conns */ + bool log_file_err; /* only bitch once */ + + struct spd_route spd; + + /* internal fields: */ + + unsigned long instance_serial; + policy_prio_t prio; + bool instance_initiation_ok; /* this is an instance of a policy that mandates initiate */ + enum connection_kind kind; + const struct iface *interface; /* filled in iff oriented */ + + so_serial_t /* state object serial number */ + newest_isakmp_sa, + newest_ipsec_sa; + + +#ifdef DEBUG + lset_t extra_debugging; +#endif + + /* note: if the client is the gateway, the following must be equal */ + sa_family_t addr_family; /* between gateways */ + sa_family_t tunnel_addr_family; /* between clients */ + + struct connection *policy_next; /* if multiple policies, + next one to apply */ + + struct gw_info *gw_info; + struct alg_info_esp *alg_info_esp; + struct alg_info_ike *alg_info_ike; + + struct host_pair *host_pair; + struct connection *hp_next; /* host pair list link */ + + struct connection *ac_next; /* all connections list link */ + + generalName_t *requested_ca; /* collected certificate requests */ + bool got_certrequest; +}; + +#define oriented(c) ((c).interface != NULL) +extern bool orient(struct connection *c); + +extern bool same_peer_ids(const struct connection *c + , const struct connection *d, const struct id *his_id); + +/* Format the topology of a connection end, leaving out defaults. + * Largest left end looks like: client === host : port [ host_id ] --- hop + * Note: if that==NULL, skip nexthop + */ +#define END_BUF (SUBNETTOT_BUF + ADDRTOT_BUF + IDTOA_BUF + ADDRTOT_BUF + 10) +extern size_t format_end(char *buf, size_t buf_len + , const struct end *this, const struct end *that + , bool is_left, lset_t policy); + +extern void add_connection(const whack_message_t *wm); +extern void initiate_connection(const char *name, int whackfd); +extern void initiate_opportunistic(const ip_address *our_client + , const ip_address *peer_client, int transport_proto, bool held, int whackfd); +extern void terminate_connection(const char *nm); +extern void release_connection(struct connection *c, bool relations); +extern void delete_connection(struct connection *c, bool relations); +extern void delete_connections_by_name(const char *name, bool strict); +extern void delete_every_connection(void); +extern char *add_group_instance(struct connection *group, const ip_subnet *target); +extern void remove_group_instance(const struct connection *group, const char *name); +extern void release_dead_interfaces(void); +extern void check_orientations(void); +extern struct connection *route_owner(struct connection *c + , struct spd_route **srp + , struct connection **erop + , struct spd_route **esrp); +extern struct connection *shunt_owner(const ip_subnet *ours + , const ip_subnet *his); + +extern bool uniqueIDs; /* --uniqueids? */ +extern void ISAKMP_SA_established(struct connection *c, so_serial_t serial); + +#define his_id_was_instantiated(c) ((c)->kind == CK_INSTANCE \ + && (id_is_ipaddr(&(c)->spd.that.id)? \ + sameaddr(&(c)->spd.that.id.ip_addr, &(c)->spd.that.host_addr) : TRUE)) + +struct state; /* forward declaration of tag (defined in state.h) */ +extern struct connection + *con_by_name(const char *nm, bool strict), + *find_host_connection(const ip_address *me, u_int16_t my_port + , const ip_address *him, u_int16_t his_port, lset_t policy), + *refine_host_connection(const struct state *st, const struct id *id + , chunk_t peer_ca), + *find_client_connection(struct connection *c + , const ip_subnet *our_net + , const ip_subnet *peer_net + , const u_int8_t our_protocol + , const u_int16_t out_port + , const u_int8_t peer_protocol + , const u_int16_t peer_port), + *find_connection_by_reqid(uint32_t reqid); + +extern struct connection * +find_connection_for_clients(struct spd_route **srp + , const ip_address *our_client + , const ip_address *peer_client + , int transport_proto); + +extern chunk_t get_peer_ca_and_groups(struct connection *c + , const ietfAttrList_t **peer_list); + +/* instantiating routines + * Note: connection_discard() is in state.h because all its work + * is looking through state objects. + */ +struct gw_info; /* forward declaration of tag (defined in dnskey.h) */ +struct alg_info; /* forward declaration of tag (defined in alg_info.h) */ +extern struct connection *rw_instantiate(struct connection *c + , const ip_address *him + , u_int16_t his_port + , const ip_subnet *his_net + , const struct id *his_id); + +extern struct connection *oppo_instantiate(struct connection *c + , const ip_address *him + , const struct id *his_id + , struct gw_info *gw + , const ip_address *our_client + , const ip_address *peer_client); + +extern struct connection + *build_outgoing_opportunistic_connection(struct gw_info *gw + , const ip_address *our_client + , const ip_address *peer_client); + +/* worst case: "[" serial "] " myclient "=== ..." peer "===" hisclient '\0' */ +#define CONN_INST_BUF \ + (2 + 10 + 1 + SUBNETTOT_BUF + 7 + ADDRTOT_BUF + 3 + SUBNETTOT_BUF + 1) + +extern void fmt_conn_instance(const struct connection *c + , char buf[CONN_INST_BUF]); + +/* operations on "pending", the structure representing Quick Mode + * negotiations delayed until a Keying Channel has been negotiated. + */ + +struct pending; /* forward declaration (opaque outside connections.c) */ + +extern void add_pending(int whack_sock + , struct state *isakmp_sa + , struct connection *c + , lset_t policy + , unsigned long try + , so_serial_t replacing); + +extern void release_pending_whacks(struct state *st, err_t story); +extern void unpend(struct state *st); +extern void update_pending(struct state *os, struct state *ns); +extern void flush_pending_by_state(struct state *st); +extern void show_pending_phase2(const struct host_pair *hp, const struct state *st); + +extern void connection_discard(struct connection *c); + +/* A template connection's eroute can be eclipsed by + * either a %hold or an eroute for an instance iff + * the template is a /32 -> /32. This requires some special casing. + */ +#define eclipsable(sr) (subnetishost(&(sr)->this.client) && subnetishost(&(sr)->that.client)) +extern long eclipse_count; +extern struct connection *eclipsed(struct connection *c, struct spd_route **); + + +/* print connection status */ + +extern void show_connections_status(bool all, const char *name); +extern int connection_compare(const struct connection *ca + , const struct connection *cb); +extern void update_host_pair(const char *why, struct connection *c + , const ip_address *myaddr, u_int16_t myport + , const ip_address *hisaddr, u_int16_t hisport); + +#endif /* _CONNECTIONS_H */ diff --git a/src/pluto/constants.c b/src/pluto/constants.c new file mode 100644 index 000000000..e7d7216ee --- /dev/null +++ b/src/pluto/constants.c @@ -0,0 +1,1353 @@ +/* tables of names for values defined in constants.h + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: constants.c,v 1.21 2006/03/27 07:38:59 as Exp $ + */ + +/* + * Note that the array sizes are all specified; this is to enable range + * checking by code that only includes constants.h. + */ + +#include +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "packet.h" + +/* string naming compile-time options that have interop implications */ + +const char compile_time_interop_options[] = "" +#ifdef THREADS + " THREADS" +#endif +#ifdef LIBCURL + " LIBCURL" +#endif +#ifdef LIBLDAP + " LIBLDAP" +#endif +#ifdef SMARTCARD + " SMARTCARD" +#endif +#ifdef VENDORID + " VENDORID" +#endif +#ifdef CISCO_QUIRKS + " CISCO_QUIRKS" +#endif +#ifdef USE_KEYRR + " KEYRR" +#endif + ; + +/* version */ + +static const char *const version_name[] = { + "ISAKMP Version 1.0", +}; + +enum_names version_names = + { ISAKMP_MAJOR_VERSION< if not officially defined + */ +static const char *const esp_transform_name_high[] = { + "ESP_SERPENT", + "ESP_TWOFISH" + }; + +enum_names esp_transformid_names_high = + { ESP_SERPENT, ESP_TWOFISH, esp_transform_name_high, NULL }; + +enum_names esp_transformid_names = + { ESP_DES_IV64, ESP_AES_CCM_16, esp_transform_name, &esp_transformid_names_high }; + +/* IPCOMP transform values */ + +static const char *const ipcomp_transform_name[] = { + "IPCOMP_OUI", + "IPCOMP_DEFLAT", + "IPCOMP_LZS", + "IPCOMP_LZJH", + }; + +enum_names ipcomp_transformid_names = + { IPCOMP_OUI, IPCOMP_LZJH, ipcomp_transform_name, NULL }; + +/* Identification type values */ + +static const char *const ident_name[] = { + "ID_IPV4_ADDR", + "ID_FQDN", + "ID_USER_FQDN", + "ID_IPV4_ADDR_SUBNET", + "ID_IPV6_ADDR", + "ID_IPV6_ADDR_SUBNET", + "ID_IPV4_ADDR_RANGE", + "ID_IPV6_ADDR_RANGE", + "ID_DER_ASN1_DN", + "ID_DER_ASN1_GN", + "ID_KEY_ID", + }; + +enum_names ident_names = + { ID_IPV4_ADDR, ID_KEY_ID, ident_name, NULL }; + +/* Certificate type values */ + +static const char *const cert_type_name[] = { + "CERT_NONE", + "CERT_PKCS7_WRAPPED_X509", + "CERT_PGP", + "CERT_DNS_SIGNED_KEY", + "CERT_X509_SIGNATURE", + "CERT_X509_KEY_EXCHANGE", + "CERT_KERBEROS_TOKENS", + "CERT_CRL", + "CERT_ARL", + "CERT_SPKI", + "CERT_X509_ATTRIBUTE", + }; + +enum_names cert_type_names = + { CERT_NONE, CERT_X509_ATTRIBUTE, cert_type_name, NULL }; + +/* Certificate policy names */ + +static const char *const cert_policy_name[] = { + "ALWAYS_SEND", + "SEND_IF_ASKED", + "NEVER_SEND", + }; + +enum_names cert_policy_names = + { CERT_ALWAYS_SEND, CERT_NEVER_SEND, cert_policy_name, NULL }; + +/* Goal BITs for establishing an SA + * Note: we drop the POLICY_ prefix so that logs are more concise. + */ + +const char *const sa_policy_bit_names[] = { + "PSK", + "RSASIG", + "ENCRYPT", + "AUTHENTICATE", + "COMPRESS", + "TUNNEL", + "PFS", + "DISABLEARRIVALCHECK", + "SHUNT0", + "SHUNT1", + "FAILSHUNT0", + "FAILSHUNT1", + "DONTREKEY", + "OPPORTUNISTIC", + "GROUP", + "GROUTED", + "UP", + "MODECFGPUSH", + "XAUTHPSK", + "XAUTHRSASIG", + "XAUTHSERVER", + "DONTREAUTH", + NULL + }; + +const char *const policy_shunt_names[4] = { + "TRAP", + "PASS", + "DROP", + "REJECT", + }; + +const char *const policy_fail_names[4] = { + "NONE", + "PASS", + "DROP", + "REJECT", + }; + +/* Oakley transform attributes + * oakley_attr_bit_names does double duty: it is used for enum names + * and bit names. + */ + +const char *const oakley_attr_bit_names[] = { + "OAKLEY_ENCRYPTION_ALGORITHM", + "OAKLEY_HASH_ALGORITHM", + "OAKLEY_AUTHENTICATION_METHOD", + "OAKLEY_GROUP_DESCRIPTION", + "OAKLEY_GROUP_TYPE", + "OAKLEY_GROUP_PRIME", + "OAKLEY_GROUP_GENERATOR_ONE", + "OAKLEY_GROUP_GENERATOR_TWO", + "OAKLEY_GROUP_CURVE_A", + "OAKLEY_GROUP_CURVE_B", + "OAKLEY_LIFE_TYPE", + "OAKLEY_LIFE_DURATION", + "OAKLEY_PRF", + "OAKLEY_KEY_LENGTH", + "OAKLEY_FIELD_SIZE", + "OAKLEY_GROUP_ORDER", + "OAKLEY_BLOCK_SIZE", + NULL + }; + +static const char *const oakley_var_attr_name[] = { + "OAKLEY_GROUP_PRIME (variable length)", + "OAKLEY_GROUP_GENERATOR_ONE (variable length)", + "OAKLEY_GROUP_GENERATOR_TWO (variable length)", + "OAKLEY_GROUP_CURVE_A (variable length)", + "OAKLEY_GROUP_CURVE_B (variable length)", + NULL, + "OAKLEY_LIFE_DURATION (variable length)", + NULL, + NULL, + NULL, + "OAKLEY_GROUP_ORDER (variable length)", + }; + +static enum_names oakley_attr_desc_tv = { + OAKLEY_ENCRYPTION_ALGORITHM + ISAKMP_ATTR_AF_TV, + OAKLEY_GROUP_ORDER + ISAKMP_ATTR_AF_TV, oakley_attr_bit_names, NULL }; + +enum_names oakley_attr_names = { + OAKLEY_GROUP_PRIME, OAKLEY_GROUP_ORDER, + oakley_var_attr_name, &oakley_attr_desc_tv }; + +/* for each Oakley attribute, which enum_names describes its values? */ +enum_names *oakley_attr_val_descs[] = { + NULL, /* (none) */ + &oakley_enc_names, /* OAKLEY_ENCRYPTION_ALGORITHM */ + &oakley_hash_names, /* OAKLEY_HASH_ALGORITHM */ + &oakley_auth_names, /* OAKLEY_AUTHENTICATION_METHOD */ + &oakley_group_names, /* OAKLEY_GROUP_DESCRIPTION */ + &oakley_group_type_names,/* OAKLEY_GROUP_TYPE */ + NULL, /* OAKLEY_GROUP_PRIME */ + NULL, /* OAKLEY_GROUP_GENERATOR_ONE */ + NULL, /* OAKLEY_GROUP_GENERATOR_TWO */ + NULL, /* OAKLEY_GROUP_CURVE_A */ + NULL, /* OAKLEY_GROUP_CURVE_B */ + &oakley_lifetime_names, /* OAKLEY_LIFE_TYPE */ + NULL, /* OAKLEY_LIFE_DURATION */ + &oakley_prf_names, /* OAKLEY_PRF */ + NULL, /* OAKLEY_KEY_LENGTH */ + NULL, /* OAKLEY_FIELD_SIZE */ + NULL, /* OAKLEY_GROUP_ORDER */ + }; + +/* IPsec DOI attributes (RFC 2407 "IPsec DOI" section 4.5) */ + +static const char *const ipsec_attr_name[] = { + "SA_LIFE_TYPE", + "SA_LIFE_DURATION", + "GROUP_DESCRIPTION", + "ENCAPSULATION_MODE", + "AUTH_ALGORITHM", + "KEY_LENGTH", + "KEY_ROUNDS", + "COMPRESS_DICT_SIZE", + "COMPRESS_PRIVATE_ALG", + }; + +static const char *const ipsec_var_attr_name[] = { + "SA_LIFE_DURATION (variable length)", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "COMPRESS_PRIVATE_ALG (variable length)", + }; + +static enum_names ipsec_attr_desc_tv = { + SA_LIFE_TYPE + ISAKMP_ATTR_AF_TV, + COMPRESS_PRIVATE_ALG + ISAKMP_ATTR_AF_TV, + ipsec_attr_name, NULL }; + +enum_names ipsec_attr_names = { + SA_LIFE_DURATION, COMPRESS_PRIVATE_ALG, + ipsec_var_attr_name, &ipsec_attr_desc_tv }; + +/* for each IPsec attribute, which enum_names describes its values? */ +enum_names *ipsec_attr_val_descs[] = { + NULL, /* (none) */ + &sa_lifetime_names, /* SA_LIFE_TYPE */ + NULL, /* SA_LIFE_DURATION */ + &oakley_group_names, /* GROUP_DESCRIPTION */ + &enc_mode_names, /* ENCAPSULATION_MODE */ + &auth_alg_names, /* AUTH_ALGORITHM */ + NULL, /* KEY_LENGTH */ + NULL, /* KEY_ROUNDS */ + NULL, /* COMPRESS_DICT_SIZE */ + NULL, /* COMPRESS_PRIVATE_ALG */ + }; + +/* SA Lifetime Type attribute */ + +static const char *const sa_lifetime_name[] = { + "SA_LIFE_TYPE_SECONDS", + "SA_LIFE_TYPE_KBYTES", + }; + +enum_names sa_lifetime_names = + { SA_LIFE_TYPE_SECONDS, SA_LIFE_TYPE_KBYTES, sa_lifetime_name, NULL }; + +/* Encapsulation Mode attribute */ + +static const char *const enc_mode_name[] = { + "ENCAPSULATION_MODE_TUNNEL", + "ENCAPSULATION_MODE_TRANSPORT", + "ENCAPSULATION_MODE_UDP_TUNNEL", + "ENCAPSULATION_MODE_UDP_TRANSPORT", + }; + +static const char *const enc_udp_mode_name[] = { + "ENCAPSULATION_MODE_UDP_TUNNEL", + "ENCAPSULATION_MODE_UDP_TRANSPORT", + }; + +static enum_names enc_udp_mode_names = + { ENCAPSULATION_MODE_UDP_TUNNEL_DRAFTS, ENCAPSULATION_MODE_UDP_TRANSPORT_DRAFTS, enc_udp_mode_name, NULL }; + +enum_names enc_mode_names = + { ENCAPSULATION_MODE_TUNNEL, ENCAPSULATION_MODE_UDP_TRANSPORT_RFC, enc_mode_name, &enc_udp_mode_names }; + +/* Auth Algorithm attribute */ + +static const char *const auth_alg_name[] = { + "AUTH_ALGORITHM_HMAC_MD5", + "AUTH_ALGORITHM_HMAC_SHA1", + "AUTH_ALGORITHM_DES_MAC", + "AUTH_ALGORITHM_KPDK", + "AUTH_ALGORITHM_HMAC_SHA2_256", + "AUTH_ALGORITHM_HMAC_SHA2_384", + "AUTH_ALGORITHM_HMAC_SHA2_512", + "AUTH_ALGORITHM_HMAC_RIPEMD", + }; + +static const char *const extended_auth_alg_name[] = { + "AUTH_ALGORITHM_NULL" + }; + +enum_names extended_auth_alg_names = + { AUTH_ALGORITHM_NULL, AUTH_ALGORITHM_NULL, extended_auth_alg_name, NULL }; + +enum_names auth_alg_names = + { AUTH_ALGORITHM_HMAC_MD5, AUTH_ALGORITHM_HMAC_RIPEMD, auth_alg_name + , &extended_auth_alg_names }; + +/* From draft-beaulieu-ike-xauth */ +static const char *const xauth_type_name[] = { + "Generic", + "RADIUS-CHAP", + "OTP", + "S/KEY", +}; + +enum_names xauth_type_names = + { XAUTH_TYPE_GENERIC, XAUTH_TYPE_SKEY, xauth_type_name, NULL}; + +/* From draft-beaulieu-ike-xauth */ +static const char *const xauth_attr_tv_name[] = { + "XAUTH_TYPE", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "XAUTH_STATUS", + }; + +enum_names xauth_attr_tv_names = { + XAUTH_TYPE + ISAKMP_ATTR_AF_TV, + XAUTH_STATUS + ISAKMP_ATTR_AF_TV, xauth_attr_tv_name, NULL }; + +static const char *const unity_attr_name[] = { + "UNITY_BANNER", + "UNITY_SAVE_PASSWD", + "UNITY_DEF_DOMAIN", + "UNITY_SPLITDNS_NAME", + "UNITY_SPLIT_INCLUDE", + "UNITY_NATT_PORT", + "UNITY_LOCAL_LAN", + "UNITY_PFS", + "UNITY_FW_TYPE", + "UNITY_BACKUP_SERVERS", + "UNITY_DDNS_HOSTNAME", +}; + +enum_names unity_attr_names = + { UNITY_BANNER , UNITY_DDNS_HOSTNAME, unity_attr_name , &xauth_attr_tv_names }; + + +static const char *const xauth_attr_name[] = { + "XAUTH_USER_NAME", + "XAUTH_USER_PASSWORD", + "XAUTH_PASSCODE", + "XAUTH_MESSAGE", + "XAUTH_CHALLENGE", + "XAUTH_DOMAIN", + "XAUTH_STATUS (wrong TLV syntax, should be TV)", + "XAUTH_NEXT_PIN", + "XAUTH_ANSWER", + }; + +enum_names xauth_attr_names = + { XAUTH_USER_NAME , XAUTH_ANSWER, xauth_attr_name , &unity_attr_names }; + +static const char *const modecfg_attr_name[] = { + "INTERNAL_IP4_ADDRESS", + "INTERNAL_IP4_NETMASK", + "INTERNAL_IP4_DNS", + "INTERNAL_IP4_NBNS", + "INTERNAL_ADDRESS_EXPIRY", + "INTERNAL_IP4_DHCP", + "APPLICATION_VERSION", + "INTERNAL_IP6_ADDRESS", + "INTERNAL_IP6_NETMASK", + "INTERNAL_IP6_DNS", + "INTERNAL_IP6_NBNS", + "INTERNAL_IP6_DHCP", + "INTERNAL_IP4_SUBNET", + "SUPPORTED_ATTRIBUTES", + "INTERNAL_IP6_SUBNET", + }; + +enum_names modecfg_attr_names = + { INTERNAL_IP4_ADDRESS, INTERNAL_IP6_SUBNET, modecfg_attr_name , &xauth_attr_names }; + +/* Oakley Lifetime Type attribute */ + +static const char *const oakley_lifetime_name[] = { + "OAKLEY_LIFE_SECONDS", + "OAKLEY_LIFE_KILOBYTES", + }; + +enum_names oakley_lifetime_names = + { OAKLEY_LIFE_SECONDS, OAKLEY_LIFE_KILOBYTES, oakley_lifetime_name, NULL }; + +/* Oakley PRF attribute (none defined) */ + +enum_names oakley_prf_names = + { 1, 0, NULL, NULL }; + +/* Oakley Encryption Algorithm attribute */ + +static const char *const oakley_enc_name[] = { + "OAKLEY_DES_CBC", + "OAKLEY_IDEA_CBC", + "OAKLEY_BLOWFISH_CBC", + "OAKLEY_RC5_R16_B64_CBC", + "OAKLEY_3DES_CBC", + "OAKLEY_CAST_CBC", + "OAKLEY_AES_CBC", + }; + +#ifdef NO_EXTRA_IKE +enum_names oakley_enc_names = + { OAKLEY_DES_CBC, OAKLEY_AES_CBC, oakley_enc_name, NULL }; +#else +static const char *const oakley_enc_name_draft_aes_cbc_02[] = { + "OAKLEY_MARS_CBC" /* 65001 */, + "OAKLEY_RC6_CBC" /* 65002 */, + "OAKLEY_ID_65003" /* 65003 */, + "OAKLEY_SERPENT_CBC" /* 65004 */, + "OAKLEY_TWOFISH_CBC" /* 65005 */, +}; +static const char *const oakley_enc_name_ssh[] = { + "OAKLEY_TWOFISH_CBC_SSH", +}; +enum_names oakley_enc_names_ssh = + { OAKLEY_TWOFISH_CBC_SSH, OAKLEY_TWOFISH_CBC_SSH, oakley_enc_name_ssh + , NULL }; + +enum_names oakley_enc_names_draft_aes_cbc_02 = + { OAKLEY_MARS_CBC, OAKLEY_TWOFISH_CBC, oakley_enc_name_draft_aes_cbc_02 + , &oakley_enc_names_ssh }; + +enum_names oakley_enc_names = + { OAKLEY_DES_CBC, OAKLEY_AES_CBC, oakley_enc_name + , &oakley_enc_names_draft_aes_cbc_02 }; +#endif + +/* Oakley Hash Algorithm attribute */ + +static const char *const oakley_hash_name[] = { + "OAKLEY_MD5", + "OAKLEY_SHA", + "OAKLEY_TIGER", + "OAKLEY_SHA2_256", + "OAKLEY_SHA2_384", + "OAKLEY_SHA2_512", + }; + +enum_names oakley_hash_names = + { OAKLEY_MD5, OAKLEY_SHA2_512, oakley_hash_name, NULL }; + +/* Oakley Authentication Method attribute */ + +static const char *const oakley_auth_name1[] = { + "OAKLEY_PRESHARED_KEY", + "OAKLEY_DSS_SIG", + "OAKLEY_RSA_SIG", + "OAKLEY_RSA_ENC", + "OAKLEY_RSA_ENC_REV", + "OAKLEY_ELGAMAL_ENC", + "OAKLEY_ELGAMAL_ENC_REV", + }; + +static const char *const oakley_auth_name2[] = { + "HybridInitRSA", + "HybridRespRSA", + "HybridInitDSS", + "HybridRespDSS", + }; + +static const char *const oakley_auth_name3[] = { + "XAUTHInitPreShared", + "XAUTHRespPreShared", + "XAUTHInitDSS", + "XAUTHRespDSS", + "XAUTHInitRSA", + "XAUTHRespRSA", + "XAUTHInitRSAEncryption", + "XAUTHRespRSAEncryption", + "XAUTHInitRSARevisedEncryption", + "XAUTHRespRSARevisedEncryption", + }; + +static enum_names oakley_auth_names1 = + { OAKLEY_PRESHARED_KEY, OAKLEY_ELGAMAL_ENC_REV + , oakley_auth_name1, NULL }; + +static enum_names oakley_auth_names2 = + { HybridInitRSA, HybridRespDSS + , oakley_auth_name2, &oakley_auth_names1 }; + +enum_names oakley_auth_names = + { XAUTHInitPreShared, XAUTHRespRSARevisedEncryption + , oakley_auth_name3, &oakley_auth_names2 }; + +/* Oakley Group Description attribute */ + +static const char *const oakley_group_name[] = { + "OAKLEY_GROUP_MODP768", + "OAKLEY_GROUP_MODP1024", + "OAKLEY_GROUP_GP155", + "OAKLEY_GROUP_GP185", + "OAKLEY_GROUP_MODP1536", + }; + +static const char *const oakley_group_name_rfc3526[] = { + "OAKLEY_GROUP_MODP2048", + "OAKLEY_GROUP_MODP3072", + "OAKLEY_GROUP_MODP4096", + "OAKLEY_GROUP_MODP6144", + "OAKLEY_GROUP_MODP8192" +}; +enum_names oakley_group_names_rfc3526 = + { OAKLEY_GROUP_MODP2048, OAKLEY_GROUP_MODP8192, + oakley_group_name_rfc3526, NULL }; + +enum_names oakley_group_names = + { OAKLEY_GROUP_MODP768, OAKLEY_GROUP_MODP1536, + oakley_group_name, &oakley_group_names_rfc3526 }; + +/* Oakley Group Type attribute */ + +static const char *const oakley_group_type_name[] = { + "OAKLEY_GROUP_TYPE_MODP", + "OAKLEY_GROUP_TYPE_ECP", + "OAKLEY_GROUP_TYPE_EC2N", + }; + +enum_names oakley_group_type_names = + { OAKLEY_GROUP_TYPE_MODP, OAKLEY_GROUP_TYPE_EC2N, oakley_group_type_name, NULL }; + +/* Notify messages -- error types */ + +static const char *const notification_name[] = { + "INVALID_PAYLOAD_TYPE", + "DOI_NOT_SUPPORTED", + "SITUATION_NOT_SUPPORTED", + "INVALID_COOKIE", + "INVALID_MAJOR_VERSION", + "INVALID_MINOR_VERSION", + "INVALID_EXCHANGE_TYPE", + "INVALID_FLAGS", + "INVALID_MESSAGE_ID", + "INVALID_PROTOCOL_ID", + "INVALID_SPI", + "INVALID_TRANSFORM_ID", + "ATTRIBUTES_NOT_SUPPORTED", + "NO_PROPOSAL_CHOSEN", + "BAD_PROPOSAL_SYNTAX", + "PAYLOAD_MALFORMED", + "INVALID_KEY_INFORMATION", + "INVALID_ID_INFORMATION", + "INVALID_CERT_ENCODING", + "INVALID_CERTIFICATE", + "CERT_TYPE_UNSUPPORTED", + "INVALID_CERT_AUTHORITY", + "INVALID_HASH_INFORMATION", + "AUTHENTICATION_FAILED", + "INVALID_SIGNATURE", + "ADDRESS_NOTIFICATION", + "NOTIFY_SA_LIFETIME", + "CERTIFICATE_UNAVAILABLE", + "UNSUPPORTED_EXCHANGE_TYPE", + "UNEQUAL_PAYLOAD_LENGTHS", + }; + +static const char *const notification_status_name[] = { + "CONNECTED", + }; + +static const char *const ipsec_notification_name[] = { + "IPSEC_RESPONDER_LIFETIME", + "IPSEC_REPLAY_STATUS", + "IPSEC_INITIAL_CONTACT", + }; + +static const char *const notification_dpd_name[] = { + "R_U_THERE", + "R_U_THERE_ACK", +}; + +enum_names notification_dpd_names = + { R_U_THERE, R_U_THERE_ACK, + notification_dpd_name, NULL }; + +enum_names ipsec_notification_names = + { IPSEC_RESPONDER_LIFETIME, IPSEC_INITIAL_CONTACT, + ipsec_notification_name, ¬ification_dpd_names }; + +enum_names notification_status_names = + { CONNECTED, CONNECTED, + notification_status_name, &ipsec_notification_names }; + +enum_names notification_names = + { INVALID_PAYLOAD_TYPE, UNEQUAL_PAYLOAD_LENGTHS, + notification_name, ¬ification_status_names }; + +/* MODECFG + * From draft-dukes-ike-mode-cfg + */ +const char *const attr_msg_type_name[] = { + "ISAKMP_CFG_RESERVED", + "ISAKMP_CFG_REQUEST", + "ISAKMP_CFG_REPLY", + "ISAKMP_CFG_SET", + "ISAKMP_CFG_ACK", + NULL + }; + +enum_names attr_msg_type_names = + { 0 , ISAKMP_CFG_ACK, attr_msg_type_name , NULL }; + +/* socket address family info */ + +static const char *const af_inet_name[] = { + "AF_INET", + }; + +static const char *const af_inet6_name[] = { + "AF_INET6", + }; + +static enum_names af_names6 = { AF_INET6, AF_INET6, af_inet6_name, NULL }; + +enum_names af_names = { AF_INET, AF_INET, af_inet_name, &af_names6 }; + +static ip_address ipv4_any, ipv6_any; +static ip_subnet ipv4_wildcard, ipv6_wildcard; +static ip_subnet ipv4_all, ipv6_all; + +const struct af_info af_inet4_info = { + AF_INET, + "AF_INET", + sizeof(struct in_addr), + sizeof(struct sockaddr_in), + 32, + ID_IPV4_ADDR, ID_IPV4_ADDR_SUBNET, ID_IPV4_ADDR_RANGE, + &ipv4_any, &ipv4_wildcard, &ipv4_all, + }; + +const struct af_info af_inet6_info = { + AF_INET6, + "AF_INET6", + sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + 128, + ID_IPV6_ADDR, ID_IPV6_ADDR_SUBNET, ID_IPV6_ADDR_RANGE, + &ipv6_any, &ipv6_wildcard, &ipv6_all, + }; + +const struct af_info * +aftoinfo(int af) +{ + switch (af) + { + case AF_INET: + return &af_inet4_info; + case AF_INET6: + return &af_inet6_info; + default: + return NULL; + } +} + +bool +subnetisnone(const ip_subnet *sn) +{ + ip_address base; + + networkof(sn, &base); + return isanyaddr(&base) && subnetishost(sn); +} + +/* BIND enumerated types */ + +#include + +static const char *const rr_type_name[] = { + "T_A", /* 1 host address */ + "T_NS", /* 2 authoritative server */ + "T_MD", /* 3 mail destination */ + "T_MF", /* 4 mail forwarder */ + "T_CNAME", /* 5 canonical name */ + "T_SOA", /* 6 start of authority zone */ + "T_MB", /* 7 mailbox domain name */ + "T_MG", /* 8 mail group member */ + "T_MR", /* 9 mail rename name */ + "T_NULL", /* 10 null resource record */ + "T_WKS", /* 11 well known service */ + "T_PTR", /* 12 domain name pointer */ + "T_HINFO", /* 13 host information */ + "T_MINFO", /* 14 mailbox information */ + "T_MX", /* 15 mail routing information */ + "T_TXT", /* 16 text strings */ + "T_RP", /* 17 responsible person */ + "T_AFSDB", /* 18 AFS cell database */ + "T_X25", /* 19 X_25 calling address */ + "T_ISDN", /* 20 ISDN calling address */ + "T_RT", /* 21 router */ + "T_NSAP", /* 22 NSAP address */ + "T_NSAP_PTR", /* 23 reverse NSAP lookup (deprecated) */ + "T_SIG", /* 24 security signature */ + "T_KEY", /* 25 security key */ + "T_PX", /* 26 X.400 mail mapping */ + "T_GPOS", /* 27 geographical position (withdrawn) */ + "T_AAAA", /* 28 IP6 Address */ + "T_LOC", /* 29 Location Information */ + "T_NXT", /* 30 Next Valid Name in Zone */ + "T_EID", /* 31 Endpoint identifier */ + "T_NIMLOC", /* 32 Nimrod locator */ + "T_SRV", /* 33 Server selection */ + "T_ATMA", /* 34 ATM Address */ + "T_NAPTR", /* 35 Naming Authority PoinTeR */ + NULL + }; + +enum_names rr_type_names = { T_A, T_NAPTR, rr_type_name, NULL }; + +/* Query type values which do not appear in resource records */ +static const char *const rr_qtype_name[] = { + "T_IXFR", /* 251 incremental zone transfer */ + "T_AXFR", /* 252 transfer zone of authority */ + "T_MAILB", /* 253 transfer mailbox records */ + "T_MAILA", /* 254 transfer mail agent records */ + "T_ANY", /* 255 wildcard match */ + NULL + }; + +enum_names rr_qtype_names = { T_IXFR, T_ANY, rr_qtype_name, &rr_type_names }; + +static const char *const rr_class_name[] = { + "C_IN", /* 1 the arpa internet */ + NULL + }; + +enum_names rr_class_names = { C_IN, C_IN, rr_class_name, NULL }; + +/* + * NAT-Traversal defines for nat_traveral type from nat_traversal.h + * + */ +const char *const natt_type_bitnames[] = { + "draft-ietf-ipsec-nat-t-ike-00/01", /* 0 */ + "draft-ietf-ipsec-nat-t-ike-02/03", + "RFC 3947", + "3", /* 3 */ + "4", "5", "6", "7", + "8", "9", "10", "11", + "12", "13", "14", "15", + "16", "17", "18", "19", + "20", "21", "22", "23", + "24", "25", "26", "27", + "28", "29", + "nat is behind me", + "nat is behind peer" +}; + +/* look up enum names in an enum_names */ + +const char * +enum_name(enum_names *ed, unsigned long val) +{ + enum_names *p; + + for (p = ed; p != NULL; p = p->en_next_range) + { + if (p->en_first <= val && val <= p->en_last) + return p->en_names[val - p->en_first]; + } + return NULL; +} + +/* find or construct a string to describe an enum value + * Result may be in STATIC buffer! + */ +const char * +enum_show(enum_names *ed, unsigned long val) +{ + const char *p = enum_name(ed, val); + + if (p == NULL) + { + static char buf[12]; /* only one! I hope that it is big enough */ + + snprintf(buf, sizeof(buf), "%lu??", val); + p = buf; + } + return p; +} + + +static char bitnamesbuf[200]; /* only one! I hope that it is big enough! */ + +int +enum_search(enum_names *ed, const char *str) +{ + enum_names *p; + const char *ptr; + unsigned en; + + for (p = ed; p != NULL; p = p->en_next_range) + for (en = p->en_first; en <= p->en_last ;en++) + { + ptr = p->en_names[en - p->en_first]; + if (ptr == 0) continue; + /* if (strncmp(ptr, str, strlen(ptr))==0) */ + if (strcmp(ptr, str) == 0) + return en; + } + return -1; +} + +/* construct a string to name the bits on in a set + * Result may be in STATIC buffer! + * Note: prettypolicy depends on internal details. + */ +const char * +bitnamesof(const char *const table[], lset_t val) +{ + char *p = bitnamesbuf; + lset_t bit; + const char *const *tp; + + if (val == 0) + return "none"; + + for (tp = table, bit = 01; val != 0; bit <<= 1) + { + if (val & bit) + { + const char *n = *tp; + size_t nl; + + if (n == NULL || *n == '\0') + { + /* no name for this bit, so use hex */ + static char flagbuf[sizeof("0x80000000")]; + + snprintf(flagbuf, sizeof(flagbuf), "0x%llx", bit); + n = flagbuf; + } + + nl = strlen(n); + + if (p != bitnamesbuf && p < bitnamesbuf+sizeof(bitnamesbuf) - 1) + *p++ = '+'; + + if (bitnamesbuf+sizeof(bitnamesbuf) - p > (ptrdiff_t)nl) + { + strcpy(p, n); + p += nl; + } + val -= bit; + } + if (*tp != NULL) + tp++; /* move on, but not past end */ + } + *p = '\0'; + return bitnamesbuf; +} + +/* print a policy: like bitnamesof, but it also does the non-bitfields. + * Suppress the shunt and fail fields if 0. + */ +const char * +prettypolicy(lset_t policy) +{ + const char *bn = bitnamesof(sa_policy_bit_names + , policy & ~(POLICY_SHUNT_MASK | POLICY_FAIL_MASK)); + size_t len; + lset_t shunt = (policy & POLICY_SHUNT_MASK) >> POLICY_SHUNT_SHIFT; + lset_t fail = (policy & POLICY_FAIL_MASK) >> POLICY_FAIL_SHIFT; + + if (bn != bitnamesbuf) + bitnamesbuf[0] = '\0'; + len = strlen(bitnamesbuf); + if (shunt != 0) + { + snprintf(bitnamesbuf + len, sizeof(bitnamesbuf) - len, "+%s" + , policy_shunt_names[shunt]); + len += strlen(bitnamesbuf + len); + } + if (fail != 0) + { + snprintf(bitnamesbuf + len, sizeof(bitnamesbuf) - len, "+failure%s" + , policy_fail_names[fail]); + len += strlen(bitnamesbuf + len); + } + if (NEVER_NEGOTIATE(policy)) + { + snprintf(bitnamesbuf + len, sizeof(bitnamesbuf) - len, "+NEVER_NEGOTIATE"); + len += strlen(bitnamesbuf + len); + } + return bitnamesbuf; +} + +/* test a set by seeing if all bits have names */ + +bool +testset(const char *const table[], lset_t val) +{ + lset_t bit; + const char *const *tp; + + for (tp = table, bit = 01; val != 0; bit <<= 1, tp++) + { + const char *n = *tp; + + if (n == NULL || ((val & bit) && *n == '\0')) + return FALSE; + val &= ~bit; + } + return TRUE; +} + + +const char sparse_end[] = "end of sparse names"; + +/* look up enum names in a sparse_names */ +const char *sparse_name(sparse_names sd, unsigned long val) +{ + const struct sparse_name *p; + + for (p = sd; p->name != sparse_end; p++) + if (p->val == val) + return p->name; + return NULL; +} + +/* find or construct a string to describe an sparse value + * Result may be in STATIC buffer! + */ +const char * +sparse_val_show(sparse_names sd, unsigned long val) +{ + const char *p = sparse_name(sd, val); + + if (p == NULL) + { + static char buf[12]; /* only one! I hope that it is big enough */ + + snprintf(buf, sizeof(buf), "%lu??", val); + p = buf; + } + return p; +} + +void init_constants(void) +{ + happy(anyaddr(AF_INET, &ipv4_any)); + happy(anyaddr(AF_INET6, &ipv6_any)); + + happy(addrtosubnet(&ipv4_any, &ipv4_wildcard)); + happy(addrtosubnet(&ipv6_any, &ipv6_wildcard)); + + happy(initsubnet(&ipv4_any, 0, '0', &ipv4_all)); + happy(initsubnet(&ipv6_any, 0, '0', &ipv6_all)); +} diff --git a/src/pluto/constants.h b/src/pluto/constants.h new file mode 100644 index 000000000..3ab10be61 --- /dev/null +++ b/src/pluto/constants.h @@ -0,0 +1,1269 @@ + +/* manifest constants + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: constants.h,v 1.20 2006/02/28 19:13:33 as Exp $ + */ + +#ifndef _CONSTANTS_H +#define _CONSTANTS_H + +extern const char compile_time_interop_options[]; + +extern void init_constants(void); + +/* + * NOTE:For debugging purposes, constants.c has tables to map numbers back to names. + * Any changes here should be reflected there. + */ + +#define elemsof(array) (sizeof(array) / sizeof(*(array))) /* number of elements in an array */ + +/* Many routines return only success or failure, but wish to describe + * the failure in a message. We use the convention that they return + * a NULL on success and a pointer to constant string on failure. + * The fact that the string is a constant is limiting, but it + * avoids storage management issues: the recipient is allowed to assume + * that the string will live "long enough" (usually forever). + * defines err_t for this return type. + */ + +typedef int bool; +#define FALSE 0 +#define TRUE 1 + +#define NULL_FD (-1) /* NULL file descriptor */ +#define dup_any(fd) ((fd) == NULL_FD? NULL_FD : dup(fd)) +#define close_any(fd) { if ((fd) != NULL_FD) { close(fd); (fd) = NULL_FD; } } + +#define BITS_PER_BYTE 8 + +#define streq(a, b) (strcmp((a), (b)) == 0) /* clearer shorthand */ +#define strcaseeq(a, b) (strcasecmp((a), (b)) == 0) /* clearer shorthand */ + +/* set type with room for at least 64 elements for ALG opts (was 32 in stock FS) */ + +typedef unsigned long long lset_t; +#define LEMPTY 0ULL +#define LELEM(opt) (1ULL << (opt)) +#define LRANGE(lwb, upb) LRANGES(LELEM(lwb), LELEM(upb)) +#define LRANGES(first, last) (last - first + last) +#define LHAS(set, elem) ((LELEM(elem) & (set)) != LEMPTY) +#define LIN(subset, set) (((subset) & (set)) == (subset)) +#define LDISJOINT(a, b) (((a) & (b)) == LEMPTY) + +/* Control and lock pathnames */ +#ifndef IPSEC_PIDDIR +# define IPSEC_PIDDIR "/var/run" +#endif +#ifndef DEFAULT_CTLBASE +# define DEFAULT_CTLBASE IPSEC_PIDDIR "/pluto" +#endif + +#define CTL_SUFFIX ".ctl" /* for UNIX domain socket pathname */ +#define LOCK_SUFFIX ".pid" /* for pluto's lock */ +#define INFO_SUFFIX ".info" /* for UNIX domain socket for apps */ + +/* Routines to check and display values. + * + * An enum_names describes an enumeration. + * enum_name() returns the name of an enum value, or NULL if invalid. + * enum_show() is like enum_name, except it formats a numeric representation + * for any invalid value (in a static area!) + * + * bitnames() formats a display of a set of named bits (in a static area) + */ + +struct enum_names { + unsigned long en_first; /* first value in range */ + unsigned long en_last; /* last value in range (inclusive) */ + const char *const *en_names; + const struct enum_names *en_next_range; /* descriptor of next range */ +}; + +typedef const struct enum_names enum_names; + +extern const char *enum_name(enum_names *ed, unsigned long val); +extern const char *enum_show(enum_names *ed, unsigned long val); +extern int enum_search(enum_names *ed, const char *string); + +extern bool testset(const char *const table[], lset_t val); +extern const char *bitnamesof(const char *const table[], lset_t val); + +/* sparse_names is much like enum_names, except values are + * not known to be contiguous or ordered. + * The array of names is ended with one with the name sparse_end + * (this avoids having to reserve a value to signify the end). + * Often appropriate for enums defined by others. + */ +struct sparse_name { + unsigned long val; + const char *const name; +}; +typedef const struct sparse_name sparse_names[]; + +extern const char *sparse_name(sparse_names sd, unsigned long val); +extern const char *sparse_val_show(sparse_names sd, unsigned long val); +extern const char sparse_end[]; + +#define FULL_INET_ADDRESS_SIZE 6 + +/* Group parameters from draft-ietf-ike-01.txt section 6 */ + +#define MODP_GENERATOR "2" + +#define MODP768_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 " \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD " \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 " \ + "E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF" + +#define MODP1024_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 " \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD " \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 " \ + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED " \ + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 " \ + "FFFFFFFF FFFFFFFF" + +#define MODP1536_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 " \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD " \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 " \ + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED " \ + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D " \ + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F " \ + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D " \ + "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF " + +/* draft-ietf-ipsec-ike-modp-groups-03.txt */ +#define MODP2048_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" \ + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" \ + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" \ + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" \ + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" \ + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" \ + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" \ + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" \ + "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF" + +#define MODP3072_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" \ + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" \ + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" \ + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" \ + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" \ + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" \ + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" \ + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" \ + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" \ + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" \ + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" \ + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" \ + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" \ + "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF" + +#define MODP4096_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" \ + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" \ + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" \ + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" \ + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" \ + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" \ + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" \ + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" \ + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" \ + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" \ + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" \ + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" \ + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" \ + "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" \ + "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" \ + "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" \ + "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" \ + "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" \ + "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" \ + "FFFFFFFF FFFFFFFF" + +/* copy&pasted from rfc3526: */ +#define MODP6144_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08" \ + "8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B" \ + "302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9" \ + "A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6" \ + "49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8" \ + "FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" \ + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C" \ + "180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718" \ + "3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D" \ + "04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D" \ + "B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226" \ + "1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" \ + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC" \ + "E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26" \ + "99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB" \ + "04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2" \ + "233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127" \ + "D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" \ + "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406" \ + "AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918" \ + "DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151" \ + "2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03" \ + "F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F" \ + "BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" \ + "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B" \ + "B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632" \ + "387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E" \ + "6DCC4024 FFFFFFFF FFFFFFFF" + +/* copy&pasted from rfc3526: */ +#define MODP8192_MODULUS \ + "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" \ + "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" \ + "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" \ + "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" \ + "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" \ + "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" \ + "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" \ + "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" \ + "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" \ + "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" \ + "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" \ + "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" \ + "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" \ + "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" \ + "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" \ + "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" \ + "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" \ + "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" \ + "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" \ + "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" \ + "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" \ + "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" \ + "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" \ + "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" \ + "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" \ + "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" \ + "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" \ + "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" \ + "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" \ + "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" \ + "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" \ + "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" \ + "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" \ + "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" \ + "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" \ + "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" \ + "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" \ + "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" \ + "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" \ + "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" \ + "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" \ + "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" \ + "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF" +#define LOCALSECRETSIZE (256 / BITS_PER_BYTE) + +/* limits on nonce sizes. See RFC2409 "The internet key exchange (IKE)" 5 */ +#define MINIMUM_NONCE_SIZE 8 /* bytes */ +#define DEFAULT_NONCE_SIZE 16 /* bytes */ +#define MAXIMUM_NONCE_SIZE 256 /* bytes */ + +#define COOKIE_SIZE 8 +#define MAX_ISAKMP_SPI_SIZE 16 + +#define MD2_DIGEST_SIZE (128 / BITS_PER_BYTE) +#define MD5_DIGEST_SIZE (128 / BITS_PER_BYTE) +#define SHA1_DIGEST_SIZE (160 / BITS_PER_BYTE) +#define SHA2_256_DIGEST_SIZE (256 / BITS_PER_BYTE) +#define SHA2_384_DIGEST_SIZE (384 / BITS_PER_BYTE) +#define SHA2_512_DIGEST_SIZE (512 / BITS_PER_BYTE) + +#define MD5_BLOCK_SIZE (512 / BITS_PER_BYTE) +#define SHA1_BLOCK_SIZE (512 / BITS_PER_BYTE) +#define SHA2_256_BLOCK_SIZE (512 / BITS_PER_BYTE) +#define SHA2_384_BLOCK_SIZE (1024 / BITS_PER_BYTE) +#define SHA2_512_BLOCK_SIZE (1024 / BITS_PER_BYTE) + +#define DES_CBC_BLOCK_SIZE (64 / BITS_PER_BYTE) + +#define DSS_QBITS 160 /* bits in DSS's "q" (FIPS 186-1) */ + +/* Maximum is required for SHA2_512 */ +#define MAX_DIGEST_LEN SHA2_512_DIGEST_SIZE +#define MAX_HASH_BLOCK_SIZE SHA2_512_BLOCK_SIZE + +/* RFC 2404 "HMAC-SHA-1-96" section 3 */ +#define HMAC_SHA1_KEY_LEN SHA1_DIGEST_SIZE + +/* RFC 2403 "HMAC-MD5-96" section 3 */ +#define HMAC_MD5_KEY_LEN MD5_DIGEST_SIZE + +#define IKE_UDP_PORT 500 + +/* RFC 2560 OCSP - certificate status */ + +typedef enum { + CERT_GOOD = 0, + CERT_REVOKED = 1, + CERT_UNKNOWN = 2, + CERT_UNDEFINED = 3 +} cert_status_t; + +/* RFC 2459 CRL reason codes */ + +extern enum_names crl_reason_names; + +typedef enum { + REASON_UNSPECIFIED = 0, + REASON_KEY_COMPROMISE = 1, + REASON_CA_COMPROMISE = 2, + REASON_AFFILIATION_CHANGED = 3, + REASON_SUPERSEDED = 4, + REASON_CESSATION_OF_OPERATON = 5, + REASON_CERTIFICATE_HOLD = 6, + REASON_REMOVE_FROM_CRL = 8 +} crl_reason_t; + +/* RFC 3706 Dead Peer Detection */ + +extern enum_names dpd_action_names; + +typedef enum { + DPD_ACTION_NONE = 0, + DPD_ACTION_CLEAR = 1, + DPD_ACTION_HOLD = 2, + DPD_ACTION_RESTART = 3, + DPD_ACTION_UNKNOWN = 4 +} dpd_action_t; + +/* Timer events */ + +extern enum_names timer_event_names; + +enum event_type { + EVENT_NULL, /* non-event */ + EVENT_REINIT_SECRET, /* Refresh cookie secret */ +#ifdef KLIPS + EVENT_SHUNT_SCAN, /* scan shunt eroutes known to kernel */ +#endif + EVENT_SO_DISCARD, /* discard unfinished state object */ + EVENT_RETRANSMIT, /* Retransmit packet */ + EVENT_SA_REPLACE, /* SA replacement event */ + EVENT_SA_REPLACE_IF_USED, /* SA replacement event */ + EVENT_SA_EXPIRE, /* SA expiration event */ + EVENT_NAT_T_KEEPALIVE, /* NAT Traversal Keepalive */ + EVENT_DPD, /* dead peer detection */ + EVENT_DPD_TIMEOUT, /* dead peer detection timeout */ + EVENT_LOG_DAILY /* reset certain log events/stats */ +}; + +#define EVENT_REINIT_SECRET_DELAY 3600 /* 1 hour */ +#define EVENT_RETRANSMIT_DELAY_0 10 /* 10 seconds */ + +/* Misc. stuff */ + +#define MAXIMUM_RETRANSMISSIONS 2 +#define MAXIMUM_RETRANSMISSIONS_INITIAL 20 + +#define MAX_INPUT_UDP_SIZE 65536 +#define MAX_OUTPUT_UDP_SIZE 65536 + +/* Version numbers */ + +#define ISAKMP_MAJOR_VERSION 0x1 +#define ISAKMP_MINOR_VERSION 0x0 + +extern enum_names version_names; + +/* Domain of Interpretation */ + +extern enum_names doi_names; + +#define ISAKMP_DOI_ISAKMP 0 +#define ISAKMP_DOI_IPSEC 1 + +/* IPsec DOI things */ + +#define IPSEC_DOI_SITUATION_LENGTH 4 +#define IPSEC_DOI_LDI_LENGTH 4 +#define IPSEC_DOI_SPI_SIZE 4 + +/* SPI value 0 is invalid and values 1-255 are reserved to IANA. + * ESP: RFC 2402 2.4; AH: RFC 2406 2.1 + * IPComp RFC 2393 substitutes a CPI in the place of an SPI. + * see also draft-shacham-ippcp-rfc2393bis-05.txt. + * We (FreeS/WAN) reserve 0x100 to 0xFFF for manual keying, so + * Pluto won't generate these values. + */ +#define IPSEC_DOI_SPI_MIN 0x100 +#define IPSEC_DOI_SPI_OUR_MIN 0x1000 + +/* debugging settings: a set of selections for reporting + * These would be more naturally situated in log.h, + * but they are shared with whack. + * IMPAIR_* actually change behaviour, usually badly, + * to aid in testing. Naturally, these are not included in ALL. + * + * NOTE: changes here must be done in concert with changes to DBGOPT_* + * in whack.c. A change to WHACK_MAGIC in whack.h will be required too. + */ +#ifdef DEBUG +extern const char *const debug_bit_names[]; +#endif + +#define DBG_RAW LELEM(0) /* raw packet I/O */ +#define DBG_CRYPT LELEM(1) /* encryption/decryption of messages */ +#define DBG_PARSING LELEM(2) /* show decoding of messages */ +#define DBG_EMITTING LELEM(3) /* show encoding of messages */ +#define DBG_CONTROL LELEM(4) /* control flow within Pluto */ +#define DBG_LIFECYCLE LELEM(5) /* SA lifecycle */ +#define DBG_KLIPS LELEM(6) /* messages to KLIPS */ +#define DBG_DNS LELEM(7) /* DNS activity */ +#define DBG_NATT LELEM(8) /* NAT-T */ +#define DBG_OPPO LELEM(9) /* opportunism */ +#define DBG_CONTROLMORE LELEM(10) /* more detailed debugging */ + +#define DBG_PRIVATE LELEM(11) /* private information: DANGER! */ + +#define IMPAIR0 12 /* first bit for IMPAIR_* */ + +#define IMPAIR_DELAY_ADNS_KEY_ANSWER LELEM(IMPAIR0+0) /* sleep before answering */ +#define IMPAIR_DELAY_ADNS_TXT_ANSWER LELEM(IMPAIR0+1) /* sleep before answering */ +#define IMPAIR_BUST_MI2 LELEM(IMPAIR0+2) /* make MI2 really large */ +#define IMPAIR_BUST_MR2 LELEM(IMPAIR0+3) /* make MI2 really large */ + +#define DBG_NONE 0 /* no options on, including impairments */ +#define DBG_ALL LRANGES(DBG_RAW, DBG_CONTROLMORE) /* all logging options on EXCEPT DBG_PRIVATE */ + +/* State of exchanges + * + * The name of the state describes the last message sent, not the + * message currently being input or output (except during retry). + * In effect, the state represents the last completed action. + * + * Messages are named [MQ][IR]n where + * - M stands for Main Mode (Phase 1); + * Q stands for Quick Mode (Phase 2) + * - I stands for Initiator; + * R stands for Responder + * - n, a digit, stands for the number of the message + * + * It would be more convenient if each state accepted a message + * and produced one. This is the case for states at the start + * or end of an exchange. To fix this, we pretend that there are + * MR0 and QR0 messages before the MI1 and QR1 messages. Similarly, + * we pretend that there are MR4 and QR2 messages. + * + * STATE_MAIN_R0 and STATE_QUICK_R0 are intermediate states (not + * retained between messages) representing the state that accepts the + * first message of an exchange has been read but not processed. + * + * state_microcode state_microcode_table in demux.c describes + * other important details. + */ + +extern enum_names state_names; +extern const char *const state_story[]; + +enum state_kind { + STATE_UNDEFINED, /* 0 -- most likely accident */ + + /* Opportunism states: see "Opportunistic Encryption" 2.2 */ + + OPPO_ACQUIRE, /* got an ACQUIRE message for this pair */ + OPPO_GW_DISCOVERED, /* got TXT specifying gateway */ + + /* IKE states */ + + STATE_MAIN_R0, + STATE_MAIN_I1, + STATE_MAIN_R1, + STATE_MAIN_I2, + STATE_MAIN_R2, + STATE_MAIN_I3, + STATE_MAIN_R3, + STATE_MAIN_I4, + + STATE_QUICK_R0, + STATE_QUICK_I1, + STATE_QUICK_R1, + STATE_QUICK_I2, + STATE_QUICK_R2, + + STATE_INFO, + STATE_INFO_PROTECTED, + + /* XAUTH states */ + + STATE_XAUTH_I0, /* initiator state (client) */ + STATE_XAUTH_R1, /* responder state (server) */ + STATE_XAUTH_I1, + STATE_XAUTH_R2, + STATE_XAUTH_I2, + STATE_XAUTH_R3, + + /* Mode Config pull states */ + + STATE_MODE_CFG_R0, /* responder state (server) */ + STATE_MODE_CFG_I1, /* initiator state (client) */ + STATE_MODE_CFG_R1, + STATE_MODE_CFG_I2, + + /* Mode Config push states */ + + STATE_MODE_CFG_I0, /* initiator state (client) */ + STATE_MODE_CFG_R3, /* responder state (server) */ + STATE_MODE_CFG_I3, + STATE_MODE_CFG_R4, + + STATE_IKE_ROOF +}; + +#define STATE_IKE_FLOOR STATE_MAIN_R0 + +#define PHASE1_INITIATOR_STATES (LELEM(STATE_MAIN_I1) | LELEM(STATE_MAIN_I2) \ + | LELEM(STATE_MAIN_I3) | LELEM(STATE_MAIN_I4)) +#define ISAKMP_SA_ESTABLISHED_STATES ( \ + LELEM(STATE_MAIN_R3) | LELEM(STATE_MAIN_I4) \ + | LELEM(STATE_XAUTH_R1) | LELEM(STATE_XAUTH_R2) | LELEM(STATE_XAUTH_R3) \ + | LELEM(STATE_XAUTH_I1) | LELEM(STATE_XAUTH_I2) \ + | LELEM(STATE_MODE_CFG_I1) | LELEM(STATE_MODE_CFG_R1) | LELEM(STATE_MODE_CFG_I2) \ + | LELEM(STATE_MODE_CFG_R3) | LELEM(STATE_MODE_CFG_I3) | LELEM(STATE_MODE_CFG_R4)) + +#define IS_PHASE1(s) ((STATE_MAIN_R0 <= (s) && (s) <= STATE_MAIN_I4) \ + || (STATE_XAUTH_I0 <= (s) && (s) <= STATE_XAUTH_R3) \ + || (STATE_MODE_CFG_R0 <= (s) && (s) <= STATE_MODE_CFG_R4)) + +#define IS_QUICK(s) (STATE_QUICK_R0 <= (s) && (s) <= STATE_QUICK_R2) +#define IS_ISAKMP_ENCRYPTED(s) (STATE_MAIN_I2 <= (s)) + +#define IS_ISAKMP_SA_ESTABLISHED(s) ( \ + (s) == STATE_MAIN_R3 \ + || (s) == STATE_MAIN_I4 \ + || (s) == STATE_XAUTH_I2 \ + || (s) == STATE_XAUTH_R3 \ + || (s) == STATE_MODE_CFG_R1 \ + || (s) == STATE_MODE_CFG_I2 \ + || (s) == STATE_MODE_CFG_I3 \ + || (s) == STATE_MODE_CFG_R4) + +#define IS_IPSEC_SA_ESTABLISHED(s) ((s) == STATE_QUICK_I2 || (s) == STATE_QUICK_R2) +#define IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(s) ((s) == STATE_QUICK_R1) + +/* kind of struct connection + * Ordered (mostly) by concreteness. Order is exploited. + */ + +extern enum_names connection_kind_names; + +enum connection_kind { + CK_GROUP, /* policy group: instantiates to template */ + CK_TEMPLATE, /* abstract connection, with wildcard */ + CK_PERMANENT, /* normal connection */ + CK_INSTANCE, /* instance of template, created for a particular attempt */ + CK_GOING_AWAY /* instance being deleted -- don't delete again */ +}; + + +/* routing status. + * Note: routing ignores source address, but erouting does not! + * Note: a connection can only be routed if it is NEVER_NEGOTIATE + * or HAS_IPSEC_POLICY. + */ + +extern enum_names routing_story; + +/* note that this is assumed to be ordered! */ +enum routing_t { + RT_UNROUTED, /* unrouted */ + RT_UNROUTED_HOLD, /* unrouted, but HOLD shunt installed */ + RT_ROUTED_ECLIPSED, /* RT_ROUTED_PROSPECTIVE except bare HOLD or instance has eroute */ + RT_ROUTED_PROSPECTIVE, /* routed, and prospective shunt installed */ + RT_ROUTED_HOLD, /* routed, and HOLD shunt installed */ + RT_ROUTED_FAILURE, /* routed, and failure-context shunt installed */ + RT_ROUTED_TUNNEL, /* routed, and erouted to an IPSEC SA group */ + RT_UNROUTED_KEYED /* keyed, but not routed, on purpose */ +}; + +#define routed(rs) ((rs) > RT_UNROUTED_HOLD) +#define erouted(rs) ((rs) != RT_UNROUTED) +#define shunt_erouted(rs) (erouted(rs) && (rs) != RT_ROUTED_TUNNEL) + +/* Payload types + * RFC2408 Internet Security Association and Key Management Protocol (ISAKMP) + * section 3.1 + * + * RESERVED 14-127 + * Private USE 128-255 + */ + +extern enum_names payload_names; +extern const char *const payload_name[]; + +#define ISAKMP_NEXT_NONE 0 /* No other payload following */ +#define ISAKMP_NEXT_SA 1 /* Security Association */ +#define ISAKMP_NEXT_P 2 /* Proposal */ +#define ISAKMP_NEXT_T 3 /* Transform */ +#define ISAKMP_NEXT_KE 4 /* Key Exchange */ +#define ISAKMP_NEXT_ID 5 /* Identification */ +#define ISAKMP_NEXT_CERT 6 /* Certificate */ +#define ISAKMP_NEXT_CR 7 /* Certificate Request */ +#define ISAKMP_NEXT_HASH 8 /* Hash */ +#define ISAKMP_NEXT_SIG 9 /* Signature */ +#define ISAKMP_NEXT_NONCE 10 /* Nonce */ +#define ISAKMP_NEXT_N 11 /* Notification */ +#define ISAKMP_NEXT_D 12 /* Delete */ +#define ISAKMP_NEXT_VID 13 /* Vendor ID */ +#define ISAKMP_NEXT_ATTR 14 /* Mode config Attribute */ + +#define ISAKMP_NEXT_NATD_RFC 20 /* NAT-Traversal: NAT-D (rfc) */ +#define ISAKMP_NEXT_NATOA_RFC 21 /* NAT-Traversal: NAT-OA (rfc) */ +#define ISAKMP_NEXT_ROOF 22 /* roof on payload types */ + +#define ISAKMP_NEXT_NATD_DRAFTS 130 /* NAT-Traversal: NAT-D (drafts) */ +#define ISAKMP_NEXT_NATOA_DRAFTS 131 /* NAT-Traversal: NAT-OA (drafts) */ + +/* These values are to be used within the Type field of an Attribute (14) + * ISAKMP payload. + */ +#define ISAKMP_CFG_REQUEST 1 +#define ISAKMP_CFG_REPLY 2 +#define ISAKMP_CFG_SET 3 +#define ISAKMP_CFG_ACK 4 + +extern enum_names attr_msg_type_names; + +/* Mode Config attribute values */ +#define INTERNAL_IP4_ADDRESS 1 +#define INTERNAL_IP4_NETMASK 2 +#define INTERNAL_IP4_DNS 3 +#define INTERNAL_IP4_NBNS 4 +#define INTERNAL_ADDRESS_EXPIRY 5 +#define INTERNAL_IP4_DHCP 6 +#define APPLICATION_VERSION 7 +#define INTERNAL_IP6_ADDRESS 8 +#define INTERNAL_IP6_NETMASK 9 +#define INTERNAL_IP6_DNS 10 +#define INTERNAL_IP6_NBNS 11 +#define INTERNAL_IP6_DHCP 12 +#define INTERNAL_IP4_SUBNET 13 +#define SUPPORTED_ATTRIBUTES 14 +#define INTERNAL_IP6_SUBNET 15 + + +extern enum_names modecfg_attr_names; + +/* XAUTH attribute values */ +#define XAUTH_TYPE 16520 +#define XAUTH_USER_NAME 16521 +#define XAUTH_USER_PASSWORD 16522 +#define XAUTH_PASSCODE 16523 +#define XAUTH_MESSAGE 16524 +#define XAUTH_CHALLENGE 16525 +#define XAUTH_DOMAIN 16526 +#define XAUTH_STATUS 16527 +#define XAUTH_NEXT_PIN 16528 +#define XAUTH_ANSWER 16529 + +#define XAUTH_BASE XAUTH_TYPE + +extern enum_names xauth_attr_names; + +/* ISAKMP mode config attributes specific to the Unity vendor Id */ +#define UNITY_BANNER 28672 +#define UNITY_SAVE_PASSWD 28673 +#define UNITY_DEF_DOMAIN 28674 +#define UNITY_SPLITDNS_NAME 28675 +#define UNITY_SPLIT_INCLUDE 28676 +#define UNITY_NATT_PORT 28677 +#define UNITY_LOCAL_LAN 28678 +#define UNITY_PFS 28679 +#define UNITY_FW_TYPE 28680 +#define UNITY_BACKUP_SERVERS 28681 +#define UNITY_DDNS_HOSTNAME 28682 + +#define UNITY_BASE UNITY_BANNER + +extern enum_names unity_attr_names; + +/* XAUTH authentication types */ +#define XAUTH_TYPE_GENERIC 0 +#define XAUTH_TYPE_CHAP 1 +#define XAUTH_TYPE_OTP 2 +#define XAUTH_TYPE_SKEY 3 + +/* Values for XAUTH_STATUS */ +#define XAUTH_STATUS_FAIL 0 +#define XAUTH_STATUS_OK 1 + +extern enum_names xauth_type_names; + +/* Exchange types + * RFC2408 "Internet Security Association and Key Management Protocol (ISAKMP)" + * section 3.1 + * + * ISAKMP Future Use 6 - 31 + * DOI Specific Use 32 - 239 + * Private Use 240 - 255 + * + * Note: draft-ietf-ipsec-dhless-enc-mode-00.txt Appendix A + * defines "DHless RSA Encryption" as 6. + */ + +extern enum_names exchange_names; + +#define ISAKMP_XCHG_NONE 0 +#define ISAKMP_XCHG_BASE 1 +#define ISAKMP_XCHG_IDPROT 2 /* ID Protection */ +#define ISAKMP_XCHG_AO 3 /* Authentication Only */ +#define ISAKMP_XCHG_AGGR 4 /* Aggressive */ +#define ISAKMP_XCHG_INFO 5 /* Informational */ +#define ISAKMP_XCHG_MODE_CFG 6 /* Mode Config */ + +/* Extra exchange types, defined by Oakley + * RFC2409 "The Internet Key Exchange (IKE)", near end of Appendix A + */ +#define ISAKMP_XCHG_QUICK 32 /* Oakley Quick Mode */ +#define ISAKMP_XCHG_NGRP 33 /* Oakley New Group Mode */ +/* added in draft-ietf-ipsec-ike-01.txt, near end of Appendix A */ +#define ISAKMP_XCHG_ACK_INFO 34 /* Oakley Acknowledged Informational */ + +/* Flag bits */ + +extern const char *const flag_bit_names[]; + +#define ISAKMP_FLAG_ENCRYPTION 0x1 +#define ISAKMP_FLAG_COMMIT 0x2 + +/* Situation definition for IPsec DOI */ + +extern const char *const sit_bit_names[]; + +#define SIT_IDENTITY_ONLY 0x01 +#define SIT_SECRECY 0x02 +#define SIT_INTEGRITY 0x04 + +/* Protocol IDs + * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.1 + */ + +extern enum_names protocol_names; + +#define PROTO_ISAKMP 1 +#define PROTO_IPSEC_AH 2 +#define PROTO_IPSEC_ESP 3 +#define PROTO_IPCOMP 4 + +/* warning: trans_show uses enum_show, so same static buffer is used */ +#define trans_show(p, t) \ + ((p)==PROTO_IPSEC_AH ? enum_show(&ah_transformid_names, (t)) \ + : (p)==PROTO_IPSEC_ESP ? enum_show(&esp_transformid_names, (t)) \ + : (p)==PROTO_IPCOMP ? enum_show(&ipcomp_transformid_names, (t)) \ + : "??") + +/* many transform values are moved to freeswan/ipsec_policy.h */ + +extern enum_names isakmp_transformid_names; + +#define KEY_IKE 1 + +extern enum_names ah_transformid_names; +extern enum_names esp_transformid_names; +extern enum_names ipcomp_transformid_names; + +/* the following are from RFC 2393/draft-shacham-ippcp-rfc2393bis-05.txt 3.3 */ +typedef u_int16_t cpi_t; +#define IPCOMP_CPI_SIZE 2 +#define IPCOMP_FIRST_NEGOTIATED 256 +#define IPCOMP_LAST_NEGOTIATED 61439 + +/* Identification type values + * RFC 2407 The Internet IP security Domain of Interpretation for ISAKMP 4.6.2.1 + */ + +extern enum_names ident_names; +extern enum_names cert_type_names; +extern enum_names cert_policy_names; + +typedef enum certpolicy { + CERT_ALWAYS_SEND = 0, /* the default */ + CERT_SEND_IF_ASKED = 1, + CERT_NEVER_SEND = 2, + + CERT_YES_SEND = 3, /* synonym for CERT_ALWAYS_SEND */ + CERT_NO_SEND = 4 /* synonym for CERT_NEVER_SEND */ +} certpolicy_t; + +/* Policies for establishing an SA + * + * These are used to specify attributes (eg. encryption) and techniques + * (eg PFS) for an SA. + * Note: certain CD_ definitions in whack.c parallel these -- keep them + * in sync! + */ + +extern const char *const sa_policy_bit_names[]; +extern const char *prettypolicy(lset_t policy); + +/* ISAKMP auth techniques (none means never negotiate) */ +#define POLICY_PSK LELEM(0) +#define POLICY_RSASIG LELEM(1) + +#define POLICY_ISAKMP_SHIFT 0 /* log2(POLICY_PSK) */ +#define POLICY_ID_AUTH_MASK (POLICY_PSK | POLICY_RSASIG | POLICY_XAUTH_PSK | POLICY_XAUTH_RSASIG) +#define POLICY_ISAKMP_MASK POLICY_ID_AUTH_MASK /* all so far */ + +/* Quick Mode (IPSEC) attributes */ +#define POLICY_ENCRYPT LELEM(2) /* must be first of IPSEC policies */ +#define POLICY_AUTHENTICATE LELEM(3) /* must be second */ +#define POLICY_COMPRESS LELEM(4) /* must be third */ +#define POLICY_TUNNEL LELEM(5) +#define POLICY_PFS LELEM(6) +#define POLICY_DISABLEARRIVALCHECK LELEM(7) /* supress tunnel egress address checking */ + +#define POLICY_IPSEC_SHIFT 2 /* log2(POLICY_ENCRYPT) */ +#define POLICY_IPSEC_MASK LRANGES(POLICY_ENCRYPT, POLICY_DISABLEARRIVALCHECK) + +/* shunt attributes: what to do when routed without tunnel (2 bits) */ +#define POLICY_SHUNT_SHIFT 8 /* log2(POLICY_SHUNT_PASS) */ +#define POLICY_SHUNT_MASK (03ul << POLICY_SHUNT_SHIFT) + +#define POLICY_SHUNT_TRAP (0ul << POLICY_SHUNT_SHIFT) /* default: negotiate */ +#define POLICY_SHUNT_PASS (1ul << POLICY_SHUNT_SHIFT) +#define POLICY_SHUNT_DROP (2ul << POLICY_SHUNT_SHIFT) +#define POLICY_SHUNT_REJECT (3ul << POLICY_SHUNT_SHIFT) + +/* fail attributes: what to do with failed negotiation (2 bits) */ + +#define POLICY_FAIL_SHIFT 10 /* log2(POLICY_FAIL_PASS) */ +#define POLICY_FAIL_MASK (03ul << POLICY_FAIL_SHIFT) + +#define POLICY_FAIL_NONE (0ul << POLICY_FAIL_SHIFT) /* default */ +#define POLICY_FAIL_PASS (1ul << POLICY_FAIL_SHIFT) +#define POLICY_FAIL_DROP (2ul << POLICY_FAIL_SHIFT) +#define POLICY_FAIL_REJECT (3ul << POLICY_FAIL_SHIFT) + +/* connection policy + * Other policies could vary per state object. These live in connection. + */ +#define POLICY_DONT_REKEY LELEM(12) /* don't rekey state either Phase */ +#define POLICY_OPPO LELEM(13) /* is this opportunistic? */ +#define POLICY_GROUP LELEM(14) /* is this a group template? */ +#define POLICY_GROUTED LELEM(15) /* do we want this group routed? */ +#define POLICY_UP LELEM(16) /* do we want this up? */ +#define POLICY_MODECFG_PUSH LELEM(17) /* is modecfg pushed by server? */ +#define POLICY_XAUTH_PSK LELEM(18) /* do we support XAUTH????PreShared? */ +#define POLICY_XAUTH_RSASIG LELEM(19) /* do we support XAUTH????RSA? */ +#define POLICY_XAUTH_SERVER LELEM(20) /* are we an XAUTH server? */ +#define POLICY_DONT_REAUTH LELEM(21) /* don't reauthenticate on rekeying, IKEv2 only */ +#define POLICY_BEET LELEM(22) /* bound end2end tunnel, IKEv2 */ + +/* Any IPsec policy? If not, a connection description + * is only for ISAKMP SA, not IPSEC SA. (A pun, I admit.) + * Note: a connection can only be routed if it is NEVER_NEGOTIATE + * or HAS_IPSEC_POLICY. + */ +#define HAS_IPSEC_POLICY(p) (((p) & POLICY_IPSEC_MASK) != 0) + +/* Don't allow negotiation? */ +#define NEVER_NEGOTIATE(p) (LDISJOINT((p), POLICY_ID_AUTH_MASK)) + + +/* Oakley transform attributes + * draft-ietf-ipsec-ike-01.txt appendix A + */ + +extern enum_names oakley_attr_names; +extern const char *const oakley_attr_bit_names[]; + +#define OAKLEY_ENCRYPTION_ALGORITHM 1 +#define OAKLEY_HASH_ALGORITHM 2 +#define OAKLEY_AUTHENTICATION_METHOD 3 +#define OAKLEY_GROUP_DESCRIPTION 4 +#define OAKLEY_GROUP_TYPE 5 +#define OAKLEY_GROUP_PRIME 6 /* B/V */ +#define OAKLEY_GROUP_GENERATOR_ONE 7 /* B/V */ +#define OAKLEY_GROUP_GENERATOR_TWO 8 /* B/V */ +#define OAKLEY_GROUP_CURVE_A 9 /* B/V */ +#define OAKLEY_GROUP_CURVE_B 10 /* B/V */ +#define OAKLEY_LIFE_TYPE 11 +#define OAKLEY_LIFE_DURATION 12 /* B/V */ +#define OAKLEY_PRF 13 +#define OAKLEY_KEY_LENGTH 14 +#define OAKLEY_FIELD_SIZE 15 +#define OAKLEY_GROUP_ORDER 16 /* B/V */ +#define OAKLEY_BLOCK_SIZE 17 + +/* for each Oakley attribute, which enum_names describes its values? */ +extern enum_names *oakley_attr_val_descs[]; + +/* IPsec DOI attributes + * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.5 + */ + +extern enum_names ipsec_attr_names; + +#define SA_LIFE_TYPE 1 +#define SA_LIFE_DURATION 2 /* B/V */ +#define GROUP_DESCRIPTION 3 +#define ENCAPSULATION_MODE 4 +#define AUTH_ALGORITHM 5 +#define KEY_LENGTH 6 +#define KEY_ROUNDS 7 +#define COMPRESS_DICT_SIZE 8 +#define COMPRESS_PRIVATE_ALG 9 /* B/V */ + +/* for each IPsec attribute, which enum_names describes its values? */ +extern enum_names *ipsec_attr_val_descs[]; + +/* SA Lifetime Type attribute + * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.5 + * Default time specified in 4.5 + * + * There are two defaults for IPSEC SA lifetime, SA_LIFE_DURATION_DEFAULT, + * and PLUTO_SA_LIFE_DURATION_DEFAULT. + * SA_LIFE_DURATION_DEFAULT is specified in RFC2407 "The Internet IP + * Security Domain of Interpretation for ISAKMP" 4.5. It applies when + * an ISAKMP negotiation does not explicitly specify a life duration. + * PLUTO_SA_LIFE_DURATION_DEFAULT is specified in pluto(8). It applies + * when a connection description does not specify --ipseclifetime. + * The value of SA_LIFE_DURATION_MAXIMUM is our local policy. + */ + +extern enum_names sa_lifetime_names; + +#define SA_LIFE_TYPE_SECONDS 1 +#define SA_LIFE_TYPE_KBYTES 2 + +#define SA_LIFE_DURATION_DEFAULT 28800 /* eight hours (RFC2407 4.5) */ +#define PLUTO_SA_LIFE_DURATION_DEFAULT 3600 /* one hour (pluto(8)) */ +#define SA_LIFE_DURATION_MAXIMUM 86400 /* one day */ + +#define SA_REPLACEMENT_MARGIN_DEFAULT 540 /* (IPSEC & IKE) nine minutes */ +#define SA_REPLACEMENT_FUZZ_DEFAULT 100 /* (IPSEC & IKE) 100% of MARGIN */ +#define SA_REPLACEMENT_RETRIES_DEFAULT 3 /* (IPSEC & IKE) */ + +#define SA_LIFE_DURATION_K_DEFAULT 0xFFFFFFFFlu + +/* Encapsulation Mode attribute */ + +extern enum_names enc_mode_names; + +#define ENCAPSULATION_MODE_UNSPECIFIED 0 /* not legal -- used internally */ +#define ENCAPSULATION_MODE_TUNNEL 1 +#define ENCAPSULATION_MODE_TRANSPORT 2 + +#define ENCAPSULATION_MODE_UDP_TUNNEL_RFC 3 +#define ENCAPSULATION_MODE_UDP_TRANSPORT_RFC 4 + +#define ENCAPSULATION_MODE_UDP_TUNNEL_DRAFTS 61443 +#define ENCAPSULATION_MODE_UDP_TRANSPORT_DRAFTS 61444 + +/* Auth Algorithm attribute */ + +extern enum_names auth_alg_names, extended_auth_alg_names; + +#define AUTH_ALGORITHM_NONE 0 /* our private designation */ +#define AUTH_ALGORITHM_HMAC_MD5 1 +#define AUTH_ALGORITHM_HMAC_SHA1 2 +#define AUTH_ALGORITHM_DES_MAC 3 +#define AUTH_ALGORITHM_KPDK 4 +#define AUTH_ALGORITHM_HMAC_SHA2_256 5 +#define AUTH_ALGORITHM_HMAC_SHA2_384 6 +#define AUTH_ALGORITHM_HMAC_SHA2_512 7 +#define AUTH_ALGORITHM_HMAC_RIPEMD 8 +#define AUTH_ALGORITHM_NULL 251 + +/* Oakley Lifetime Type attribute + * draft-ietf-ipsec-ike-01.txt appendix A + * As far as I can see, there is not specification for + * OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT. This could lead to interop problems! + * For no particular reason, we chose three hours. + * The value of OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM is our local policy. + */ +extern enum_names oakley_lifetime_names; + +#define OAKLEY_LIFE_SECONDS 1 +#define OAKLEY_LIFE_KILOBYTES 2 + +#define OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT 10800 /* three hours */ +#define OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM 86400 /* one day */ + +/* Oakley PRF attribute (none defined) + * draft-ietf-ipsec-ike-01.txt appendix A + */ +extern enum_names oakley_prf_names; + +/* HMAC (see rfc2104.txt) */ + +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5C + +/* Oakley Encryption Algorithm attribute + * draft-ietf-ipsec-ike-01.txt appendix A + * and from http://www.isi.edu/in-notes/iana/assignments/ipsec-registry + */ + +extern enum_names oakley_enc_names; + +#define OAKLEY_DES_CBC 1 +#define OAKLEY_IDEA_CBC 2 +#define OAKLEY_BLOWFISH_CBC 3 +#define OAKLEY_RC5_R16_B64_CBC 4 +#define OAKLEY_3DES_CBC 5 +#define OAKLEY_CAST_CBC 6 +#define OAKLEY_AES_CBC 7 + +#define OAKLEY_MARS_CBC 65001 +#define OAKLEY_RC6_CBC 65002 +#define OAKLEY_ID_65003 65003 +#define OAKLEY_SERPENT_CBC 65004 +#define OAKLEY_TWOFISH_CBC 65005 + +#define OAKLEY_TWOFISH_CBC_SSH 65289 + +#define OAKLEY_ENCRYPT_MAX 65535 /* pretty useless :) */ + +/* Oakley Hash Algorithm attribute + * draft-ietf-ipsec-ike-01.txt appendix A + * and from http://www.isi.edu/in-notes/iana/assignments/ipsec-registry + */ + +extern enum_names oakley_hash_names; + +#define OAKLEY_MD5 1 +#define OAKLEY_SHA 2 +#define OAKLEY_TIGER 3 +#define OAKLEY_SHA2_256 4 +#define OAKLEY_SHA2_384 5 +#define OAKLEY_SHA2_512 6 + +#define OAKLEY_HASH_MAX 7 + +/* Oakley Authentication Method attribute + * draft-ietf-ipsec-ike-01.txt appendix A + * Goofy Hybrid extensions from draft-ietf-ipsec-isakmp-hybrid-auth-05.txt + * Goofy XAUTH extensions from draft-ietf-ipsec-isakmp-xauth-06.txt + */ + +extern enum_names oakley_auth_names; + +#define OAKLEY_PRESHARED_KEY 1 +#define OAKLEY_DSS_SIG 2 +#define OAKLEY_RSA_SIG 3 +#define OAKLEY_RSA_ENC 4 +#define OAKLEY_RSA_ENC_REV 5 +#define OAKLEY_ELGAMAL_ENC 6 +#define OAKLEY_ELGAMAL_ENC_REV 7 + +#define OAKLEY_AUTH_ROOF 8 /* roof on auth values THAT WE SUPPORT */ + +#define HybridInitRSA 64221 +#define HybridRespRSA 64222 +#define HybridInitDSS 64223 +#define HybridRespDSS 64224 + +#define XAUTHInitPreShared 65001 +#define XAUTHRespPreShared 65002 +#define XAUTHInitDSS 65003 +#define XAUTHRespDSS 65004 +#define XAUTHInitRSA 65005 +#define XAUTHRespRSA 65006 +#define XAUTHInitRSAEncryption 65007 +#define XAUTHRespRSAEncryption 65008 +#define XAUTHInitRSARevisedEncryption 65009 +#define XAUTHRespRSARevisedEncryption 65010 + +/* Oakley Group Description attribute + * draft-ietf-ipsec-ike-01.txt appendix A + */ +extern enum_names oakley_group_names; + +#define OAKLEY_GROUP_MODP768 1 +#define OAKLEY_GROUP_MODP1024 2 +#define OAKLEY_GROUP_GP155 3 +#define OAKLEY_GROUP_GP185 4 +#define OAKLEY_GROUP_MODP1536 5 + +#define OAKLEY_GROUP_MODP2048 14 +#define OAKLEY_GROUP_MODP3072 15 +#define OAKLEY_GROUP_MODP4096 16 +#define OAKLEY_GROUP_MODP6144 17 +#define OAKLEY_GROUP_MODP8192 18 +/* you must also touch: constants.c, crypto.c */ + +/* Oakley Group Type attribute + * draft-ietf-ipsec-ike-01.txt appendix A + */ +extern enum_names oakley_group_type_names; + +#define OAKLEY_GROUP_TYPE_MODP 1 +#define OAKLEY_GROUP_TYPE_ECP 2 +#define OAKLEY_GROUP_TYPE_EC2N 3 + + +/* Notify messages -- error types + * See RFC2408 ISAKMP 3.14.1 + */ + +extern enum_names notification_names; +extern enum_names ipsec_notification_names; + +typedef enum { + NOTHING_WRONG = 0, /* unofficial! */ + + INVALID_PAYLOAD_TYPE = 1, + DOI_NOT_SUPPORTED = 2, + SITUATION_NOT_SUPPORTED = 3, + INVALID_COOKIE = 4, + INVALID_MAJOR_VERSION = 5, + INVALID_MINOR_VERSION = 6, + INVALID_EXCHANGE_TYPE = 7, + INVALID_FLAGS = 8, + INVALID_MESSAGE_ID = 9, + INVALID_PROTOCOL_ID = 10, + INVALID_SPI = 11, + INVALID_TRANSFORM_ID = 12, + ATTRIBUTES_NOT_SUPPORTED = 13, + NO_PROPOSAL_CHOSEN = 14, + BAD_PROPOSAL_SYNTAX = 15, + PAYLOAD_MALFORMED = 16, + INVALID_KEY_INFORMATION = 17, + INVALID_ID_INFORMATION = 18, + INVALID_CERT_ENCODING = 19, + INVALID_CERTIFICATE = 20, + CERT_TYPE_UNSUPPORTED = 21, + INVALID_CERT_AUTHORITY = 22, + INVALID_HASH_INFORMATION = 23, + AUTHENTICATION_FAILED = 24, + INVALID_SIGNATURE = 25, + ADDRESS_NOTIFICATION = 26, + NOTIFY_SA_LIFETIME = 27, + CERTIFICATE_UNAVAILABLE = 28, + UNSUPPORTED_EXCHANGE_TYPE = 29, + UNEQUAL_PAYLOAD_LENGTHS = 30, + + /* ISAKMP status type */ + CONNECTED = 16384, + + /* IPSEC DOI additions; status types (RFC2407 IPSEC DOI 4.6.3) + * These must be sent under the protection of an ISAKMP SA. + */ + IPSEC_RESPONDER_LIFETIME = 24576, + IPSEC_REPLAY_STATUS = 24577, + IPSEC_INITIAL_CONTACT = 24578, + + /* RFC 3706 DPD */ + R_U_THERE = 36136, + R_U_THERE_ACK = 36137 + + } notification_t; + + +/* Public key algorithm number + * Same numbering as used in DNSsec + * See RFC 2535 DNSsec 3.2 The KEY Algorithm Number Specification. + * Also found in BIND 8.2.2 include/isc/dst.h as DST algorithm codes. + */ + +enum pubkey_alg +{ + PUBKEY_ALG_RSA = 1, + PUBKEY_ALG_DSA = 3, +}; + +/* Limits on size of RSA moduli. + * The upper bound matches that of DNSsec (see RFC 2537). + * The lower bound must be more than 11 octets for certain + * the encoding to work, but it must be much larger for any + * real security. For now, we require 512 bits. + */ + +#define RSA_MIN_OCTETS_RFC 12 + +#define RSA_MIN_OCTETS (512 / BITS_PER_BYTE) +#define RSA_MIN_OCTETS_UGH "RSA modulus too small for security: less than 512 bits" + +#define RSA_MAX_OCTETS (8192 / BITS_PER_BYTE) +#define RSA_MAX_OCTETS_UGH "RSA modulus too large: more than 8192 bits" + +/* Note: RFC 2537 encoding adds a few bytes. If you use a small + * modulus like 3, the overhead is only 2 bytes + */ +#define RSA_MAX_ENCODING_BYTES (RSA_MAX_OCTETS + 2) + +/* socket address family info */ + +struct af_info +{ + int af; + const char *name; + size_t ia_sz; + size_t sa_sz; + int mask_cnt; + u_int8_t id_addr, id_subnet, id_range; + const ip_address *any; + const ip_subnet *none; /* 0.0.0.0/32 or IPv6 equivalent */ + const ip_subnet *all; /* 0.0.0.0/0 or IPv6 equivalent */ +}; + +extern const struct af_info + af_inet4_info, + af_inet6_info; + +extern const struct af_info *aftoinfo(int af); + +extern enum_names af_names; + +#define subnetisaddr(sn, a) (subnetishost(sn) && addrinsubnet((a), (sn))) +extern bool subnetisnone(const ip_subnet *sn); + +/* BIND enumerated types */ + +extern enum_names + rr_qtype_names, + rr_type_names, + rr_class_names; + +/* How authenticated is info that might have come from DNS? + * In order of increasing confidence. + */ +enum dns_auth_level { + DAL_UNSIGNED, /* AD in response, but no signature: no authentication */ + DAL_NOTSEC, /* no AD in response: authentication impossible */ + DAL_SIGNED, /* AD and signature in response: authentic */ + DAL_LOCAL /* locally provided (pretty good) */ +}; + +/* + * define a macro for use in error messages + */ + +#ifdef USE_KEYRR +#define RRNAME "TXT or KEY" +#else +#define RRNAME "TXT" +#endif + +/* natt traversal types */ +extern const char *const natt_type_bitnames[]; + +#endif /* _CONSTANTS_H */ diff --git a/src/pluto/cookie.c b/src/pluto/cookie.c new file mode 100644 index 000000000..458120e46 --- /dev/null +++ b/src/pluto/cookie.c @@ -0,0 +1,67 @@ +/* cookie generation/verification routines. + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: cookie.c,v 1.2 2005/08/17 16:38:20 as Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "sha1.h" +#include "rnd.h" +#include "cookie.h" + +const u_char zero_cookie[COOKIE_SIZE]; /* guaranteed 0 */ + +/* Generate a cookie. + * First argument is true if we're to create an Initiator cookie. + * Length SHOULD be a multiple of sizeof(u_int32_t). + */ +void +get_cookie(bool initiator, u_int8_t *cookie, int length, const ip_address *addr) +{ + u_char buffer[SHA1_DIGEST_SIZE]; + SHA1_CTX ctx; + + do { + if (initiator) + { + get_rnd_bytes(cookie, length); + } + else /* Responder cookie */ + { + /* This looks as good as any way */ + size_t addr_length; + static u_int32_t counter = 0; + unsigned char addr_buff[ + sizeof(union {struct in_addr A; struct in6_addr B;})]; + + addr_length = addrbytesof(addr, addr_buff, sizeof(addr_buff)); + SHA1Init(&ctx); + SHA1Update(&ctx, addr_buff, addr_length); + SHA1Update(&ctx, secret_of_the_day, sizeof(secret_of_the_day)); + counter++; + SHA1Update(&ctx, (const void *) &counter, sizeof(counter)); + SHA1Final(buffer, &ctx); + memcpy(cookie, buffer, length); + } + } while (is_zero_cookie(cookie)); /* probably never loops */ +} diff --git a/src/pluto/cookie.h b/src/pluto/cookie.h new file mode 100644 index 000000000..f5b0e64d1 --- /dev/null +++ b/src/pluto/cookie.h @@ -0,0 +1,24 @@ +/* cookie generation/verification routines. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: cookie.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#include + +extern const u_char zero_cookie[COOKIE_SIZE]; /* guaranteed 0 */ + +extern void get_cookie(bool initiator, u_int8_t *cookie, int length + , const ip_address *addr); + +#define is_zero_cookie(cookie) all_zero((cookie), COOKIE_SIZE) diff --git a/src/pluto/crl.c b/src/pluto/crl.c new file mode 100644 index 000000000..05e8d1402 --- /dev/null +++ b/src/pluto/crl.c @@ -0,0 +1,763 @@ +/* Support of X.509 certificate revocation lists (CRLs) + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: crl.c,v 1.12 2005/12/06 22:49:57 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "asn1.h" +#include "oid.h" +#include "x509.h" +#include "crl.h" +#include "ca.h" +#include "certs.h" +#include "keys.h" +#include "whack.h" +#include "fetch.h" +#include "sha1.h" + +/* chained lists of X.509 crls */ + +static x509crl_t *x509crls = NULL; + +/* ASN.1 definition of an X.509 certificate list */ + +static const asn1Object_t crlObjects[] = { + { 0, "certificateList", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ + { 1, "tbsCertList", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "version", ASN1_INTEGER, ASN1_OPT | + ASN1_BODY }, /* 2 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ + { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 4 */ + { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ + { 2, "thisUpdate", ASN1_EOC, ASN1_RAW }, /* 6 */ + { 2, "nextUpdate", ASN1_EOC, ASN1_RAW }, /* 7 */ + { 2, "revokedCertificates", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 8 */ + { 3, "certList", ASN1_SEQUENCE, ASN1_NONE }, /* 9 */ + { 4, "userCertificate", ASN1_INTEGER, ASN1_BODY }, /* 10 */ + { 4, "revocationDate", ASN1_EOC, ASN1_RAW }, /* 11 */ + { 4, "crlEntryExtensions", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 12 */ + { 5, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */ + { 6, "extnID", ASN1_OID, ASN1_BODY }, /* 14 */ + { 6, "critical", ASN1_BOOLEAN, ASN1_DEF | + ASN1_BODY }, /* 15 */ + { 6, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 16 */ + { 4, "end opt or loop", ASN1_EOC, ASN1_END }, /* 17 */ + { 2, "end opt or loop", ASN1_EOC, ASN1_END }, /* 18 */ + { 2, "optional extensions", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 19 */ + { 3, "crlExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */ + { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */ + { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */ + { 5, "critical", ASN1_BOOLEAN, ASN1_DEF | + ASN1_BODY }, /* 23 */ + { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */ + { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */ + { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */ + }; + +#define CRL_OBJ_CERTIFICATE_LIST 0 +#define CRL_OBJ_TBS_CERT_LIST 1 +#define CRL_OBJ_VERSION 2 +#define CRL_OBJ_SIG_ALG 4 +#define CRL_OBJ_ISSUER 5 +#define CRL_OBJ_THIS_UPDATE 6 +#define CRL_OBJ_NEXT_UPDATE 7 +#define CRL_OBJ_USER_CERTIFICATE 10 +#define CRL_OBJ_REVOCATION_DATE 11 +#define CRL_OBJ_CRL_ENTRY_EXTN_ID 14 +#define CRL_OBJ_CRL_ENTRY_CRITICAL 15 +#define CRL_OBJ_CRL_ENTRY_EXTN_VALUE 16 +#define CRL_OBJ_EXTN_ID 22 +#define CRL_OBJ_CRITICAL 23 +#define CRL_OBJ_EXTN_VALUE 24 +#define CRL_OBJ_ALGORITHM 27 +#define CRL_OBJ_SIGNATURE 28 +#define CRL_OBJ_ROOF 29 + + +const x509crl_t empty_x509crl = { + NULL , /* *next */ + UNDEFINED_TIME, /* installed */ + NULL , /* distributionPoints */ + { NULL, 0 } , /* certificateList */ + { NULL, 0 } , /* tbsCertList */ + 1 , /* version */ + OID_UNKNOWN , /* sigAlg */ + { NULL, 0 } , /* issuer */ + UNDEFINED_TIME, /* thisUpdate */ + UNDEFINED_TIME, /* nextUpdate */ + NULL , /* revokedCertificates */ + /* crlExtensions */ + /* extension */ + /* extnID */ + /* critical */ + /* extnValue */ + { NULL, 0 } , /* authKeyID */ + { NULL, 0 } , /* authKeySerialNumber */ + OID_UNKNOWN , /* algorithm */ + { NULL, 0 } /* signature */ +}; + +/* + * get the X.509 CRL with a given issuer + */ +static x509crl_t* +get_x509crl(chunk_t issuer, chunk_t serial, chunk_t keyid) +{ + x509crl_t *crl = x509crls; + x509crl_t *prev_crl = NULL; + + while (crl != NULL) + { + if ((keyid.ptr != NULL && crl->authKeyID.ptr != NULL) + ? same_keyid(keyid, crl->authKeyID) + : (same_dn(crl->issuer, issuer) && same_serial(serial, crl->authKeySerialNumber))) + { + if (crl != x509crls) + { + /* bring the CRL up front */ + prev_crl->next = crl->next; + crl->next = x509crls; + x509crls = crl; + } + return crl; + } + prev_crl = crl; + crl = crl->next; + } + return NULL; +} + +/* + * free the dynamic memory used to store revoked certificates + */ +static void +free_revoked_certs(revokedCert_t* revokedCerts) +{ + while (revokedCerts != NULL) + { + revokedCert_t * revokedCert = revokedCerts; + revokedCerts = revokedCert->next; + pfree(revokedCert); + } +} + +/* + * free the dynamic memory used to store CRLs + */ +void +free_crl(x509crl_t *crl) +{ + free_revoked_certs(crl->revokedCertificates); + free_generalNames(crl->distributionPoints, TRUE); + pfree(crl->certificateList.ptr); + pfree(crl); +} + +static void +free_first_crl(void) +{ + x509crl_t *crl = x509crls; + + x509crls = crl->next; + free_crl(crl); +} + +void +free_crls(void) +{ + lock_crl_list("free_crls"); + + while (x509crls != NULL) + free_first_crl(); + + unlock_crl_list("free_crls"); +} + +/* + * Insert X.509 CRL into chained list + */ +bool +insert_crl(chunk_t blob, chunk_t crl_uri, bool cache_crl) +{ + x509crl_t *crl = alloc_thing(x509crl_t, "x509crl"); + + *crl = empty_x509crl; + + if (parse_x509crl(blob, 0, crl)) + { + x509cert_t *issuer_cert; + x509crl_t *oldcrl; + bool valid_sig; + generalName_t *gn; + + /* add distribution point */ + gn = alloc_thing(generalName_t, "generalName"); + gn->kind = GN_URI; + gn->name = crl_uri; + gn->next = crl->distributionPoints; + crl->distributionPoints = gn; + + lock_authcert_list("insert_crl"); + /* get the issuer cacert */ + issuer_cert = get_authcert(crl->issuer, crl->authKeySerialNumber, + crl->authKeyID, AUTH_CA); + if (issuer_cert == NULL) + { + plog("crl issuer cacert not found"); + free_crl(crl); + unlock_authcert_list("insert_crl"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("crl issuer cacert found") + ) + + /* check the issuer's signature of the crl */ + valid_sig = check_signature(crl->tbsCertList, crl->signature + , crl->algorithm, crl->algorithm, issuer_cert); + unlock_authcert_list("insert_crl"); + + if (!valid_sig) + { + free_crl(crl); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("crl signature is valid") + ) + + lock_crl_list("insert_crl"); + oldcrl = get_x509crl(crl->issuer, crl->authKeySerialNumber + , crl->authKeyID); + + if (oldcrl != NULL) + { + if (crl->thisUpdate > oldcrl->thisUpdate) + { + /* keep any known CRL distribution points */ + add_distribution_points(oldcrl->distributionPoints + , &crl->distributionPoints); + + /* now delete the old CRL */ + free_first_crl(); + DBG(DBG_CONTROL, + DBG_log("thisUpdate is newer - existing crl deleted") + ) + } + else + { + unlock_crl_list("insert_crls"); + DBG(DBG_CONTROL, + DBG_log("thisUpdate is not newer - existing crl not replaced"); + ) + free_crl(crl); + return oldcrl->nextUpdate - time(NULL) > 2*crl_check_interval; + } + } + + /* insert new CRL */ + crl->next = x509crls; + x509crls = crl; + + unlock_crl_list("insert_crl"); + + /* If crl caching is enabled then the crl is saved locally. + * Only http or ldap URIs are cached but not local file URIs. + * The issuer's subjectKeyID is used as a unique filename + */ + if (cache_crl && strncasecmp(crl_uri.ptr, "file", 4) != 0) + { + char path[BUF_LEN]; + char buf[BUF_LEN]; + char digest_buf[SHA1_DIGEST_SIZE]; + chunk_t subjectKeyID = { digest_buf, SHA1_DIGEST_SIZE }; + + if (issuer_cert->subjectKeyID.ptr == NULL) + compute_subjectKeyID(issuer_cert, subjectKeyID); + else + subjectKeyID = issuer_cert->subjectKeyID; + + datatot(subjectKeyID.ptr, subjectKeyID.len, 16, buf, BUF_LEN); + snprintf(path, BUF_LEN, "%s/%s.crl", CRL_PATH, buf); + write_chunk(path, "crl", crl->certificateList, 0022, TRUE); + } + + /* is the fetched crl valid? */ + return crl->nextUpdate - time(NULL) > 2*crl_check_interval; + } + else + { + plog(" error in X.509 crl"); + free_crl(crl); + return FALSE; + } +} + +/* + * Loads CRLs + */ +void +load_crls(void) +{ + struct dirent **filelist; + u_char buf[BUF_LEN]; + u_char *save_dir; + int n; + + /* change directory to specified path */ + save_dir = getcwd(buf, BUF_LEN); + if (chdir(CRL_PATH)) + { + plog("Could not change to directory '%s'", CRL_PATH); + } + else + { + plog("Changing to directory '%s'", CRL_PATH); + n = scandir(CRL_PATH, &filelist, file_select, alphasort); + + if (n < 0) + plog(" scandir() error"); + else + { + while (n--) + { + bool pgp = FALSE; + chunk_t blob = empty_chunk; + char *filename = filelist[n]->d_name; + + if (load_coded_file(filename, NULL, "crl", &blob, &pgp)) + { + chunk_t crl_uri; + + crl_uri.len = 7 + sizeof(CRL_PATH) + strlen(filename); + crl_uri.ptr = alloc_bytes(crl_uri.len + 1, "crl uri"); + + /* build CRL file URI */ + snprintf(crl_uri.ptr, crl_uri.len + 1, "file://%s/%s" + , CRL_PATH, filename); + + insert_crl(blob, crl_uri, FALSE); + } + free(filelist[n]); + } + free(filelist); + } + } + /* restore directory path */ + chdir(save_dir); +} + +/* + * Parses a CRL revocation reason code + */ +static crl_reason_t +parse_crl_reasonCode(chunk_t object) +{ + crl_reason_t reason = REASON_UNSPECIFIED; + + if (*object.ptr == ASN1_ENUMERATED + && asn1_length(&object) == 1) + { + reason = *object.ptr; + } + + DBG(DBG_PARSING, + DBG_log(" '%s'", enum_name(&crl_reason_names, reason)) + ) + return reason; +} + +/* + * Parses an X.509 CRL + */ +bool +parse_x509crl(chunk_t blob, u_int level0, x509crl_t *crl) +{ + u_char buf[BUF_LEN]; + asn1_ctx_t ctx; + bool critical; + chunk_t extnID; + chunk_t userCertificate; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < CRL_OBJ_ROOF) + { + if (!extract_object(crlObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + /* those objects which will parsed further need the next higher level */ + level++; + + switch (objectID) { + case CRL_OBJ_CERTIFICATE_LIST: + crl->certificateList = object; + break; + case CRL_OBJ_TBS_CERT_LIST: + crl->tbsCertList = object; + break; + case CRL_OBJ_VERSION: + crl->version = (object.len) ? (1+(u_int)*object.ptr) : 1; + DBG(DBG_PARSING, + DBG_log(" v%d", crl->version); + ) + break; + case CRL_OBJ_SIG_ALG: + crl->sigAlg = parse_algorithmIdentifier(object, level, NULL); + break; + case CRL_OBJ_ISSUER: + crl->issuer = object; + DBG(DBG_PARSING, + dntoa(buf, BUF_LEN, object); + DBG_log(" '%s'",buf) + ) + break; + case CRL_OBJ_THIS_UPDATE: + crl->thisUpdate = parse_time(object, level); + break; + case CRL_OBJ_NEXT_UPDATE: + crl->nextUpdate = parse_time(object, level); + break; + case CRL_OBJ_USER_CERTIFICATE: + userCertificate = object; + break; + case CRL_OBJ_REVOCATION_DATE: + { + /* put all the serial numbers and the revocation date in a chained list + with revocedCertificates pointing to the first revoked certificate */ + + revokedCert_t *revokedCert = alloc_thing(revokedCert_t, "revokedCert"); + revokedCert->userCertificate = userCertificate; + revokedCert->revocationDate = parse_time(object, level); + revokedCert->revocationReason = REASON_UNSPECIFIED; + revokedCert->next = crl->revokedCertificates; + crl->revokedCertificates = revokedCert; + } + break; + case CRL_OBJ_CRL_ENTRY_EXTN_ID: + case CRL_OBJ_EXTN_ID: + extnID = object; + break; + case CRL_OBJ_CRL_ENTRY_CRITICAL: + case CRL_OBJ_CRITICAL: + critical = object.len && *object.ptr; + DBG(DBG_PARSING, + DBG_log(" %s",(critical)?"TRUE":"FALSE"); + ) + break; + case CRL_OBJ_CRL_ENTRY_EXTN_VALUE: + case CRL_OBJ_EXTN_VALUE: + { + u_int extn_oid = known_oid(extnID); + + if (extn_oid == OID_CRL_REASON_CODE) + { + crl->revokedCertificates->revocationReason = + parse_crl_reasonCode(object); + } + else if (extn_oid == OID_AUTHORITY_KEY_ID) + { + parse_authorityKeyIdentifier(object, level + , &crl->authKeyID, &crl->authKeySerialNumber); + } + } + break; + case CRL_OBJ_ALGORITHM: + crl->algorithm = parse_algorithmIdentifier(object, level, NULL); + break; + case CRL_OBJ_SIGNATURE: + crl->signature = object; + break; + default: + break; + } + objectID++; + } + time(&crl->installed); + return TRUE; +} + +/* Checks if the current certificate is revoked. It goes through the + * list of revoked certificates of the corresponding crl. Either the + * status CERT_GOOD or CERT_REVOKED is returned + */ +static cert_status_t +check_revocation(const x509crl_t *crl, chunk_t serial +, time_t *revocationDate, crl_reason_t * revocationReason) +{ + revokedCert_t *revokedCert = crl->revokedCertificates; + + *revocationDate = UNDEFINED_TIME; + *revocationReason = REASON_UNSPECIFIED; + + DBG(DBG_CONTROL, + DBG_dump_chunk("serial number:", serial) + ) + + while(revokedCert != NULL) + { + /* compare serial numbers */ + if (revokedCert->userCertificate.len == serial.len && + memcmp(revokedCert->userCertificate.ptr, serial.ptr, serial.len) == 0) + { + *revocationDate = revokedCert->revocationDate; + *revocationReason = revokedCert->revocationReason; + return CERT_REVOKED; + } + revokedCert = revokedCert->next; + } + return CERT_GOOD; +} + +/* + * check if any crls are about to expire + */ +void +check_crls(void) +{ + x509crl_t *crl; + + lock_crl_list("check_crls"); + crl = x509crls; + + while (crl != NULL) + { + time_t time_left = crl->nextUpdate - time(NULL); + u_char buf[BUF_LEN]; + + DBG(DBG_CONTROL, + dntoa(buf, BUF_LEN, crl->issuer); + DBG_log("issuer: '%s'",buf); + if (crl->authKeyID.ptr != NULL) + { + datatot(crl->authKeyID.ptr, crl->authKeyID.len, ':' + , buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + DBG_log("%ld seconds left", time_left) + ) + if (time_left < 2*crl_check_interval) + { + fetch_req_t *req = build_crl_fetch_request(crl->issuer + , crl->authKeySerialNumber + , crl->authKeyID, crl->distributionPoints); + add_crl_fetch_request(req); + } + crl = crl->next; + } + unlock_crl_list("check_crls"); +} + +/* + * verify if a cert hasn't been revoked by a crl + */ +cert_status_t +verify_by_crl(const x509cert_t *cert, time_t *until, time_t *revocationDate +, crl_reason_t *revocationReason) +{ + x509crl_t *crl; + + ca_info_t *ca = get_ca_info(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID); + + generalName_t *crluri = (ca == NULL)? NULL : ca->crluri; + + *revocationDate = UNDEFINED_TIME; + *revocationReason = REASON_UNSPECIFIED; + + lock_crl_list("verify_by_crl"); + crl = get_x509crl(cert->issuer, cert->authKeySerialNumber, cert->authKeyID); + + if (crl == NULL) + { + unlock_crl_list("verify_by_crl"); + plog("crl not found"); + + if (cert->crlDistributionPoints != NULL) + { + fetch_req_t *req = build_crl_fetch_request(cert->issuer + , cert->authKeySerialNumber + , cert->authKeyID, cert->crlDistributionPoints); + add_crl_fetch_request(req); + } + + if (crluri != NULL) + { + fetch_req_t *req = build_crl_fetch_request(cert->issuer + , cert->authKeySerialNumber + , cert->authKeyID, crluri); + add_crl_fetch_request(req); + } + + if (cert->crlDistributionPoints != 0 || crluri != NULL) + { + wake_fetch_thread("verify_by_crl"); + return CERT_UNKNOWN; + } + else + return CERT_UNDEFINED; + } + else + { + x509cert_t *issuer_cert; + bool valid; + + DBG(DBG_CONTROL, + DBG_log("crl found") + ) + + add_distribution_points(cert->crlDistributionPoints + , &crl->distributionPoints); + + add_distribution_points(crluri + , &crl->distributionPoints); + + lock_authcert_list("verify_by_crl"); + + issuer_cert = get_authcert(crl->issuer, crl->authKeySerialNumber + , crl->authKeyID, AUTH_CA); + valid = check_signature(crl->tbsCertList, crl->signature + , crl->algorithm, crl->algorithm, issuer_cert); + + unlock_authcert_list("verify_by_crl"); + + if (valid) + { + cert_status_t status; + + DBG(DBG_CONTROL, + DBG_log("crl signature is valid") + ) + /* return the expiration date */ + *until = crl->nextUpdate; + + /* has the certificate been revoked? */ + status = check_revocation(crl, cert->serialNumber, revocationDate + , revocationReason); + + if (*until < time(NULL)) + { + fetch_req_t *req; + + plog("crl update is overdue since %s" + , timetoa(until, TRUE)); + + /* try to fetch a crl update */ + req = build_crl_fetch_request(crl->issuer + , crl->authKeySerialNumber + , crl->authKeyID, crl->distributionPoints); + unlock_crl_list("verify_by_crl"); + + add_crl_fetch_request(req); + wake_fetch_thread("verify_by_crl"); + } + else + { + unlock_crl_list("verify_by_crl"); + DBG(DBG_CONTROL, + DBG_log("crl is valid") + ) + } + return status; + } + else + { + unlock_crl_list("verify_by_crl"); + plog("crl signature is invalid"); + return CERT_UNKNOWN; + } + } +} + +/* + * list all X.509 crls in the chained list + */ +void +list_crls(bool utc, bool strict) +{ + x509crl_t *crl; + + lock_crl_list("list_crls"); + crl = x509crls; + + if (crl != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of X.509 CRLs:"); + whack_log(RC_COMMENT, " "); + } + + while (crl != NULL) + { + u_char buf[BUF_LEN]; + u_int revoked = 0; + revokedCert_t *revokedCert = crl->revokedCertificates; + + /* count number of revoked certificates in CRL */ + while (revokedCert != NULL) + { + revoked++; + revokedCert = revokedCert->next; + } + + whack_log(RC_COMMENT, "%s, revoked certs: %d", + timetoa(&crl->installed, utc), revoked); + dntoa(buf, BUF_LEN, crl->issuer); + whack_log(RC_COMMENT, " issuer: '%s'", buf); + + list_distribution_points(crl->distributionPoints); + + whack_log(RC_COMMENT, " updates: this %s", + timetoa(&crl->thisUpdate, utc)); + whack_log(RC_COMMENT, " next %s %s", + timetoa(&crl->nextUpdate, utc), + check_expiry(crl->nextUpdate, CRL_WARNING_INTERVAL, strict)); + if (crl->authKeyID.ptr != NULL) + { + datatot(crl->authKeyID.ptr, crl->authKeyID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authkey: %s", buf); + } + if (crl->authKeySerialNumber.ptr != NULL) + { + datatot(crl->authKeySerialNumber.ptr, crl->authKeySerialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " aserial: %s", buf); + } + + crl = crl->next; + } + unlock_crl_list("list_crls"); +} + diff --git a/src/pluto/crl.h b/src/pluto/crl.h new file mode 100644 index 000000000..9f985b6cd --- /dev/null +++ b/src/pluto/crl.h @@ -0,0 +1,87 @@ +/* Support of X.509 certificate revocation lists (CRLs) + * Copyright (C) 2000-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: crl.h,v 1.4 2005/07/18 19:36:22 as Exp $ + */ + +#include "constants.h" + +/* access structure for a revoked serial number */ + +typedef struct revokedCert revokedCert_t; + +struct revokedCert{ + revokedCert_t *next; + chunk_t userCertificate; + time_t revocationDate; + crl_reason_t revocationReason; +}; + +/* storage structure for an X.509 CRL */ + +typedef struct x509crl x509crl_t; + +struct x509crl { + x509crl_t *next; + time_t installed; + generalName_t *distributionPoints; + chunk_t certificateList; + chunk_t tbsCertList; + u_int version; + /* signature */ + int sigAlg; + chunk_t issuer; + time_t thisUpdate; + time_t nextUpdate; + revokedCert_t *revokedCertificates; + /* v2 extensions */ + /* crlExtensions */ + /* extension */ + /* extnID */ + /* critical */ + /* extnValue */ + chunk_t authKeyID; + chunk_t authKeySerialNumber; + + /* signatureAlgorithm */ + int algorithm; + chunk_t signature; +}; + +/* apply a strict CRL policy + * flag set in plutomain.c and used in ipsec_doi.c and rcv_whack.c + */ +extern bool strict_crl_policy; + +/* + * cache the retrieved CRLs by storing them locally as a file + */ +extern bool cache_crls; + +/* + * check periodically for expired crls + */ +extern long crl_check_interval; + +/* used for initialization */ +extern const x509crl_t empty_x509crl; + +extern bool parse_x509crl(chunk_t blob, u_int level0, x509crl_t *crl); +extern void load_crls(void); +extern void check_crls(void); +extern bool insert_crl(chunk_t blob, chunk_t crl_uri, bool cache_crl); +extern cert_status_t verify_by_crl(const x509cert_t *cert, time_t *until + , time_t *revocationDate, crl_reason_t *revocationReason); +extern void list_crls(bool utc, bool strict); +extern void free_crls(void); +extern void free_crl(x509crl_t *crl); diff --git a/src/pluto/crypto.c b/src/pluto/crypto.c new file mode 100644 index 000000000..f1b7c3f5f --- /dev/null +++ b/src/pluto/crypto.c @@ -0,0 +1,627 @@ +/* crypto interfaces + * Copyright (C) 1998-2001 D. Hugh Redelmeier + * Copyright (C) 2007 Andreas Steffen + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: crypto.c,v 1.5 2005/12/06 22:51:34 as Exp $ + */ + +#include +#include +#include +#include + +#include +#define HEADER_DES_LOCL_H /* stupid trick to force prototype decl in */ +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "state.h" +#include "log.h" +#include "md5.h" +#include "sha1.h" +#include "crypto.h" /* requires sha1.h and md5.h */ +#include "alg_info.h" +#include "ike_alg.h" + + +/* moduli and generator. */ + +static MP_INT + modp1024_modulus, + modp1536_modulus, + modp2048_modulus, + modp3072_modulus, + modp4096_modulus, + modp6144_modulus, + modp8192_modulus; + +MP_INT groupgenerator; /* MODP group generator (2) */ + +static void do_3des(u_int8_t *buf, size_t buf_len, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc); + +static struct encrypt_desc crypto_encryptor_3des = +{ + algo_type: IKE_ALG_ENCRYPT, + algo_id: OAKLEY_3DES_CBC, + algo_next: NULL, + enc_ctxsize: sizeof(des_key_schedule) * 3, + enc_blocksize: DES_CBC_BLOCK_SIZE, + keydeflen: DES_CBC_BLOCK_SIZE * 3 * BITS_PER_BYTE, + keyminlen: DES_CBC_BLOCK_SIZE * 3 * BITS_PER_BYTE, + keymaxlen: DES_CBC_BLOCK_SIZE * 3 * BITS_PER_BYTE, + do_crypt: do_3des, +}; + +/* MD5 hash test vectors + * from RFC 1321 "MD5 Message-Digest Algorithm" + * April 1992, R. Rivest, RSA Data Security + */ + +static const u_char md5_test0_msg[] = { + +}; + +static const u_char md5_test0_msg_digest[] = { + 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e +}; + +static const u_char md5_test1_msg[] = { + 0x61 +}; + +static const u_char md5_test1_msg_digest[] = { + 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, + 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 +}; + +static const u_char md5_test2_msg[] = { + 0x61, 0x62, 0x63 +}; + +static const u_char md5_test2_msg_digest[] = { + 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, + 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 +}; + +static const u_char md5_test3_msg[] = { + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74 +}; + +static const u_char md5_test3_msg_digest[] = { + 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, + 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 +}; + +static const u_char md5_test4_msg[] = { + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a +}; + +static const u_char md5_test4_msg_digest[] = { + 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, + 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b +}; + +static const u_char md5_test5_msg[] = { + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 +}; + +static const u_char md5_test5_msg_digest[] = { + 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, + 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f +}; + +static const u_char md5_test6_msg[] = { + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30 +}; + +static const u_char md5_test6_msg_digest[] = { + 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, + 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a +}; + +static const hash_testvector_t md5_hash_testvectors[] = { + { sizeof(md5_test0_msg), md5_test0_msg, md5_test0_msg_digest }, + { sizeof(md5_test1_msg), md5_test1_msg, md5_test1_msg_digest }, + { sizeof(md5_test2_msg), md5_test2_msg, md5_test2_msg_digest }, + { sizeof(md5_test3_msg), md5_test3_msg, md5_test3_msg_digest }, + { sizeof(md5_test4_msg), md5_test4_msg, md5_test4_msg_digest }, + { sizeof(md5_test5_msg), md5_test5_msg, md5_test5_msg_digest }, + { sizeof(md5_test6_msg), md5_test6_msg, md5_test6_msg_digest }, + { 0, NULL, NULL } +}; + +/* MD5 hmac test vectors + * from RFC 2202 "Test Cases for HMAC-MD5 and HMAC-SHA-1" + * September 1997, P. Cheng, IBM & R. Glenn, NIST + */ + +static const u_char md5_hmac1_key[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b +}; + +static const u_char md5_hmac1_msg[] = { + 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 +}; + +static const u_char md5_hmac1[] = { + 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c, + 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d +}; + +static const u_char md5_hmac2_key[] = { + 0x4a, 0x65, 0x66, 0x65 +}; + +static const u_char md5_hmac2_msg[] = { + 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, + 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x3f +}; + +static const u_char md5_hmac2[] = { + 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03, + 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 +}; + +static const u_char md5_hmac3_key[] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa +}; + +static const u_char md5_hmac3_msg[] = { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd +}; + +static const u_char md5_hmac3[] = { + 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88, + 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 +}; + +static const u_char md5_hmac4_key[] = { + 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 +}; + +static const u_char md5_hmac4_msg[] = { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd +}; + +static const u_char md5_hmac4[] = { + 0x69, 0x7e, 0xaf, 0x0a, 0xca, 0x3a, 0x3a, 0xea, + 0x3a, 0x75, 0x16, 0x47, 0x46, 0xff, 0xaa, 0x79 +}; + +static const u_char md5_hmac6_key[] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, +}; + +static const u_char md5_hmac6_msg[] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, + 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, + 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, + 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x2d, 0x20, + 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79, + 0x20, 0x46, 0x69, 0x72, 0x73, 0x74 +}; + +static const u_char md5_hmac6[] = { + 0x6b, 0x1a, 0xb7, 0xfe, 0x4b, 0xd7, 0xbf, 0x8f, + 0x0b, 0x62, 0xe6, 0xce, 0x61, 0xb9, 0xd0, 0xcd +}; + +static const u_char md5_hmac7_msg[] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, + 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, + 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, + 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x72, + 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x4f, 0x6e, + 0x65, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, + 0x53, 0x69, 0x7a, 0x65, 0x20, 0x44, 0x61, 0x74, + 0x61 +}; + +static const u_char md5_hmac7[] = { + 0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee, + 0x1f, 0xb1, 0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e +}; + +static const hmac_testvector_t md5_hmac_testvectors[] = { + { sizeof(md5_hmac1_key), md5_hmac1_key, sizeof(md5_hmac1_msg), md5_hmac1_msg, md5_hmac1 }, + { sizeof(md5_hmac2_key), md5_hmac2_key, sizeof(md5_hmac2_msg), md5_hmac2_msg, md5_hmac2 }, + { sizeof(md5_hmac3_key), md5_hmac3_key, sizeof(md5_hmac3_msg), md5_hmac3_msg, md5_hmac3 }, + { sizeof(md5_hmac4_key), md5_hmac4_key, sizeof(md5_hmac4_msg), md5_hmac4_msg, md5_hmac4 }, + { sizeof(md5_hmac6_key), md5_hmac6_key, sizeof(md5_hmac6_msg), md5_hmac6_msg, md5_hmac6 }, + { sizeof(md5_hmac6_key), md5_hmac6_key, sizeof(md5_hmac7_msg), md5_hmac7_msg, md5_hmac7 }, + { 0, NULL, 0, NULL, NULL } +}; + +static struct hash_desc crypto_hasher_md5 = +{ + algo_type: IKE_ALG_HASH, + algo_id: OAKLEY_MD5, + algo_next: NULL, + hash_ctx_size: sizeof(MD5_CTX), + hash_block_size: MD5_BLOCK_SIZE, + hash_digest_size: MD5_DIGEST_SIZE, + hash_testvectors: md5_hash_testvectors, + hmac_testvectors: md5_hmac_testvectors, + hash_init: (void (*)(void *)) MD5Init, + hash_update: (void (*)(void *, const u_int8_t *, size_t)) MD5Update, + hash_final: (void (*)(u_char *, void *)) MD5Final +}; + +/* SHA-1 test vectors + * from "The Secure Hash Algorithm Validation System (SHAVS)" + * July 22, 2004, Lawrence E. Bassham III, NIST + */ + +static const u_char sha1_short2_msg[] = { + 0x5e +}; + +static const u_char sha1_short2_msg_digest[] = { + 0x5e, 0x6f, 0x80, 0xa3, 0x4a, 0x97, 0x98, 0xca, + 0xfc, 0x6a, 0x5d, 0xb9, 0x6c, 0xc5, 0x7b, 0xa4, + 0xc4, 0xdb, 0x59, 0xc2 +}; + +static const u_char sha1_short4_msg[] = { + 0x9a, 0x7d, 0xfd, 0xf1, 0xec, 0xea, 0xd0, 0x6e, + 0xd6, 0x46, 0xaa, 0x55, 0xfe, 0x75, 0x71, 0x46 +}; + +static const u_char sha1_short4_msg_digest[] = { + 0x82, 0xab, 0xff, 0x66, 0x05, 0xdb, 0xe1, 0xc1, + 0x7d, 0xef, 0x12, 0xa3, 0x94, 0xfa, 0x22, 0xa8, + 0x2b, 0x54, 0x4a, 0x35 +}; + +static const u_char sha1_long2_msg[] = { + 0xf7, 0x8f, 0x92, 0x14, 0x1b, 0xcd, 0x17, 0x0a, + 0xe8, 0x9b, 0x4f, 0xba, 0x15, 0xa1, 0xd5, 0x9f, + 0x3f, 0xd8, 0x4d, 0x22, 0x3c, 0x92, 0x51, 0xbd, + 0xac, 0xbb, 0xae, 0x61, 0xd0, 0x5e, 0xd1, 0x15, + 0xa0, 0x6a, 0x7c, 0xe1, 0x17, 0xb7, 0xbe, 0xea, + 0xd2, 0x44, 0x21, 0xde, 0xd9, 0xc3, 0x25, 0x92, + 0xbd, 0x57, 0xed, 0xea, 0xe3, 0x9c, 0x39, 0xfa, + 0x1f, 0xe8, 0x94, 0x6a, 0x84, 0xd0, 0xcf, 0x1f, + 0x7b, 0xee, 0xad, 0x17, 0x13, 0xe2, 0xe0, 0x95, + 0x98, 0x97, 0x34, 0x7f, 0x67, 0xc8, 0x0b, 0x04, + 0x00, 0xc2, 0x09, 0x81, 0x5d, 0x6b, 0x10, 0xa6, + 0x83, 0x83, 0x6f, 0xd5, 0x56, 0x2a, 0x56, 0xca, + 0xb1, 0xa2, 0x8e, 0x81, 0xb6, 0x57, 0x66, 0x54, + 0x63, 0x1c, 0xf1, 0x65, 0x66, 0xb8, 0x6e, 0x3b, + 0x33, 0xa1, 0x08, 0xb0, 0x53, 0x07, 0xc0, 0x0a, + 0xff, 0x14, 0xa7, 0x68, 0xed, 0x73, 0x50, 0x60, + 0x6a, 0x0f, 0x85, 0xe6, 0xa9, 0x1d, 0x39, 0x6f, + 0x5b, 0x5c, 0xbe, 0x57, 0x7f, 0x9b, 0x38, 0x80, + 0x7c, 0x7d, 0x52, 0x3d, 0x6d, 0x79, 0x2f, 0x6e, + 0xbc, 0x24, 0xa4, 0xec, 0xf2, 0xb3, 0xa4, 0x27, + 0xcd, 0xbb, 0xfb +}; + +static const u_char sha1_long2_msg_digest[] = { + 0xcb, 0x00, 0x82, 0xc8, 0xf1, 0x97, 0xd2, 0x60, + 0x99, 0x1b, 0xa6, 0xa4, 0x60, 0xe7, 0x6e, 0x20, + 0x2b, 0xad, 0x27, 0xb3 +}; + +static const hash_testvector_t sha1_hash_testvectors[] = { + { sizeof(sha1_short2_msg), sha1_short2_msg, sha1_short2_msg_digest }, + { sizeof(sha1_short4_msg), sha1_short4_msg, sha1_short4_msg_digest }, + { sizeof(sha1_long2_msg), sha1_long2_msg, sha1_long2_msg_digest }, + { 0, NULL, NULL } +}; + +/* SHA-1 hmac test vectors + * from RFC 2202 "Test Cases for HMAC-MD5 and HMAC-SHA-1" + * September 1997, P. Cheng, IBM & R. Glenn, NIST + */ + +static const u_char sha1_hmac1_key[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; + +static const u_char sha1_hmac1[] = { + 0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, + 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, + 0xf1, 0x46, 0xbe, 0x00 +}; + +static const u_char sha1_hmac2[] = { + 0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2, + 0xd2, 0x74, 0x16, 0xd5, 0xf1, 0x84, 0xdf, 0x9c, + 0x25, 0x9a, 0x7c, 0x79 +}; + +static const u_char sha1_hmac3_key[] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa +}; + +static const u_char sha1_hmac3[] = { + 0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd, + 0x91, 0xa3, 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f, + 0x63, 0xf1, 0x75, 0xd3 +}; + +static const u_char sha1_hmac4[] = { + 0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6, + 0xbc, 0x84, 0x14, 0xf9, 0xbf, 0x50, 0xc8, 0x6c, + 0x2d, 0x72, 0x35, 0xda +}; + +static const u_char sha1_hmac6[] = { + 0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e, + 0x95, 0x70, 0x56, 0x37, 0xce, 0x8a, 0x3b, 0x55, + 0xed, 0x40, 0x21, 0x12 +}; + +static const u_char sha1_hmac7[] = { + 0xe8, 0xe9, 0x9d, 0x0f, 0x45, 0x23, 0x7d, 0x78, + 0x6d, 0x6b, 0xba, 0xa7, 0x96, 0x5c, 0x78, 0x08, + 0xbb, 0xff, 0x1a, 0x91 +}; + +static const hmac_testvector_t sha1_hmac_testvectors[] = { + { sizeof(sha1_hmac1_key), sha1_hmac1_key, sizeof(md5_hmac1_msg), md5_hmac1_msg, sha1_hmac1 }, + { sizeof(md5_hmac2_key), md5_hmac2_key, sizeof(md5_hmac2_msg), md5_hmac2_msg, sha1_hmac2 }, + { sizeof(sha1_hmac3_key), sha1_hmac3_key, sizeof(md5_hmac3_msg), md5_hmac3_msg, sha1_hmac3 }, + { sizeof(md5_hmac4_key), md5_hmac4_key, sizeof(md5_hmac4_msg), md5_hmac4_msg, sha1_hmac4 }, + { sizeof(md5_hmac6_key), md5_hmac6_key, sizeof(md5_hmac6_msg), md5_hmac6_msg, sha1_hmac6 }, + { sizeof(md5_hmac6_key), md5_hmac6_key, sizeof(md5_hmac7_msg), md5_hmac7_msg, sha1_hmac7 }, + { 0, NULL, 0, NULL, NULL } +}; + +static struct hash_desc crypto_hasher_sha1 = +{ + algo_type: IKE_ALG_HASH, + algo_id: OAKLEY_SHA, + algo_next: NULL, + hash_ctx_size: sizeof(SHA1_CTX), + hash_block_size: SHA1_BLOCK_SIZE, + hash_digest_size: SHA1_DIGEST_SIZE, + hash_testvectors: sha1_hash_testvectors, + hmac_testvectors: sha1_hmac_testvectors, + hash_init: (void (*)(void *)) SHA1Init, + hash_update: (void (*)(void *, const u_int8_t *, size_t)) SHA1Update, + hash_final: (void (*)(u_char *, void *)) SHA1Final +}; + +void +init_crypto(void) +{ + if (mpz_init_set_str(&groupgenerator, MODP_GENERATOR, 10) != 0 + || mpz_init_set_str(&modp1024_modulus, MODP1024_MODULUS, 16) != 0 + || mpz_init_set_str(&modp1536_modulus, MODP1536_MODULUS, 16) != 0 + || mpz_init_set_str(&modp2048_modulus, MODP2048_MODULUS, 16) != 0 + || mpz_init_set_str(&modp3072_modulus, MODP3072_MODULUS, 16) != 0 + || mpz_init_set_str(&modp4096_modulus, MODP4096_MODULUS, 16) != 0 + || mpz_init_set_str(&modp6144_modulus, MODP6144_MODULUS, 16) != 0 + || mpz_init_set_str(&modp8192_modulus, MODP8192_MODULUS, 16) != 0) + exit_log("mpz_init_set_str() failed in init_crypto()"); + + ike_alg_add((struct ike_alg *) &crypto_encryptor_3des); + ike_alg_add((struct ike_alg *) &crypto_hasher_sha1); + ike_alg_add((struct ike_alg *) &crypto_hasher_md5); + ike_alg_init(); + ike_alg_test(); +} + +/* Oakley group description + * + * See RFC2409 "The Internet key exchange (IKE)" 6. + */ + +const struct oakley_group_desc unset_group = {0, NULL, 0}; /* magic signifier */ + +const struct oakley_group_desc oakley_group[OAKLEY_GROUP_SIZE] = { +# define BYTES(bits) (((bits) + BITS_PER_BYTE - 1) / BITS_PER_BYTE) + { OAKLEY_GROUP_MODP1024, &modp1024_modulus, BYTES(1024) }, + { OAKLEY_GROUP_MODP1536, &modp1536_modulus, BYTES(1536) }, + { OAKLEY_GROUP_MODP2048, &modp2048_modulus, BYTES(2048) }, + { OAKLEY_GROUP_MODP3072, &modp3072_modulus, BYTES(3072) }, + { OAKLEY_GROUP_MODP4096, &modp4096_modulus, BYTES(4096) }, + { OAKLEY_GROUP_MODP6144, &modp6144_modulus, BYTES(6144) }, + { OAKLEY_GROUP_MODP8192, &modp8192_modulus, BYTES(8192) }, +# undef BYTES +}; + +const struct oakley_group_desc * +lookup_group(u_int16_t group) +{ + int i; + + for (i = 0; i != elemsof(oakley_group); i++) + if (group == oakley_group[i].group) + return &oakley_group[i]; + return NULL; +} + +/* Encryption Routines + * + * Each uses and updates the state object's st_new_iv. + * This must already be initialized. + */ + +/* encrypt or decrypt part of an IKE message using DES + * See RFC 2409 "IKE" Appendix B + */ +static void __attribute__ ((unused)) +do_des(bool enc, void *buf, size_t buf_len, struct state *st) +{ + des_key_schedule ks; + + (void) des_set_key((des_cblock *)st->st_enc_key.ptr, ks); + + passert(st->st_new_iv_len >= DES_CBC_BLOCK_SIZE); + st->st_new_iv_len = DES_CBC_BLOCK_SIZE; /* truncate */ + + des_ncbc_encrypt((des_cblock *)buf, (des_cblock *)buf, buf_len, + ks, + (des_cblock *)st->st_new_iv, enc); +} + +/* encrypt or decrypt part of an IKE message using 3DES + * See RFC 2409 "IKE" Appendix B + */ +static void +do_3des(u_int8_t *buf, size_t buf_len, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc) +{ + des_key_schedule ks[3]; + + passert (!key_size || (key_size==(DES_CBC_BLOCK_SIZE * 3))) + (void) des_set_key((des_cblock *)key + 0, ks[0]); + (void) des_set_key((des_cblock *)key + 1, ks[1]); + (void) des_set_key((des_cblock *)key + 2, ks[2]); + + des_ede3_cbc_encrypt((des_cblock *)buf, (des_cblock *)buf, buf_len, + ks[0], ks[1], ks[2], + (des_cblock *)iv, enc); +} + +/* hash and prf routines */ +void +crypto_cbc_encrypt(const struct encrypt_desc *e, bool enc, u_int8_t *buf, size_t size, struct state *st) +{ + passert(st->st_new_iv_len >= e->enc_blocksize); + st->st_new_iv_len = e->enc_blocksize; /* truncate */ + + e->do_crypt(buf, size, st->st_enc_key.ptr, st->st_enc_key.len, st->st_new_iv, enc); + /* + e->set_key(&ctx, st->st_enc_key.ptr, st->st_enc_key.len); + e->cbc_crypt(&ctx, buf, size, st->st_new_iv, enc); + */ +} + +/* HMAC package + * rfc2104.txt specifies how HMAC works. + */ + +void +hmac_init(struct hmac_ctx *ctx, + const struct hash_desc *h, + const u_char *key, size_t key_len) +{ + int k; + + ctx->h = h; + ctx->hmac_digest_size = h->hash_digest_size; + + /* Prepare the two pads for the HMAC */ + + memset(ctx->buf1, '\0', h->hash_block_size); + + if (key_len <= h->hash_block_size) + { + memcpy(ctx->buf1, key, key_len); + } + else + { + h->hash_init(&ctx->hash_ctx); + h->hash_update(&ctx->hash_ctx, key, key_len); + h->hash_final(ctx->buf1, &ctx->hash_ctx); + } + + memcpy(ctx->buf2, ctx->buf1, h->hash_block_size); + + for (k = 0; k < h->hash_block_size; k++) + { + ctx->buf1[k] ^= HMAC_IPAD; + ctx->buf2[k] ^= HMAC_OPAD; + } + + hmac_reinit(ctx); +} + +void +hmac_reinit(struct hmac_ctx *ctx) +{ + ctx->h->hash_init(&ctx->hash_ctx); + ctx->h->hash_update(&ctx->hash_ctx, ctx->buf1, ctx->h->hash_block_size); +} + +void +hmac_update(struct hmac_ctx *ctx, + const u_char *data, size_t data_len) +{ + ctx->h->hash_update(&ctx->hash_ctx, data, data_len); +} + +void +hmac_final(u_char *output, struct hmac_ctx *ctx) +{ + const struct hash_desc *h = ctx->h; + + h->hash_final(output, &ctx->hash_ctx); + + h->hash_init(&ctx->hash_ctx); + h->hash_update(&ctx->hash_ctx, ctx->buf2, h->hash_block_size); + h->hash_update(&ctx->hash_ctx, output, h->hash_digest_size); + h->hash_final(output, &ctx->hash_ctx); +} diff --git a/src/pluto/crypto.h b/src/pluto/crypto.h new file mode 100644 index 000000000..48c983349 --- /dev/null +++ b/src/pluto/crypto.h @@ -0,0 +1,108 @@ +/* crypto interfaces + * Copyright (C) 1998, 1999 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: crypto.h,v 1.6 2005/04/07 20:13:30 as Exp $ + */ + +#include /* GNU MP library */ + +#include "libsha2/sha2.h" +#include "ike_alg.h" + +extern void init_crypto(void); + +/* Oakley group descriptions */ + +extern MP_INT groupgenerator; /* MODP group generator (2) */ + +struct oakley_group_desc { + u_int16_t group; + MP_INT *modulus; + size_t bytes; +}; + +extern const struct oakley_group_desc unset_group; /* magic signifier */ +extern const struct oakley_group_desc *lookup_group(u_int16_t group); +#define OAKLEY_GROUP_SIZE 7 +extern const struct oakley_group_desc oakley_group[OAKLEY_GROUP_SIZE]; + +/* unification of cryptographic encoding/decoding algorithms + * The IV is taken from and returned to st->st_new_iv. + * This allows the old IV to be retained. + * Use update_iv to commit to the new IV (for example, once a packet has + * been validated). + */ + +#define MAX_OAKLEY_KEY_LEN0 (3 * DES_CBC_BLOCK_SIZE) +#define MAX_OAKLEY_KEY_LEN (256/BITS_PER_BYTE) + +struct state; /* forward declaration, dammit */ + +void crypto_cbc_encrypt(const struct encrypt_desc *e, bool enc, u_int8_t *buf, size_t size, struct state *st); + +#define update_iv(st) memcpy((st)->st_iv, (st)->st_new_iv \ + , (st)->st_iv_len = (st)->st_new_iv_len) + +#define set_ph1_iv(st, iv) \ + passert((st)->st_ph1_iv_len <= sizeof((st)->st_ph1_iv)); \ + memcpy((st)->st_ph1_iv, (iv), (st)->st_ph1_iv_len); + +/* unification of cryptographic hashing mechanisms */ + +#ifndef NO_HASH_CTX +union hash_ctx { + MD5_CTX ctx_md5; + SHA1_CTX ctx_sha1; + sha256_context ctx_sha256; + sha512_context ctx_sha512; + }; + +/* HMAC package + * Note that hmac_ctx can be (and is) copied since there are + * no persistent pointers into it. + */ + +struct hmac_ctx { + const struct hash_desc *h; /* underlying hash function */ + size_t hmac_digest_size; /* copy of h->hash_digest_size */ + union hash_ctx hash_ctx; /* ctx for hash function */ + u_char buf1[MAX_HASH_BLOCK_SIZE]; + u_char buf2[MAX_HASH_BLOCK_SIZE]; + }; + +extern void hmac_init( + struct hmac_ctx *ctx, + const struct hash_desc *h, + const u_char *key, + size_t key_len); + +#define hmac_init_chunk(ctx, h, ch) hmac_init((ctx), (h), (ch).ptr, (ch).len) + +extern void hmac_reinit(struct hmac_ctx *ctx); /* saves recreating pads */ + +extern void hmac_update( + struct hmac_ctx *ctx, + const u_char *data, + size_t data_len); + +#define hmac_update_chunk(ctx, ch) hmac_update((ctx), (ch).ptr, (ch).len) + +extern void hmac_final(u_char *output, struct hmac_ctx *ctx); + +#define hmac_final_chunk(ch, name, ctx) { \ + pfreeany((ch).ptr); \ + (ch).len = (ctx)->hmac_digest_size; \ + (ch).ptr = alloc_bytes((ch).len, name); \ + hmac_final((ch).ptr, (ctx)); \ + } +#endif diff --git a/src/pluto/db_ops.c b/src/pluto/db_ops.c new file mode 100644 index 000000000..bbcd7918f --- /dev/null +++ b/src/pluto/db_ops.c @@ -0,0 +1,439 @@ +/* Dynamic db (proposal, transforms, attributes) handling. + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: db_ops.c,v 1.4 2005/04/07 20:13:44 as Exp $ + */ + +/* + * The stratedy is to have (full contained) struct db_prop in db_context + * pointing to ONE dynamically sizable transform vector (trans0). + * Each transform stores attrib. in ONE dyn. sizable attribute vector (attrs0) + * in a "serialized" way (attributes storage is used in linear sequence for + * subsecuent transforms). + * + * Resizing for both trans0 and attrs0 is supported: + * - For trans0: quite simple, just allocate and copy trans. vector content + * also update trans_cur (by offset) + * - For attrs0: after allocating and copying attrs, I must rewrite each + * trans->attrs present in trans0; to achieve this, calculate + * attrs pointer offset (new minus old) and iterate over + * each transform "adding" this difference. + * also update attrs_cur (by offset) + * + * db_context structure: + * +---------------------+ + * | prop | + * | .protoid | + * | .trans | --+ + * | .trans_cnt | | + * +---------------------+ <-+ + * | trans0 | ----> { trans#1 | ... | trans#i | ... } + * +---------------------+ ^ + * | trans_cur | ----------------------' current transf. + * +---------------------+ + * | attrs0 | ----> { attr#1 | ... | attr#j | ... } + * +---------------------+ ^ + * | attrs_cur | ---------------------' current attr. + * +---------------------+ + * | max_trans,max_attrs | max_trans/attrs: number of elem. of each vector + * +---------------------+ + * + * See testing examples at end for interface usage. + */ +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "state.h" +#include "packet.h" +#include "spdb.h" +#include "db_ops.h" +#include "log.h" +#include "whack.h" + +#include + +#ifndef NO_PLUTO +#else +#define passert(x) assert(x) +extern int debug; /* eg: spi.c */ +#define DBG(cond, action) { if (debug) { action ; } } +#define DBG_log(x, args...) fprintf(stderr, x "\n" , ##args); +#define alloc_thing(thing, name) alloc_bytes(sizeof (thing), name) +void * alloc_bytes(size_t size, const char *name) { + void *p=malloc(size); + if (p == NULL) + fprintf(stderr, "unable to malloc %lu bytes for %s", + (unsigned long) size, name); + memset(p, '\0', size); + return p; +} +#define pfreeany(ptr) free(ptr) + +#endif + +#ifdef NOT_YET +/* + * Allocator cache: + * Because of the single-threaded nature of pluto/spdb.c, + * alloc()/free() is exercised many times with very small + * lifetime objects. + * Just caching last object (currently it will select the + * largest) will avoid this allocation mas^Wperturbations + * + */ +struct db_ops_alloc_cache { + void *ptr; + int size; +}; +#endif + +#ifndef NO_DB_OPS_STATS +/* + * stats: do account for allocations + * displayed in db_ops_show_status() + */ +struct db_ops_stats { + int st_curr_cnt; /* current number of allocations */ + int st_total_cnt; /* total allocations so far */ + size_t st_maxsz; /* max. size requested */ +}; +#define DB_OPS_ZERO { 0, 0, 0}; +#define DB_OPS_STATS_DESC "{curr_cnt, total_cnt, maxsz}" +#define DB_OPS_STATS_STR(name) name "={%d,%d,%d} " +#define DB_OPS_STATS_F(st) (st).st_curr_cnt, (st).st_total_cnt, (int)(st).st_maxsz +static struct db_ops_stats db_context_st = DB_OPS_ZERO; +static struct db_ops_stats db_trans_st = DB_OPS_ZERO; +static struct db_ops_stats db_attrs_st = DB_OPS_ZERO; +static __inline__ void * alloc_bytes_st (size_t size, const char *str, struct db_ops_stats *st) +{ + void *ptr = alloc_bytes(size, str); + if (ptr) { + st->st_curr_cnt++; + st->st_total_cnt++; + if (size > st->st_maxsz) st->st_maxsz=size; + } + return ptr; +} +#define ALLOC_BYTES_ST(z,s,st) alloc_bytes_st(z, s, &st); +#define PFREE_ST(p,st) do { st.st_curr_cnt--; pfree(p); } while (0); + +#else + +#define ALLOC_BYTES_ST(z,s,n) alloc_bytes(z, s); +#define PFREE_ST(p,n) pfree(p); + +#endif /* NO_DB_OPS_STATS */ +/* Initialize db object + * max_trans and max_attrs can be 0, will be dynamically expanded + * as a result of "add" operations + */ +int +db_prop_init(struct db_context *ctx, u_int8_t protoid, int max_trans, int max_attrs) +{ + int ret=-1; + + ctx->trans0 = NULL; + ctx->attrs0 = NULL; + + if (max_trans > 0) { /* quite silly if not */ + ctx->trans0 = ALLOC_BYTES_ST ( sizeof (struct db_trans) * max_trans, + "db_context->trans", db_trans_st); + if (!ctx->trans0) goto out; + } + + if (max_attrs > 0) { /* quite silly if not */ + ctx->attrs0 = ALLOC_BYTES_ST (sizeof (struct db_attr) * max_attrs, + "db_context->attrs", db_attrs_st); + if (!ctx->attrs0) goto out; + } + ret = 0; +out: + if (ret < 0 && ctx->trans0) { + PFREE_ST(ctx->trans0, db_trans_st); + ctx->trans0 = NULL; + } + ctx->max_trans = max_trans; + ctx->max_attrs = max_attrs; + ctx->trans_cur = ctx->trans0; + ctx->attrs_cur = ctx->attrs0; + ctx->prop.protoid = protoid; + ctx->prop.trans = ctx->trans0; + ctx->prop.trans_cnt = 0; + return ret; +} + +/* Expand storage for transforms by number delta_trans */ +static int +db_trans_expand(struct db_context *ctx, int delta_trans) +{ + int ret = -1; + struct db_trans *new_trans, *old_trans; + int max_trans = ctx->max_trans + delta_trans; + int offset; + + old_trans = ctx->trans0; + new_trans = ALLOC_BYTES_ST ( sizeof (struct db_trans) * max_trans, + "db_context->trans (expand)", db_trans_st); + if (!new_trans) + goto out; + memcpy(new_trans, old_trans, ctx->max_trans * sizeof(struct db_trans)); + + /* update trans0 (obviously) */ + ctx->trans0 = ctx->prop.trans = new_trans; + /* update trans_cur (by offset) */ + offset = (char *)(new_trans) - (char *)(old_trans); + + { + char *cctx = (char *)(ctx->trans_cur); + + cctx += offset; + ctx->trans_cur = (struct db_trans *)cctx; + } + /* update elem count */ + ctx->max_trans = max_trans; + PFREE_ST(old_trans, db_trans_st); + ret = 0; +out: + return ret; +} +/* + * Expand storage for attributes by delta_attrs number AND + * rewrite trans->attr pointers + */ +static int +db_attrs_expand(struct db_context *ctx, int delta_attrs) +{ + int ret = -1; + struct db_attr *new_attrs, *old_attrs; + struct db_trans *t; + int ti; + int max_attrs = ctx->max_attrs + delta_attrs; + int offset; + + old_attrs = ctx->attrs0; + new_attrs = ALLOC_BYTES_ST ( sizeof (struct db_attr) * max_attrs, + "db_context->attrs (expand)", db_attrs_st); + if (!new_attrs) + goto out; + + memcpy(new_attrs, old_attrs, ctx->max_attrs * sizeof(struct db_attr)); + + /* update attrs0 and attrs_cur (obviously) */ + offset = (char *)(new_attrs) - (char *)(old_attrs); + + { + char *actx = (char *)(ctx->attrs0); + + actx += offset; + ctx->attrs0 = (struct db_attr *)actx; + + actx = (char *)ctx->attrs_cur; + actx += offset; + ctx->attrs_cur = (struct db_attr *)actx; + } + + /* for each transform, rewrite attrs pointer by offsetting it */ + for (t=ctx->prop.trans, ti=0; ti < ctx->prop.trans_cnt; t++, ti++) { + char *actx = (char *)(t->attrs); + + actx += offset; + t->attrs = (struct db_attr *)actx; + } + /* update elem count */ + ctx->max_attrs = max_attrs; + PFREE_ST(old_attrs, db_attrs_st); + ret = 0; +out: + return ret; +} +/* Allocate a new db object */ +struct db_context * +db_prop_new(u_int8_t protoid, int max_trans, int max_attrs) +{ + struct db_context *ctx; + ctx = ALLOC_BYTES_ST ( sizeof (struct db_context), "db_context", db_context_st); + if (!ctx) goto out; + + if (db_prop_init(ctx, protoid, max_trans, max_attrs) < 0) { + PFREE_ST(ctx, db_context_st); + ctx=NULL; + } +out: + return ctx; +} +/* Free a db object */ +void +db_destroy(struct db_context *ctx) +{ + if (ctx->trans0) PFREE_ST(ctx->trans0, db_trans_st); + if (ctx->attrs0) PFREE_ST(ctx->attrs0, db_attrs_st); + PFREE_ST(ctx, db_context_st); +} +/* Start a new transform, expand trans0 is needed */ +int +db_trans_add(struct db_context *ctx, u_int8_t transid) +{ + /* skip incrementing current trans pointer the 1st time*/ + if (ctx->trans_cur && ctx->trans_cur->attr_cnt) + ctx->trans_cur++; + /* + * Strategy: if more space is needed, expand by + * /2 + 1 + * + * This happens to produce a "reasonable" sequence + * after few allocations, eg.: + * 0,1,2,4,8,13,20,31,47 + */ + if ((ctx->trans_cur - ctx->trans0) >= ctx->max_trans) { + /* XXX:jjo if fails should shout and flag it */ + if (db_trans_expand(ctx, ctx->max_trans/2 + 1)<0) + return -1; + } + ctx->trans_cur->transid = transid; + ctx->trans_cur->attrs=ctx->attrs_cur; + ctx->trans_cur->attr_cnt = 0; + ctx->prop.trans_cnt++; + return 0; +} +/* Add attr copy to current transform, expanding attrs0 if needed */ +int +db_attr_add(struct db_context *ctx, const struct db_attr *a) +{ + /* + * Strategy: if more space is needed, expand by + * /2 + 1 + */ + if ((ctx->attrs_cur - ctx->attrs0) >= ctx->max_attrs) { + /* XXX:jjo if fails should shout and flag it */ + if (db_attrs_expand(ctx, ctx->max_attrs/2 + 1) < 0) + return -1; + } + *ctx->attrs_cur++=*a; + ctx->trans_cur->attr_cnt++; + return 0; +} +/* Add attr copy (by value) to current transform, + * expanding attrs0 if needed, just calls db_attr_add(). + */ +int +db_attr_add_values(struct db_context *ctx, u_int16_t type, u_int16_t val) +{ + struct db_attr attr; + attr.type = type; + attr.val = val; + return db_attr_add (ctx, &attr); +} +#ifndef NO_DB_OPS_STATS +int +db_ops_show_status(void) +{ + whack_log(RC_COMMENT, "stats " __FILE__ ": " + DB_OPS_STATS_DESC " :" + DB_OPS_STATS_STR("context") + DB_OPS_STATS_STR("trans") + DB_OPS_STATS_STR("attrs"), + DB_OPS_STATS_F(db_context_st), + DB_OPS_STATS_F(db_trans_st), + DB_OPS_STATS_F(db_attrs_st) + ); + return 0; +} +#endif /* NO_DB_OPS_STATS */ +/* + * From below to end just testing stuff .... + */ +#ifdef TEST +static void db_prop_print(struct db_prop *p) +{ + struct db_trans *t; + struct db_attr *a; + int ti, ai; + enum_names *n, *n_at, *n_av; + printf("protoid=\"%s\"\n", enum_name(&protocol_names, p->protoid)); + for (ti=0, t=p->trans; ti< p->trans_cnt; ti++, t++) { + switch( t->transid) { + case PROTO_ISAKMP: + n=&isakmp_transformid_names;break; + case PROTO_IPSEC_ESP: + n=&esp_transformid_names;break; + default: + continue; + } + printf(" transid=\"%s\"\n", + enum_name(n, t->transid)); + for (ai=0, a=t->attrs; ai < t->attr_cnt; ai++, a++) { + int i; + switch( t->transid) { + case PROTO_ISAKMP: + n_at=&oakley_attr_names; + i=a->type|ISAKMP_ATTR_AF_TV; + n_av=oakley_attr_val_descs[(i)&ISAKMP_ATTR_RTYPE_MASK]; + break; + case PROTO_IPSEC_ESP: + n_at=&ipsec_attr_names; + i=a->type|ISAKMP_ATTR_AF_TV; + n_av=ipsec_attr_val_descs[(i)&ISAKMP_ATTR_RTYPE_MASK]; + break; + default: + continue; + } + printf(" type=\"%s\" value=\"%s\"\n", + enum_name(n_at, i), + enum_name(n_av, a->val)); + } + } + +} +static void db_print(struct db_context *ctx) +{ + printf("trans_cur diff=%d, attrs_cur diff=%d\n", + ctx->trans_cur - ctx->trans0, + ctx->attrs_cur - ctx->attrs0); + db_prop_print(&ctx->prop); +} + +void +passert_fail(const char *pred_str, const char *file_str, unsigned long line_no); +void abort(void); +void +passert_fail(const char *pred_str, const char *file_str, unsigned long line_no) +{ + fprintf(stderr, "ASSERTION FAILED at %s:%lu: %s", file_str, line_no, pred_str); + abort(); /* exiting correctly doesn't always work */ +} +int main(void) { + struct db_context *ctx=db_prop_new(PROTO_ISAKMP, 0, 0); + db_trans_add(ctx, KEY_IKE); + db_attr_add_values(ctx, OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC); + db_attr_add_values(ctx, OAKLEY_HASH_ALGORITHM, OAKLEY_MD5); + db_attr_add_values(ctx, OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG); + db_attr_add_values(ctx, OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024); + db_trans_add(ctx, KEY_IKE); + db_attr_add_values(ctx, OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_AES_CBC); + db_attr_add_values(ctx, OAKLEY_HASH_ALGORITHM, OAKLEY_MD5); + db_attr_add_values(ctx, OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY); + db_attr_add_values(ctx, OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536); + db_trans_add(ctx, ESP_3DES); + db_attr_add_values(ctx, AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_SHA1); + db_print(ctx); + db_destroy(ctx); + return 0; +} +#endif diff --git a/src/pluto/db_ops.h b/src/pluto/db_ops.h new file mode 100644 index 000000000..433e75280 --- /dev/null +++ b/src/pluto/db_ops.h @@ -0,0 +1,56 @@ +/* Dynamic db (proposal, transforms, attributes) handling. + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: db_ops.h,v 1.3 2004/09/17 12:37:37 as Exp $ + */ + +#ifndef _DB_OPS_H +#define _DB_OPS_H + +/* + * Main db object, (quite proposal "oriented") + */ +#ifndef NO_DB_CONTEXT +struct db_context { + struct db_prop prop; /* proposal buffer (not pointer) */ + struct db_trans *trans0; /* transf. list, dynamically sized */ + struct db_trans *trans_cur; /* current transform ptr */ + struct db_attr *attrs0; /* attr. list, dynamically sized */ + struct db_attr *attrs_cur; /* current attribute ptr */ + int max_trans; /* size of trans list */ + int max_attrs; /* size of attrs list */ +}; +/* + * Allocate a new db object + */ +struct db_context * db_prop_new(u_int8_t protoid, int max_trans, int max_attrs); +/* Initialize object for proposal building */ +int db_prop_init(struct db_context *ctx, u_int8_t protoid, int max_trans, int max_attrs); +/* Free all resourses for this db */ +void db_destroy(struct db_context *ctx); + +/* Start a new transform */ +int db_trans_add(struct db_context *ctx, u_int8_t transid); +/* Add a new attribute by copying db_attr content */ +int db_attr_add(struct db_context *db_ctx, const struct db_attr *attr); +/* Add a new attribute by value */ +int db_attr_add_values(struct db_context *ctx, u_int16_t type, u_int16_t val); + +/* Get proposal from db object */ +static __inline__ struct db_prop *db_prop_get(struct db_context *ctx) { + return &ctx->prop; +} +/* Show stats (allocation, etc) */ +#endif /* NO_DB_CONTEXT */ +int db_ops_show_status(void); +#endif /* _DB_OPS_H */ diff --git a/src/pluto/defs.c b/src/pluto/defs.c new file mode 100644 index 000000000..9ae32a480 --- /dev/null +++ b/src/pluto/defs.c @@ -0,0 +1,374 @@ +/* misc. universal things + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: defs.c,v 1.9 2006/01/04 21:00:43 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ + +const chunk_t empty_chunk = { NULL, 0 }; + +bool +all_zero(const unsigned char *m, size_t len) +{ + size_t i; + + for (i = 0; i != len; i++) + if (m[i] != '\0') + return FALSE; + return TRUE; +} + +/* memory allocation + * + * LEAK_DETECTIVE puts a wrapper around each allocation and maintains + * a list of live ones. If a dead one is freed, an assertion MIGHT fail. + * If the live list is currupted, that will often be detected. + * In the end, report_leaks() is called, and the names of remaining + * live allocations are printed. At the moment, it is hoped, not that + * the list is empty, but that there will be no surprises. + * + * Accepted Leaks: + * - "struct iface" and "device name" (for "discovered" net interfaces) + * - "struct event in event_schedule()" (events not associated with states) + * - "Pluto lock name" (one only, needed until end -- why bother?) + */ + +#ifdef LEAK_DETECTIVE + +/* this magic number is 3671129837 decimal (623837458 complemented) */ +#define LEAK_MAGIC 0xDAD0FEEDul + +union mhdr { + struct { + const char *name; + union mhdr *older, *newer; + unsigned long magic; + } i; /* info */ + unsigned long junk; /* force maximal alignment */ +}; + +static union mhdr *allocs = NULL; + +void *alloc_bytes(size_t size, const char *name) +{ + union mhdr *p = malloc(sizeof(union mhdr) + size); + + if (p == NULL) + exit_log("unable to malloc %lu bytes for %s" + , (unsigned long) size, name); + p->i.name = name; + p->i.older = allocs; + if (allocs != NULL) + allocs->i.newer = p; + allocs = p; + p->i.newer = NULL; + p->i.magic = LEAK_MAGIC; + + memset(p+1, '\0', size); + return p+1; +} + +void * +clone_bytes(const void *orig, size_t size, const char *name) +{ + void *p = alloc_bytes(size, name); + + memcpy(p, orig, size); + return p; +} + +void +pfree(void *ptr) +{ + union mhdr *p; + + passert(ptr != NULL); + p = ((union mhdr *)ptr) - 1; + passert(p->i.magic == LEAK_MAGIC); + if (p->i.older != NULL) + { + passert(p->i.older->i.newer == p); + p->i.older->i.newer = p->i.newer; + } + if (p->i.newer == NULL) + { + passert(p == allocs); + allocs = p->i.older; + } + else + { + passert(p->i.newer->i.older == p); + p->i.newer->i.older = p->i.older; + } + p->i.magic = ~LEAK_MAGIC; + free(p); +} + +void +report_leaks(void) +{ + union mhdr + *p = allocs, + *pprev = NULL; + unsigned long n = 0; + + while (p != NULL) + { + passert(p->i.magic == LEAK_MAGIC); + passert(pprev == p->i.newer); + pprev = p; + p = p->i.older; + n++; + if (p == NULL || pprev->i.name != p->i.name) + { + if (n != 1) + plog("leak: %lu * %s", n, pprev->i.name); + else + plog("leak: %s", pprev->i.name); + n = 0; + } + } +} + +#else /* !LEAK_DETECTIVE */ + +void *alloc_bytes(size_t size, const char *name) +{ + void *p = malloc(size); + + if (p == NULL) + exit_log("unable to malloc %lu bytes for %s" + , (unsigned long) size, name); + memset(p, '\0', size); + return p; +} + +void *clone_bytes(const void *orig, size_t size, const char *name) +{ + void *p = malloc(size); + + if (p == NULL) + exit_log("unable to malloc %lu bytes for %s" + , (unsigned long) size, name); + memcpy(p, orig, size); + return p; +} +#endif /* !LEAK_DETECTIVE */ + +/* Note that there may be as many as six IDs that are temporary at + * one time before unsharing the two ends of a connection. So we need + * at least six temporary buffers for DER_ASN1_DN IDs. + * We rotate them. Be careful! + */ +#define MAX_BUF 10 + +char* +temporary_cyclic_buffer(void) +{ + static char buf[MAX_BUF][BUF_LEN]; /* MAX_BUF internal buffers */ + static int counter = 0; /* cyclic counter */ + + if (++counter == MAX_BUF) counter = 0; /* next internal buffer */ + return buf[counter]; /* assign temporary buffer */ +} + +/* concatenates two sub paths into a string with a maximum size of BUF_LEN + * use for temporary storage only + */ +const char* +concatenate_paths(const char *a, const char *b) +{ + char *c; + + if (*b == '/' || *b == '.') + return b; + + c = temporary_cyclic_buffer(); + snprintf(c, BUF_LEN, "%s/%s", a, b); + return c; +} + +/* compare two chunks, returns zero if a equals b + * negative/positive if a is earlier/later in the alphabet than b + */ +int +cmp_chunk(chunk_t a, chunk_t b) +{ + int cmp_len, len, cmp_value; + + cmp_len = a.len - b.len; + len = (cmp_len < 0)? a.len : b.len; + cmp_value = memcmp(a.ptr, b.ptr, len); + + return (cmp_value == 0)? cmp_len : cmp_value; +}; + +/* moves a chunk to a memory position, chunk is freed afterwards + * position pointer is advanced after the insertion point + */ +void +mv_chunk(u_char **pos, chunk_t content) +{ + if (content.len > 0) + { + chunkcpy(*pos, content); + freeanychunk(content); + } +} + +/* + * write the binary contents of a chunk_t to a file + */ +bool +write_chunk(const char *filename, const char *label, chunk_t ch +, mode_t mask, bool force) +{ + mode_t oldmask; + FILE *fd; + + if (!force) + { + fd = fopen(filename, "r"); + if (fd) + { + fclose(fd); + plog(" %s file '%s' already exists", label, filename); + return FALSE; + } + } + + /* set umask */ + oldmask = umask(mask); + + fd = fopen(filename, "w"); + + if (fd) + { + fwrite(ch.ptr, sizeof(u_char), ch.len, fd); + fclose(fd); + plog(" written %s file '%s' (%d bytes)", label, filename, (int)ch.len); + umask(oldmask); + return TRUE; + } + else + { + plog(" could not open %s file '%s' for writing", label, filename); + umask(oldmask); + return FALSE; + } +} + +/* Names of the months */ + +static const char* months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + + +/* + * Display a date either in local or UTC time + */ +char* +timetoa(const time_t *time, bool utc) +{ + static char buf[TIMETOA_BUF]; + + if (*time == UNDEFINED_TIME) + sprintf(buf, "--- -- --:--:--%s----", (utc)?" UTC ":" "); + else + { + struct tm *t = (utc)? gmtime(time) : localtime(time); + + sprintf(buf, "%s %02d %02d:%02d:%02d%s%04d", + months[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, + (utc)?" UTC ":" ", t->tm_year + 1900 + ); + } + return buf; +} + +/* checks if the expiration date has been reached and + * warns during the warning_interval of the imminent + * expiry. strict=TRUE declares a fatal error, + * strict=FALSE issues a warning upon expiry. + */ +const char* +check_expiry(time_t expiration_date, int warning_interval, bool strict) +{ + time_t now; + int time_left; + + if (expiration_date == UNDEFINED_TIME) + return "ok (expires never)"; + + /* determine the current time */ + time(&now); + + time_left = (expiration_date - now); + if (time_left < 0) + return strict? "fatal (expired)" : "warning (expired)"; + + if (time_left > 86400*warning_interval) + return "ok"; + { + static char buf[35]; /* temporary storage */ + const char* unit = "second"; + + if (time_left > 172800) + { + time_left /= 86400; + unit = "day"; + } + else if (time_left > 7200) + { + time_left /= 3600; + unit = "hour"; + } + else if (time_left > 120) + { + time_left /= 60; + unit = "minute"; + } + snprintf(buf, 35, "warning (expires in %d %s%s)", time_left, + unit, (time_left == 1)?"":"s"); + return buf; + } +} + + +/* + * Filter eliminating the directory entries '.' and '..' + */ +int +file_select(const struct dirent *entry) +{ + return strcmp(entry->d_name, "." ) && + strcmp(entry->d_name, ".."); +} + + diff --git a/src/pluto/defs.h b/src/pluto/defs.h new file mode 100644 index 000000000..3bfb29a22 --- /dev/null +++ b/src/pluto/defs.h @@ -0,0 +1,145 @@ +/* misc. universal things + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: defs.h,v 1.10 2006/01/04 21:00:43 as Exp $ + */ + +#ifndef _DEFS_H +#define _DEFS_H + +#include + +#ifdef KLIPS +# define USED_BY_KLIPS /* ignore */ +#else +# define USED_BY_KLIPS UNUSED +#endif + +#ifdef DEBUG +# define USED_BY_DEBUG /* ignore */ +#else +# define USED_BY_DEBUG UNUSED +#endif + +/* Length of temporary buffers */ + +#define BUF_LEN 512 + +/* type of serial number of a state object + * Needed in connections.h and state.h; here to simplify dependencies. + */ +typedef unsigned long so_serial_t; +#define SOS_NOBODY 0 /* null serial number */ +#define SOS_FIRST 1 /* first normal serial number */ + +/* memory allocation */ + +extern void *alloc_bytes(size_t size, const char *name); +#define alloc_thing(thing, name) (alloc_bytes(sizeof(thing), (name))) + +extern void *clone_bytes(const void *orig, size_t size, const char *name); +#define clone_thing(orig, name) clone_bytes((const void *)&(orig), sizeof(orig), (name)) +#define clone_str(str, name) \ + ((str) == NULL? NULL : clone_bytes((str), strlen((str))+1, (name))) + +#ifdef LEAK_DETECTIVE + extern void pfree(void *ptr); + extern void report_leaks(void); +#else +# define pfree(ptr) free(ptr) /* ordinary stdc free */ +#endif +#define pfreeany(p) { if ((p) != NULL) pfree(p); } +#define replace(p, q) { pfreeany(p); (p) = (q); } + + +/* chunk is a simple pointer-and-size abstraction */ + +struct chunk { + u_char *ptr; + size_t len; + }; +typedef struct chunk chunk_t; + +#define setchunk(ch, addr, size) { (ch).ptr = (addr); (ch).len = (size); } +#define strchunk(str) { str, sizeof(str) } +/* NOTE: freeanychunk, unlike pfreeany, NULLs .ptr */ +#define freeanychunk(ch) { pfreeany((ch).ptr); (ch).ptr = NULL; } +#define clonetochunk(ch, addr, size, name) \ + { (ch).ptr = clone_bytes((addr), (ch).len = (size), name); } +#define clonereplacechunk(ch, addr, size, name) \ + { pfreeany((ch).ptr); clonetochunk(ch, addr, size, name); } +#define chunkcpy(dst, chunk) \ + { memcpy(dst, chunk.ptr, chunk.len); dst += chunk.len;} +#define same_chunk(a, b) \ + ( (a).len == (b).len && memcmp((a).ptr, (b).ptr, (b).len) == 0 ) + +extern char* temporary_cyclic_buffer(void); +extern const char* concatenate_paths(const char *a, const char *b); + +extern const chunk_t empty_chunk; + +/* compare two chunks */ +extern int cmp_chunk(chunk_t a, chunk_t b); + +/* move a chunk to a memory position and free it after insertion */ +extern void mv_chunk(u_char **pos, chunk_t content); + +/* write the binary contents of a chunk_t to a file */ +extern bool write_chunk(const char *filename, const char *label, chunk_t ch + ,mode_t mask, bool force); + +/* display a date either in local or UTC time */ +extern char* timetoa(const time_t *time, bool utc); + +/* warns a predefined interval before expiry */ +extern const char* check_expiry(time_t expiration_date, + int warning_interval, bool strict); + +#define MAX_PROMPT_PASS_TRIALS 5 +#define PROMPT_PASS_LEN 64 + +/* struct used to prompt for a secret passphrase + * from a console with file descriptor fd + */ +typedef struct { + char secret[PROMPT_PASS_LEN+1]; + bool prompt; + int fd; +} prompt_pass_t; + +/* no time defined in time_t */ +#define UNDEFINED_TIME 0 + +/* size of timetoa string buffer */ +#define TIMETOA_BUF 30 + +/* filter eliminating the directory entries '.' and '..' */ +typedef struct dirent dirent_t; +extern int file_select(const dirent_t *entry); + +/* cleanly exit Pluto */ + +extern void exit_pluto(int /*status*/) NEVER_RETURNS; + + +/* zero all bytes */ +#define zero(x) memset((x), '\0', sizeof(*(x))) + +/* are all bytes 0? */ +extern bool all_zero(const unsigned char *m, size_t len); + +/* pad_up(n, m) is the amount to add to n to make it a multiple of m */ +#define pad_up(n, m) (((m) - 1) - (((n) + (m) - 1) % (m))) + +#endif /* _DEFS_H */ diff --git a/src/pluto/demux.c b/src/pluto/demux.c new file mode 100644 index 000000000..7e59b184d --- /dev/null +++ b/src/pluto/demux.c @@ -0,0 +1,2499 @@ +/* demultiplex incoming IKE messages + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: demux.c,v 1.14 2006/06/22 11:58:25 as Exp $ + */ + +/* Ordering Constraints on Payloads + * + * rfc2409: The Internet Key Exchange (IKE) + * + * 5 Exchanges: + * "The SA payload MUST precede all other payloads in a phase 1 exchange." + * + * "Except where otherwise noted, there are no requirements for ISAKMP + * payloads in any message to be in any particular order." + * + * 5.3 Phase 1 Authenticated With a Revised Mode of Public Key Encryption: + * + * "If the HASH payload is sent it MUST be the first payload of the + * second message exchange and MUST be followed by the encrypted + * nonce. If the HASH payload is not sent, the first payload of the + * second message exchange MUST be the encrypted nonce." + * + * "Save the requirements on the location of the optional HASH payload + * and the mandatory nonce payload there are no further payload + * requirements. All payloads-- in whatever order-- following the + * encrypted nonce MUST be encrypted with Ke_i or Ke_r depending on the + * direction." + * + * 5.5 Phase 2 - Quick Mode + * + * "In Quick Mode, a HASH payload MUST immediately follow the ISAKMP + * header and a SA payload MUST immediately follow the HASH." + * [NOTE: there may be more than one SA payload, so this is not + * totally reasonable. Probably all SAs should be so constrained.] + * + * "If ISAKMP is acting as a client negotiator on behalf of another + * party, the identities of the parties MUST be passed as IDci and + * then IDcr." + * + * "With the exception of the HASH, SA, and the optional ID payloads, + * there are no payload ordering restrictions on Quick Mode." + */ + +/* Unfolding of Identity -- a central mystery + * + * This concerns Phase 1 identities, those of the IKE hosts. + * These are the only ones that are authenticated. Phase 2 + * identities are for IPsec SAs. + * + * There are three case of interest: + * + * (1) We initiate, based on a whack command specifying a Connection. + * We know the identity of the peer from the Connection. + * + * (2) (to be implemented) we initiate based on a flow from our client + * to some IP address. + * We immediately know one of the peer's client IP addresses from + * the flow. We must use this to figure out the peer's IP address + * and Id. To be solved. + * + * (3) We respond to an IKE negotiation. + * We immediately know the peer's IP address. + * We get an ID Payload in Main I2. + * + * Unfortunately, this is too late for a number of things: + * - the ISAKMP SA proposals have already been made (Main I1) + * AND one accepted (Main R1) + * - the SA includes a specification of the type of ID + * authentication so this is negotiated without being told the ID. + * - with Preshared Key authentication, Main I2 is encrypted + * using the key, so it cannot be decoded to reveal the ID + * without knowing (or guessing) which key to use. + * + * There are three reasonable choices here for the responder: + * + assume that the initiator is making wise offers since it + * knows the IDs involved. We can balk later (but not gracefully) + * when we find the actual initiator ID + * + attempt to infer identity by IP address. Again, we can balk + * when the true identity is revealed. Actually, it is enough + * to infer properties of the identity (eg. SA properties and + * PSK, if needed). + * + make all properties universal so discrimination based on + * identity isn't required. For example, always accept the same + * kinds of encryption. Accept Public Key Id authentication + * since the Initiator presumably has our public key and thinks + * we must have / can find his. This approach is weakest + * for preshared key since the actual key must be known to + * decrypt the Initiator's ID Payload. + * These choices can be blended. For example, a class of Identities + * can be inferred, sufficient to select a preshared key but not + * sufficient to infer a unique identity. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* only used for belt-and-suspenders select call */ +#include /* only used for forensic poll call */ +#include +#include +#include +#include +#include + +#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE) +# include /* for __u8, __u32 */ +# include +# include /* struct iovec */ +#endif + +#include + +#include "constants.h" +#include "defs.h" +#include "cookie.h" +#include "connections.h" +#include "state.h" +#include "packet.h" +#include "md5.h" +#include "sha1.h" +#include "crypto.h" /* requires sha1.h and md5.h */ +#include "ike_alg.h" +#include "log.h" +#include "demux.h" /* needs packet.h */ +#include "ipsec_doi.h" /* needs demux.h and state.h */ +#include "timer.h" +#include "whack.h" /* requires connections.h */ +#include "server.h" +#include "nat_traversal.h" +#include "vendor.h" +#include "modecfg.h" + +/* This file does basic header checking and demux of + * incoming packets. + */ + +/* forward declarations */ +static bool read_packet(struct msg_digest *md); +static void process_packet(struct msg_digest **mdp); + +/* Reply messages are built in this buffer. + * Only one state transition function can be using it at a time + * so suspended STFs must save and restore it. + * It could be an auto variable of complete_state_transition except for the fact + * that when a suspended STF resumes, its reply message buffer + * must be at the same location -- there are pointers into it. + */ +u_int8_t reply_buffer[MAX_OUTPUT_UDP_SIZE]; + +/* state_microcode is a tuple of information parameterizing certain + * centralized processing of a packet. For example, it roughly + * specifies what payloads are expected in this message. + * The microcode is selected primarily based on the state. + * In Phase 1, the payload structure often depends on the + * authentication technique, so that too plays a part in selecting + * the state_microcode to use. + */ + +struct state_microcode { + enum state_kind state, next_state; + lset_t flags; + lset_t req_payloads; /* required payloads (allows just one) */ + lset_t opt_payloads; /* optional payloads (any mumber) */ + /* if not ISAKMP_NEXT_NONE, process_packet will emit HDR with this as np */ + u_int8_t first_out_payload; + enum event_type timeout_event; + state_transition_fn *processor; +}; + +/* State Microcode Flags, in several groups */ + +/* Oakley Auth values: to which auth values does this entry apply? + * Most entries will use SMF_ALL_AUTH because they apply to all. + * Note: SMF_ALL_AUTH matches 0 for those circumstances when no auth + * has been set. + */ +#define SMF_ALL_AUTH LRANGE(0, OAKLEY_AUTH_ROOF-1) +#define SMF_PSK_AUTH LELEM(OAKLEY_PRESHARED_KEY) +#define SMF_DS_AUTH (LELEM(OAKLEY_DSS_SIG) | LELEM(OAKLEY_RSA_SIG)) +#define SMF_PKE_AUTH (LELEM(OAKLEY_RSA_ENC) | LELEM(OAKLEY_ELGAMAL_ENC)) +#define SMF_RPKE_AUTH (LELEM(OAKLEY_RSA_ENC_REV) | LELEM(OAKLEY_ELGAMAL_ENC_REV)) + +/* misc flags */ + +#define SMF_INITIATOR LELEM(OAKLEY_AUTH_ROOF + 0) +#define SMF_FIRST_ENCRYPTED_INPUT LELEM(OAKLEY_AUTH_ROOF + 1) +#define SMF_INPUT_ENCRYPTED LELEM(OAKLEY_AUTH_ROOF + 2) +#define SMF_OUTPUT_ENCRYPTED LELEM(OAKLEY_AUTH_ROOF + 3) +#define SMF_RETRANSMIT_ON_DUPLICATE LELEM(OAKLEY_AUTH_ROOF + 4) + +#define SMF_ENCRYPTED (SMF_INPUT_ENCRYPTED | SMF_OUTPUT_ENCRYPTED) + +/* this state generates a reply message */ +#define SMF_REPLY LELEM(OAKLEY_AUTH_ROOF + 5) + +/* this state completes P1, so any pending P2 negotiations should start */ +#define SMF_RELEASE_PENDING_P2 LELEM(OAKLEY_AUTH_ROOF + 6) + +/* end of flags */ + + +static state_transition_fn /* forward declaration */ + unexpected, + informational; + +/* state_microcode_table is a table of all state_microcode tuples. + * It must be in order of state (the first element). + * After initialization, ike_microcode_index[s] points to the + * first entry in state_microcode_table for state s. + * Remember that each state name in Main or Quick Mode describes + * what has happened in the past, not what this message is. + */ + +static const struct state_microcode + *ike_microcode_index[STATE_IKE_ROOF - STATE_IKE_FLOOR]; + +static const struct state_microcode state_microcode_table[] = { +#define PT(n) ISAKMP_NEXT_##n +#define P(n) LELEM(PT(n)) + + /***** Phase 1 Main Mode *****/ + + /* No state for main_outI1: --> HDR, SA */ + + /* STATE_MAIN_R0: I1 --> R1 + * HDR, SA --> HDR, SA + */ + { STATE_MAIN_R0, STATE_MAIN_R1 + , SMF_ALL_AUTH | SMF_REPLY + , P(SA), P(VID) | P(CR), PT(NONE) + , EVENT_RETRANSMIT, main_inI1_outR1}, + + /* STATE_MAIN_I1: R1 --> I2 + * HDR, SA --> auth dependent + * SMF_PSK_AUTH, SMF_DS_AUTH: --> HDR, KE, Ni + * SMF_PKE_AUTH: + * --> HDR, KE, [ HASH(1), ] PubKey_r, PubKey_r + * SMF_RPKE_AUTH: + * --> HDR, [ HASH(1), ] Pubkey_r, Ke_i, Ke_i [,<Ke_i] + * Note: since we don't know auth at start, we cannot differentiate + * microcode entries based on it. + */ + { STATE_MAIN_I1, STATE_MAIN_I2 + , SMF_ALL_AUTH | SMF_INITIATOR | SMF_REPLY + , P(SA), P(VID) | P(CR), PT(NONE) /* don't know yet */ + , EVENT_RETRANSMIT, main_inR1_outI2 }, + + /* STATE_MAIN_R1: I2 --> R2 + * SMF_PSK_AUTH, SMF_DS_AUTH: HDR, KE, Ni --> HDR, KE, Nr + * SMF_PKE_AUTH: HDR, KE, [ HASH(1), ] PubKey_r, PubKey_r + * --> HDR, KE, PubKey_i, PubKey_i + * SMF_RPKE_AUTH: + * HDR, [ HASH(1), ] Pubkey_r, Ke_i, Ke_i [,<Ke_i] + * --> HDR, PubKey_i, Ke_r, Ke_r + */ + { STATE_MAIN_R1, STATE_MAIN_R2 + , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_REPLY + , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(KE) + , EVENT_RETRANSMIT, main_inI2_outR2 }, + + { STATE_MAIN_R1, STATE_UNDEFINED + , SMF_PKE_AUTH | SMF_REPLY + , P(KE) | P(ID) | P(NONCE), P(VID) | P(CR) | P(HASH), PT(KE) + , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, + + { STATE_MAIN_R1, STATE_UNDEFINED + , SMF_RPKE_AUTH | SMF_REPLY + , P(NONCE) | P(KE) | P(ID), P(VID) | P(CR) | P(HASH) | P(CERT), PT(NONCE) + , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, + + /* for states from here on, output message must be encrypted */ + + /* STATE_MAIN_I2: R2 --> I3 + * SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I + * SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I + * SMF_PKE_AUTH: HDR, KE, PubKey_i, PubKey_i + * --> HDR*, HASH_I + * SMF_RPKE_AUTH: HDR, PubKey_i, Ke_r, Ke_r + * --> HDR*, HASH_I + */ + { STATE_MAIN_I2, STATE_MAIN_I3 + , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY + , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(ID) + , EVENT_RETRANSMIT, main_inR2_outI3 }, + + { STATE_MAIN_I2, STATE_UNDEFINED + , SMF_PKE_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY + , P(KE) | P(ID) | P(NONCE), P(VID) | P(CR), PT(HASH) + , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, + + { STATE_MAIN_I2, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY + , P(NONCE) | P(KE) | P(ID), P(VID) | P(CR), PT(HASH) + , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, + + /* for states from here on, input message must be encrypted */ + + /* STATE_MAIN_R2: I3 --> R3 + * SMF_PSK_AUTH: HDR*, IDi1, HASH_I --> HDR*, IDr1, HASH_R + * SMF_DS_AUTH: HDR*, IDi1, [ CERT, ] SIG_I --> HDR*, IDr1, [ CERT, ] SIG_R + * SMF_PKE_AUTH, SMF_RPKE_AUTH: HDR*, HASH_I --> HDR*, HASH_R + */ + { STATE_MAIN_R2, STATE_MAIN_R3 + , SMF_PSK_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED + | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(ID) | P(HASH), P(VID) | P(CR), PT(NONE) + , EVENT_SA_REPLACE, main_inI3_outR3 }, + + { STATE_MAIN_R2, STATE_MAIN_R3 + , SMF_DS_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED + | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(ID) | P(SIG), P(VID) | P(CR) | P(CERT), PT(NONE) + , EVENT_SA_REPLACE, main_inI3_outR3 }, + + { STATE_MAIN_R2, STATE_UNDEFINED + , SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED + | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(HASH), P(VID) | P(CR), PT(NONE) + , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ }, + + /* STATE_MAIN_I3: R3 --> done + * SMF_PSK_AUTH: HDR*, IDr1, HASH_R --> done + * SMF_DS_AUTH: HDR*, IDr1, [ CERT, ] SIG_R --> done + * SMF_PKE_AUTH, SMF_RPKE_AUTH: HDR*, HASH_R --> done + * May initiate quick mode by calling quick_outI1 + */ + { STATE_MAIN_I3, STATE_MAIN_I4 + , SMF_PSK_AUTH | SMF_INITIATOR + | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 + , P(ID) | P(HASH), P(VID) | P(CR), PT(NONE) + , EVENT_SA_REPLACE, main_inR3 }, + + { STATE_MAIN_I3, STATE_MAIN_I4 + , SMF_DS_AUTH | SMF_INITIATOR + | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 + , P(ID) | P(SIG), P(VID) | P(CR) | P(CERT), PT(NONE) + , EVENT_SA_REPLACE, main_inR3 }, + + { STATE_MAIN_I3, STATE_UNDEFINED + , SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_INITIATOR + | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 + , P(HASH), P(VID) | P(CR), PT(NONE) + , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ }, + + /* STATE_MAIN_R3: can only get here due to packet loss */ + { STATE_MAIN_R3, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE + , LEMPTY, LEMPTY + , PT(NONE), EVENT_NULL, unexpected }, + + /* STATE_MAIN_I4: can only get here due to packet loss */ + { STATE_MAIN_I4, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED + , LEMPTY, LEMPTY + , PT(NONE), EVENT_NULL, unexpected }, + + + /***** Phase 2 Quick Mode *****/ + + /* No state for quick_outI1: + * --> HDR*, HASH(1), SA, Nr [, KE ] [, IDci, IDcr ] + */ + + /* STATE_QUICK_R0: + * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] --> + * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] + * Installs inbound IPsec SAs. + * Because it may suspend for asynchronous DNS, first_out_payload + * is set to NONE to suppress early emission of HDR*. + * ??? it is legal to have multiple SAs, but we don't support it yet. + */ + { STATE_QUICK_R0, STATE_QUICK_R1 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY + , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(NONE) + , EVENT_RETRANSMIT, quick_inI1_outR1 }, + + /* STATE_QUICK_I1: + * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] --> + * HDR*, HASH(3) + * Installs inbound and outbound IPsec SAs, routing, etc. + * ??? it is legal to have multiple SAs, but we don't support it yet. + */ + { STATE_QUICK_I1, STATE_QUICK_I2 + , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_REPLY + , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(HASH) + , EVENT_SA_REPLACE, quick_inR1_outI2 }, + + /* STATE_QUICK_R1: HDR*, HASH(3) --> done + * Installs outbound IPsec SAs, routing, etc. + */ + { STATE_QUICK_R1, STATE_QUICK_R2 + , SMF_ALL_AUTH | SMF_ENCRYPTED + , P(HASH), LEMPTY, PT(NONE) + , EVENT_SA_REPLACE, quick_inI2 }, + + /* STATE_QUICK_I2: can only happen due to lost packet */ + { STATE_QUICK_I2, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + /* STATE_QUICK_R2: can only happen due to lost packet */ + { STATE_QUICK_R2, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + + /***** informational messages *****/ + + /* STATE_INFO: */ + { STATE_INFO, STATE_UNDEFINED + , SMF_ALL_AUTH + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, informational }, + + /* STATE_INFO_PROTECTED: */ + { STATE_INFO_PROTECTED, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , P(HASH), LEMPTY, PT(NONE) + , EVENT_NULL, informational }, + + /* XAUTH state transitions */ + { STATE_XAUTH_I0, STATE_XAUTH_I1 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_RETRANSMIT, xauth_inI0 }, + + { STATE_XAUTH_R1, STATE_XAUTH_R2 + , SMF_ALL_AUTH | SMF_ENCRYPTED + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_RETRANSMIT, xauth_inR1 }, + + { STATE_XAUTH_I1, STATE_XAUTH_I2 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, xauth_inI1 }, + + { STATE_XAUTH_R2, STATE_XAUTH_R3 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(NONE) + , EVENT_SA_REPLACE, xauth_inR2 }, + + { STATE_XAUTH_I2, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + { STATE_XAUTH_R3, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + /* ModeCfg pull mode state transitions */ + + { STATE_MODE_CFG_R0, STATE_MODE_CFG_R1 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inR0 }, + + { STATE_MODE_CFG_I1, STATE_MODE_CFG_I2 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inI1 }, + + { STATE_MODE_CFG_R1, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + { STATE_MODE_CFG_I2, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + /* ModeCfg push mode state transitions */ + + { STATE_MODE_CFG_I0, STATE_MODE_CFG_I3 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inI0 }, + + { STATE_MODE_CFG_R3, STATE_MODE_CFG_R4 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inR3 }, + + { STATE_MODE_CFG_I3, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + { STATE_MODE_CFG_R4, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + +#undef P +#undef PT +}; + +void +init_demux(void) +{ + /* fill ike_microcode_index: + * make ike_microcode_index[s] point to first entry in + * state_microcode_table for state s (backward scan makes this easier). + * Check that table is in order -- catch coding errors. + * For what it's worth, this routine is idempotent. + */ + const struct state_microcode *t; + + for (t = &state_microcode_table[elemsof(state_microcode_table) - 1];;) + { + passert(STATE_IKE_FLOOR <= t->state && t->state < STATE_IKE_ROOF); + ike_microcode_index[t->state - STATE_IKE_FLOOR] = t; + if (t == state_microcode_table) + break; + t--; + passert(t[0].state <= t[1].state); + } +} + +/* Process any message on the MSG_ERRQUEUE + * + * This information is generated because of the IP_RECVERR socket option. + * The API is sparsely documented, and may be LINUX-only, and only on + * fairly recent versions at that (hence the conditional compilation). + * + * - ip(7) describes IP_RECVERR + * - recvmsg(2) describes MSG_ERRQUEUE + * - readv(2) describes iovec + * - cmsg(3) describes how to process auxilliary messages + * + * ??? we should link this message with one we've sent + * so that the diagnostic can refer to that negotiation. + * + * ??? how long can the messge be? + * + * ??? poll(2) has a very incomplete description of the POLL* events. + * We assume that POLLIN, POLLOUT, and POLLERR are all we need to deal with + * and that POLLERR will be on iff there is a MSG_ERRQUEUE message. + * + * We have to code around a couple of surprises: + * + * - Select can say that a socket is ready to read from, and + * yet a read will hang. It turns out that a message available on the + * MSG_ERRQUEUE will cause select to say something is pending, but + * a normal read will hang. poll(2) can tell when a MSG_ERRQUEUE + * message is pending. + * + * This is dealt with by calling check_msg_errqueue after select + * has indicated that there is something to read, but before the + * read is performed. check_msg_errqueue will return TRUE if there + * is something left to read. + * + * - A write to a socket may fail because there is a pending MSG_ERRQUEUE + * message, without there being anything wrong with the write. This + * makes for confusing diagnostics. + * + * To avoid this, we call check_msg_errqueue before a write. True, + * there is a race condition (a MSG_ERRQUEUE message might arrive + * between the check and the write), but we should eliminate many + * of the problematic events. To narrow the window, the poll(2) + * will await until an event happens (in the case or a write, + * POLLOUT; this should be benign for POLLIN). + */ + +#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE) +static bool +check_msg_errqueue(const struct iface *ifp, short interest) +{ + struct pollfd pfd; + + pfd.fd = ifp->fd; + pfd.events = interest | POLLPRI | POLLOUT; + + while (pfd.revents = 0 + , poll(&pfd, 1, -1) > 0 && (pfd.revents & POLLERR)) + { + u_int8_t buffer[3000]; /* hope that this is big enough */ + union + { + struct sockaddr sa; + struct sockaddr_in sa_in4; + struct sockaddr_in6 sa_in6; + } from; + + int from_len = sizeof(from); + + int packet_len; + + struct msghdr emh; + struct iovec eiov; + union { + /* force alignment (not documented as necessary) */ + struct cmsghdr ecms; + + /* how much space is enough? */ + unsigned char space[256]; + } ecms_buf; + + struct cmsghdr *cm; + char fromstr[sizeof(" for message to port 65536") + INET6_ADDRSTRLEN]; + struct state *sender = NULL; + + zero(&from.sa); + from_len = sizeof(from); + + emh.msg_name = &from.sa; /* ??? filled in? */ + emh.msg_namelen = sizeof(from); + emh.msg_iov = &eiov; + emh.msg_iovlen = 1; + emh.msg_control = &ecms_buf; + emh.msg_controllen = sizeof(ecms_buf); + emh.msg_flags = 0; + + eiov.iov_base = buffer; /* see readv(2) */ + eiov.iov_len = sizeof(buffer); + + packet_len = recvmsg(ifp->fd, &emh, MSG_ERRQUEUE); + + if (packet_len == -1) + { + log_errno((e, "recvmsg(,, MSG_ERRQUEUE) on %s failed in comm_handle" + , ifp->rname)); + break; + } + else if (packet_len == sizeof(buffer)) + { + plog("MSG_ERRQUEUE message longer than %lu bytes; truncated" + , (unsigned long) sizeof(buffer)); + } + else + { + sender = find_sender((size_t) packet_len, buffer); + } + + DBG_cond_dump(DBG_ALL, "rejected packet:\n", buffer, packet_len); + DBG_cond_dump(DBG_ALL, "control:\n", emh.msg_control, emh.msg_controllen); + /* ??? Andi Kleen and misc documentation + * suggests that name will have the original destination + * of the packet. We seem to see msg_namelen == 0. + * Andi says that this is a kernel bug and has fixed it. + * Perhaps in 2.2.18/2.4.0. + */ + passert(emh.msg_name == &from.sa); + DBG_cond_dump(DBG_ALL, "name:\n", emh.msg_name + , emh.msg_namelen); + + fromstr[0] = '\0'; /* usual case :-( */ + switch (from.sa.sa_family) + { + char as[INET6_ADDRSTRLEN]; + + case AF_INET: + if (emh.msg_namelen == sizeof(struct sockaddr_in)) + snprintf(fromstr, sizeof(fromstr) + , " for message to %s port %u" + , inet_ntop(from.sa.sa_family + , &from.sa_in4.sin_addr, as, sizeof(as)) + , ntohs(from.sa_in4.sin_port)); + break; + case AF_INET6: + if (emh.msg_namelen == sizeof(struct sockaddr_in6)) + snprintf(fromstr, sizeof(fromstr) + , " for message to %s port %u" + , inet_ntop(from.sa.sa_family + , &from.sa_in6.sin6_addr, as, sizeof(as)) + , ntohs(from.sa_in6.sin6_port)); + break; + } + + for (cm = CMSG_FIRSTHDR(&emh) + ; cm != NULL + ; cm = CMSG_NXTHDR(&emh,cm)) + { + if (cm->cmsg_level == SOL_IP + && cm->cmsg_type == IP_RECVERR) + { + /* ip(7) and recvmsg(2) specify: + * ee_origin is SO_EE_ORIGIN_ICMP for ICMP + * or SO_EE_ORIGIN_LOCAL for locally generated errors. + * ee_type and ee_code are from the ICMP header. + * ee_info is the discovered MTU for EMSGSIZE errors + * ee_data is not used. + * + * ??? recvmsg(2) says "SOCK_EE_OFFENDER" but + * means "SO_EE_OFFENDER". The OFFENDER is really + * the router that complained. As such, the port + * is meaningless. + */ + + /* ??? cmsg(3) claims that CMSG_DATA returns + * void *, but RFC 2292 and /usr/include/bits/socket.h + * say unsigned char *. The manual is being fixed. + */ + struct sock_extended_err *ee = (void *)CMSG_DATA(cm); + const char *offstr = "unspecified"; + char offstrspace[INET6_ADDRSTRLEN]; + char orname[50]; + + if (cm->cmsg_len > CMSG_LEN(sizeof(struct sock_extended_err))) + { + const struct sockaddr *offender = SO_EE_OFFENDER(ee); + + switch (offender->sa_family) + { + case AF_INET: + offstr = inet_ntop(offender->sa_family + , &((const struct sockaddr_in *)offender)->sin_addr + , offstrspace, sizeof(offstrspace)); + break; + case AF_INET6: + offstr = inet_ntop(offender->sa_family + , &((const struct sockaddr_in6 *)offender)->sin6_addr + , offstrspace, sizeof(offstrspace)); + break; + default: + offstr = "unknown"; + break; + } + } + + switch (ee->ee_origin) + { + case SO_EE_ORIGIN_NONE: + snprintf(orname, sizeof(orname), "none"); + break; + case SO_EE_ORIGIN_LOCAL: + snprintf(orname, sizeof(orname), "local"); + break; + case SO_EE_ORIGIN_ICMP: + snprintf(orname, sizeof(orname) + , "ICMP type %d code %d (not authenticated)" + , ee->ee_type, ee->ee_code + ); + break; + case SO_EE_ORIGIN_ICMP6: + snprintf(orname, sizeof(orname) + , "ICMP6 type %d code %d (not authenticated)" + , ee->ee_type, ee->ee_code + ); + break; + default: + snprintf(orname, sizeof(orname), "invalid origin %lu" + , (unsigned long) ee->ee_origin); + break; + } + + { + struct state *old_state = cur_state; + + cur_state = sender; + + /* note dirty trick to suppress ~ at start of format + * if we know what state to blame. + */ + if ((packet_len == 1) && (buffer[0] = 0xff) +#ifdef DEBUG + && ((cur_debugging & DBG_NATT) == 0) +#endif + ) { + /* don't log NAT-T keepalive related errors unless NATT debug is + * enabled + */ + } + else + plog((sender != NULL) + "~" + "ERROR: asynchronous network error report on %s" + "%s" + ", complainant %s" + ": %s" + " [errno %lu, origin %s" + /* ", pad %d, info %ld" */ + /* ", data %ld" */ + "]" + , ifp->rname + , fromstr + , offstr + , strerror(ee->ee_errno) + , (unsigned long) ee->ee_errno + , orname + /* , ee->ee_pad, (unsigned long)ee->ee_info */ + /* , (unsigned long)ee->ee_data */ + ); + cur_state = old_state; + } + } + else + { + /* .cmsg_len is a kernel_size_t(!), but the value + * certainly ought to fit in an unsigned long. + */ + plog("unknown cmsg: level %d, type %d, len %lu" + , cm->cmsg_level, cm->cmsg_type + , (unsigned long) cm->cmsg_len); + } + } + } + return (pfd.revents & interest) != 0; +} +#endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */ + +bool +send_packet(struct state *st, const char *where) +{ + struct connection *c = st->st_connection; + int port_buf; + bool err; + u_int8_t ike_pkt[MAX_OUTPUT_UDP_SIZE]; + u_int8_t *ptr; + unsigned long len; + + if (c->interface->ike_float && st->st_tpacket.len != 1) + { + if ((unsigned long) st->st_tpacket.len > (MAX_OUTPUT_UDP_SIZE-sizeof(u_int32_t))) + { + DBG_log("send_packet(): really too big"); + return FALSE; + } + ptr = ike_pkt; + /** Add Non-ESP marker **/ + memset(ike_pkt, 0, sizeof(u_int32_t)); + memcpy(ike_pkt + sizeof(u_int32_t), st->st_tpacket.ptr, + (unsigned long)st->st_tpacket.len); + len = (unsigned long) st->st_tpacket.len + sizeof(u_int32_t); + } + else + { + ptr = st->st_tpacket.ptr; + len = (unsigned long) st->st_tpacket.len; + } + + DBG(DBG_RAW, + { + DBG_log("sending %lu bytes for %s through %s to %s:%u:" + , (unsigned long) st->st_tpacket.len + , where + , c->interface->rname + , ip_str(&c->spd.that.host_addr) + , (unsigned)c->spd.that.host_port); + DBG_dump_chunk(NULL, st->st_tpacket); + }); + + /* XXX: Not very clean. We manipulate the port of the ip_address to + * have a port in the sockaddr*, but we retain the original port + * and restore it afterwards. + */ + + port_buf = portof(&c->spd.that.host_addr); + setportof(htons(c->spd.that.host_port), &c->spd.that.host_addr); + +#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE) + (void) check_msg_errqueue(c->interface, POLLOUT); +#endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */ + + err = sendto(c->interface->fd + , ptr, len, 0 + , sockaddrof(&c->spd.that.host_addr) + , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)len; + + /* restore port */ + setportof(port_buf, &c->spd.that.host_addr); + + if (err) + { + /* do not log NAT-T Keep Alive packets */ + if (streq(where, "NAT-T Keep Alive")) + return FALSE; + log_errno((e, "sendto on %s to %s:%u failed in %s" + , c->interface->rname + , ip_str(&c->spd.that.host_addr) + , (unsigned)c->spd.that.host_port + , where)); + return FALSE; + } + else + { + return TRUE; + } +} + +static stf_status +unexpected(struct msg_digest *md) +{ + loglog(RC_LOG_SERIOUS, "unexpected message received in state %s" + , enum_name(&state_names, md->st->st_state)); + return STF_IGNORE; +} + +static stf_status +informational(struct msg_digest *md UNUSED) +{ + struct payload_digest *const n_pld = md->chain[ISAKMP_NEXT_N]; + + /* If the Notification Payload is not null... */ + if (n_pld != NULL) + { + pb_stream *const n_pbs = &n_pld->pbs; + struct isakmp_notification *const n = &n_pld->payload.notification; + int disp_len; + char disp_buf[200]; + + /* Switch on Notification Type (enum) */ + switch (n->isan_type) + { + case R_U_THERE: + return dpd_inI_outR(md->st, n, n_pbs); + + case R_U_THERE_ACK: + return dpd_inR(md->st, n, n_pbs); + default: + if (pbs_left(n_pbs) >= sizeof(disp_buf)-1) + disp_len = sizeof(disp_buf)-1; + else + disp_len = pbs_left(n_pbs); + memcpy(disp_buf, n_pbs->cur, disp_len); + disp_buf[disp_len] = '\0'; + break; + } + } + return STF_IGNORE; +} + +/* message digest allocation and deallocation */ + +static struct msg_digest *md_pool = NULL; + +/* free_md_pool is only used to avoid leak reports */ +void +free_md_pool(void) +{ + for (;;) + { + struct msg_digest *md = md_pool; + + if (md == NULL) + break; + md_pool = md->next; + pfree(md); + } +} + +static struct msg_digest * +alloc_md(void) +{ + struct msg_digest *md = md_pool; + + /* convenient initializer: + * - all pointers NULL + * - .note = NOTHING_WRONG + * - .encrypted = FALSE + */ + static const struct msg_digest blank_md; + + if (md == NULL) + md = alloc_thing(struct msg_digest, "msg_digest"); + else + md_pool = md->next; + + *md = blank_md; + md->digest_roof = md->digest; + + /* note: although there may be multiple msg_digests at once + * (due to suspended state transitions), there is a single + * global reply_buffer. It will need to be saved and restored. + */ + init_pbs(&md->reply, reply_buffer, sizeof(reply_buffer), "reply packet"); + + return md; +} + +void +release_md(struct msg_digest *md) +{ + freeanychunk(md->raw_packet); + pfreeany(md->packet_pbs.start); + md->packet_pbs.start = NULL; + md->next = md_pool; + md_pool = md; +} + +/* wrapper for read_packet and process_packet + * + * The main purpose of this wrapper is to factor out teardown code + * from the many return points in process_packet. This amounts to + * releasing the msg_digest and resetting global variables. + * + * When processing of a packet is suspended (STF_SUSPEND), + * process_packet sets md to NULL to prevent the msg_digest being freed. + * Someone else must ensure that msg_digest is freed eventually. + * + * read_packet is broken out to minimize the lifetime of the + * enormous input packet buffer, an auto. + */ +void +comm_handle(const struct iface *ifp) +{ + static struct msg_digest *md; + +#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE) + /* Even though select(2) says that there is a message, + * it might only be a MSG_ERRQUEUE message. At least + * sometimes that leads to a hanging recvfrom. To avoid + * what appears to be a kernel bug, check_msg_errqueue + * uses poll(2) and tells us if there is anything for us + * to read. + * + * This is early enough that teardown isn't required: + * just return on failure. + */ + if (!check_msg_errqueue(ifp, POLLIN)) + return; /* no normal message to read */ +#endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */ + + md = alloc_md(); + md->iface = ifp; + + if (read_packet(md)) + process_packet(&md); + + if (md != NULL) + release_md(md); + + cur_state = NULL; + reset_cur_connection(); + cur_from = NULL; +} + +/* read the message. + * Since we don't know its size, we read it into + * an overly large buffer and then copy it to a + * new, properly sized buffer. + */ +static bool +read_packet(struct msg_digest *md) +{ + const struct iface *ifp = md->iface; + int packet_len; + u_int8_t *buffer; + u_int8_t *buffer_nat; + union + { + struct sockaddr sa; + struct sockaddr_in sa_in4; + struct sockaddr_in6 sa_in6; + } from; + int from_len = sizeof(from); + err_t from_ugh = NULL; + static const char undisclosed[] = "unknown source"; + + happy(anyaddr(addrtypeof(&ifp->addr), &md->sender)); + zero(&from.sa); + ioctl(ifp->fd, FIONREAD, &packet_len); + buffer = alloc_bytes(packet_len, "buffer read packet"); + packet_len = recvfrom(ifp->fd, buffer, packet_len, 0 + , &from.sa, &from_len); + + /* First: digest the from address. + * We presume that nothing here disturbs errno. + */ + if (packet_len == -1 + && from_len == sizeof(from) + && all_zero((const void *)&from.sa, sizeof(from))) + { + /* "from" is untouched -- not set by recvfrom */ + from_ugh = undisclosed; + } + else if (from_len + < (int) (offsetof(struct sockaddr, sa_family) + sizeof(from.sa.sa_family))) + { + from_ugh = "truncated"; + } + else + { + const struct af_info *afi = aftoinfo(from.sa.sa_family); + + if (afi == NULL) + { + from_ugh = "unexpected Address Family"; + } + else if (from_len != (int)afi->sa_sz) + { + from_ugh = "wrong length"; + } + else + { + switch (from.sa.sa_family) + { + case AF_INET: + from_ugh = initaddr((void *) &from.sa_in4.sin_addr + , sizeof(from.sa_in4.sin_addr), AF_INET, &md->sender); + md->sender_port = ntohs(from.sa_in4.sin_port); + break; + case AF_INET6: + from_ugh = initaddr((void *) &from.sa_in6.sin6_addr + , sizeof(from.sa_in6.sin6_addr), AF_INET6, &md->sender); + md->sender_port = ntohs(from.sa_in6.sin6_port); + break; + } + } + } + + /* now we report any actual I/O error */ + if (packet_len == -1) + { + if (from_ugh == undisclosed + && errno == ECONNREFUSED) + { + /* Tone down scary message for vague event: + * We get "connection refused" in response to some + * datagram we sent, but we cannot tell which one. + */ + plog("some IKE message we sent has been rejected with ECONNREFUSED (kernel supplied no details)"); + } + else if (from_ugh != NULL) + { + log_errno((e, "recvfrom on %s failed; Pluto cannot decode source sockaddr in rejection: %s" + , ifp->rname, from_ugh)); + } + else + { + log_errno((e, "recvfrom on %s from %s:%u failed" + , ifp->rname + , ip_str(&md->sender), (unsigned)md->sender_port)); + } + + return FALSE; + } + else if (from_ugh != NULL) + { + plog("recvfrom on %s returned misformed source sockaddr: %s" + , ifp->rname, from_ugh); + return FALSE; + } + cur_from = &md->sender; + cur_from_port = md->sender_port; + + if (ifp->ike_float == TRUE) + { + u_int32_t non_esp; + + if (packet_len < (int)sizeof(u_int32_t)) + { + plog("recvfrom %s:%u too small packet (%d)" + , ip_str(cur_from), (unsigned) cur_from_port, packet_len); + return FALSE; + } + memcpy(&non_esp, buffer, sizeof(u_int32_t)); + if (non_esp != 0) + { + plog("recvfrom %s:%u has no Non-ESP marker" + , ip_str(cur_from), (unsigned) cur_from_port); + return FALSE; + } + packet_len -= sizeof(u_int32_t); + buffer_nat = alloc_bytes(packet_len, "buffer read packet"); + memcpy(buffer_nat, buffer + sizeof(u_int32_t), packet_len); + pfree(buffer); + buffer = buffer_nat; + } + + /* Clone actual message contents + * and set up md->packet_pbs to describe it. + */ + init_pbs(&md->packet_pbs, buffer, packet_len, "packet"); + + DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL, + { + DBG_log(BLANK_FORMAT); + DBG_log("*received %d bytes from %s:%u on %s" + , (int) pbs_room(&md->packet_pbs) + , ip_str(cur_from), (unsigned) cur_from_port + , ifp->rname); + }); + + DBG(DBG_RAW, + DBG_dump("", md->packet_pbs.start, pbs_room(&md->packet_pbs))); + + if ((pbs_room(&md->packet_pbs)==1) && (md->packet_pbs.start[0]==0xff)) + { + /** + * NAT-T Keep-alive packets should be discared by kernel ESPinUDP + * layer. But boggus keep-alive packets (sent with a non-esp marker) + * can reach this point. Complain and discard them. + */ + DBG(DBG_NATT, + DBG_log("NAT-T keep-alive (boggus ?) should not reach this point. " + "Ignored. Sender: %s:%u", ip_str(cur_from), + (unsigned) cur_from_port); + ) + return FALSE; + } + +#define IKEV2_VERSION_OFFSET 17 +#define IKEV2_VERSION 0x20 + + /* ignore IKEv2 packets - they will be handled by charon */ + if (pbs_room(&md->packet_pbs) > IKEV2_VERSION_OFFSET + && md->packet_pbs.start[IKEV2_VERSION_OFFSET] == IKEV2_VERSION) + { + DBG(DBG_CONTROLMORE, + DBG_log(" ignoring IKEv2 packet") + ) + return FALSE; + } + + return TRUE; +} + +/* process an input packet, possibly generating a reply. + * + * If all goes well, this routine eventually calls a state-specific + * transition function. + */ +static void +process_packet(struct msg_digest **mdp) +{ + struct msg_digest *md = *mdp; + const struct state_microcode *smc; + bool new_iv_set = FALSE; + bool restore_iv = FALSE; + u_char new_iv[MAX_DIGEST_LEN]; + u_int new_iv_len = 0; + + struct state *st = NULL; + enum state_kind from_state = STATE_UNDEFINED; /* state we started in */ + +#define SEND_NOTIFICATION(t) { \ + if (st) send_notification_from_state(st, from_state, t); \ + else send_notification_from_md(md, t); } + + if (!in_struct(&md->hdr, &isakmp_hdr_desc, &md->packet_pbs, &md->message_pbs)) + { + /* Identify specific failures: + * - bad ISAKMP major/minor version numbers + */ + if (md->packet_pbs.roof - md->packet_pbs.cur >= (ptrdiff_t)isakmp_hdr_desc.size) + { + struct isakmp_hdr *hdr = (struct isakmp_hdr *)md->packet_pbs.cur; + if ((hdr->isa_version >> ISA_MAJ_SHIFT) != ISAKMP_MAJOR_VERSION) + { + SEND_NOTIFICATION(INVALID_MAJOR_VERSION); + return; + } + else if ((hdr->isa_version & ISA_MIN_MASK) != ISAKMP_MINOR_VERSION) + { + SEND_NOTIFICATION(INVALID_MINOR_VERSION); + return; + } + } + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + + if (md->packet_pbs.roof != md->message_pbs.roof) + { + plog("size (%u) differs from size specified in ISAKMP HDR (%u)" + , (unsigned) pbs_room(&md->packet_pbs), md->hdr.isa_length); +#ifdef CISCO_QUIRKS + if (pbs_room(&md->packet_pbs) - md->hdr.isa_length == 16) + plog("Cisco VPN client appends 16 surplus NULL bytes"); + else +#endif + return; + } + + switch (md->hdr.isa_xchg) + { +#ifdef NOTYET + case ISAKMP_XCHG_NONE: + case ISAKMP_XCHG_BASE: +#endif + + case ISAKMP_XCHG_IDPROT: /* part of a Main Mode exchange */ + if (md->hdr.isa_msgid != MAINMODE_MSGID) + { + plog("Message ID was 0x%08lx but should be zero in Main Mode", + (unsigned long) md->hdr.isa_msgid); + SEND_NOTIFICATION(INVALID_MESSAGE_ID); + return; + } + + if (is_zero_cookie(md->hdr.isa_icookie)) + { + plog("Initiator Cookie must not be zero in Main Mode message"); + SEND_NOTIFICATION(INVALID_COOKIE); + return; + } + + if (is_zero_cookie(md->hdr.isa_rcookie)) + { + /* initial message from initiator + * ??? what if this is a duplicate of another message? + */ + if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION) + { + plog("initial Main Mode message is invalid:" + " its Encrypted Flag is on"); + SEND_NOTIFICATION(INVALID_FLAGS); + return; + } + + /* don't build a state until the message looks tasty */ + from_state = STATE_MAIN_R0; + } + else + { + /* not an initial message */ + + st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie + , &md->sender, md->hdr.isa_msgid); + + if (st == NULL) + { + /* perhaps this is a first message from the responder + * and contains a responder cookie that we've not yet seen. + */ + st = find_state(md->hdr.isa_icookie, zero_cookie + , &md->sender, md->hdr.isa_msgid); + + if (st == NULL) + { + plog("Main Mode message is part of an unknown exchange"); + /* XXX Could send notification back */ + return; + } + } + set_cur_state(st); + from_state = st->st_state; + } + break; + +#ifdef NOTYET + case ISAKMP_XCHG_AO: + case ISAKMP_XCHG_AGGR: +#endif + + case ISAKMP_XCHG_INFO: /* an informational exchange */ + st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie + , &md->sender, MAINMODE_MSGID); + + if (st != NULL) + set_cur_state(st); + + if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION) + { + if (st == NULL) + { + plog("Informational Exchange is for an unknown (expired?) SA"); + /* XXX Could send notification back */ + return; + } + + if (!IS_ISAKMP_ENCRYPTED(st->st_state)) + { + loglog(RC_LOG_SERIOUS, "encrypted Informational Exchange message is invalid" + " because no key is known"); + /* XXX Could send notification back */ + return; + } + + if (md->hdr.isa_msgid == MAINMODE_MSGID) + { + loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because" + " it has a Message ID of 0"); + /* XXX Could send notification back */ + return; + } + + if (!reserve_msgid(st, md->hdr.isa_msgid)) + { + loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because" + " it has a previously used Message ID (0x%08lx)" + , (unsigned long)md->hdr.isa_msgid); + /* XXX Could send notification back */ + return; + } + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + memcpy(st->st_ph1_iv, st->st_new_iv, st->st_new_iv_len); + st->st_ph1_iv_len = st->st_new_iv_len; + + /* backup new_iv */ + new_iv_len = st->st_new_iv_len; + passert(new_iv_len <= MAX_DIGEST_LEN) + memcpy(new_iv, st->st_new_iv, new_iv_len); + restore_iv = TRUE; + } + init_phase2_iv(st, &md->hdr.isa_msgid); + new_iv_set = TRUE; + + from_state = STATE_INFO_PROTECTED; + } + else + { + if (st != NULL && IS_ISAKMP_ENCRYPTED(st->st_state)) + { + loglog(RC_LOG_SERIOUS, "Informational Exchange message" + " must be encrypted"); + /* XXX Could send notification back */ + return; + } + from_state = STATE_INFO; + } + break; + + case ISAKMP_XCHG_QUICK: /* part of a Quick Mode exchange */ + if (is_zero_cookie(md->hdr.isa_icookie)) + { + plog("Quick Mode message is invalid because" + " it has an Initiator Cookie of 0"); + SEND_NOTIFICATION(INVALID_COOKIE); + return; + } + + if (is_zero_cookie(md->hdr.isa_rcookie)) + { + plog("Quick Mode message is invalid because" + " it has a Responder Cookie of 0"); + SEND_NOTIFICATION(INVALID_COOKIE); + return; + } + + if (md->hdr.isa_msgid == MAINMODE_MSGID) + { + plog("Quick Mode message is invalid because" + " it has a Message ID of 0"); + SEND_NOTIFICATION(INVALID_MESSAGE_ID); + return; + } + + st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie + , &md->sender, md->hdr.isa_msgid); + + if (st == NULL) + { + /* No appropriate Quick Mode state. + * See if we have a Main Mode state. + * ??? what if this is a duplicate of another message? + */ + st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie + , &md->sender, MAINMODE_MSGID); + + if (st == NULL) + { + plog("Quick Mode message is for a non-existent (expired?)" + " ISAKMP SA"); + /* XXX Could send notification back */ + return; + } + + set_cur_state(st); + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + loglog(RC_LOG_SERIOUS, "Quick Mode message is unacceptable because" + " it is for an incomplete ISAKMP SA"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */); + return; + } + + /* only accept this new Quick Mode exchange if it has a unique message ID */ + if (!reserve_msgid(st, md->hdr.isa_msgid)) + { + loglog(RC_LOG_SERIOUS, "Quick Mode I1 message is unacceptable because" + " it uses a previously used Message ID 0x%08lx" + " (perhaps this is a duplicated packet)" + , (unsigned long) md->hdr.isa_msgid); + SEND_NOTIFICATION(INVALID_MESSAGE_ID); + return; + } + + /* Quick Mode Initial IV */ + init_phase2_iv(st, &md->hdr.isa_msgid); + new_iv_set = TRUE; + + from_state = STATE_QUICK_R0; + } + else + { + set_cur_state(st); + from_state = st->st_state; + } + + break; + + case ISAKMP_XCHG_MODE_CFG: + if (is_zero_cookie(md->hdr.isa_icookie)) + { + plog("ModeCfg message is invalid because" + " it has an Initiator Cookie of 0"); + /* XXX Could send notification back */ + return; + } + + if (is_zero_cookie(md->hdr.isa_rcookie)) + { + plog("ModeCfg message is invalid because" + " it has a Responder Cookie of 0"); + /* XXX Could send notification back */ + return; + } + + if (md->hdr.isa_msgid == 0) + { + plog("ModeCfg message is invalid because" + " it has a Message ID of 0"); + /* XXX Could send notification back */ + return; + } + + st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie + , &md->sender, md->hdr.isa_msgid); + + if (st == NULL) + { + bool has_xauth_policy; + + /* No appropriate ModeCfg state. + * See if we have a Main Mode state. + * ??? what if this is a duplicate of another message? + */ + st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie + , &md->sender, 0); + + if (st == NULL) + { + plog("ModeCfg message is for a non-existent (expired?)" + " ISAKMP SA"); + /* XXX Could send notification back */ + return; + } + + set_cur_state(st); + + /* the XAUTH_STATUS message might have a new msgid */ + if (st->st_state == STATE_XAUTH_I1) + { + init_phase2_iv(st, &md->hdr.isa_msgid); + new_iv_set = TRUE; + from_state = st->st_state; + break; + } + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + loglog(RC_LOG_SERIOUS, "ModeCfg message is unacceptable because" + " it is for an incomplete ISAKMP SA (state=%s)" + , enum_name(&state_names, st->st_state)); + /* XXX Could send notification back */ + return; + } + init_phase2_iv(st, &md->hdr.isa_msgid); + new_iv_set = TRUE; + + /* + * okay, now we have to figure out if we are receiving a bogus + * new message in an oustanding XAUTH server conversation + * (i.e. a reply to our challenge) + * (this occurs with some broken other implementations). + * + * or if receiving for the first time, an XAUTH challenge. + * + * or if we are getting a MODECFG request. + * + * we distinguish these states because we can not both be an + * XAUTH server and client, and our policy tells us which + * one we are. + * + * to complicate further, it is normal to start a new msgid + * when going from one state to another, or when restarting + * the challenge. + * + */ + + has_xauth_policy = (st->st_connection->policy + & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK)) + != LEMPTY; + + if (has_xauth_policy && !st->st_xauth.started + && IS_PHASE1(st->st_state)) + { + from_state = STATE_XAUTH_I0; + } + else if (st->st_connection->spd.that.modecfg + && IS_PHASE1(st->st_state)) + { + from_state = STATE_MODE_CFG_R0; + } + else if (st->st_connection->spd.this.modecfg + && IS_PHASE1(st->st_state)) + { + from_state = STATE_MODE_CFG_I0; + } + else + { + /* XXX check if we are being a mode config server here */ + plog("received ModeCfg message when in state %s, and we aren't mode config client" + , enum_name(&state_names, st->st_state)); + return; + } + } + else + { + set_cur_state(st); + from_state = st->st_state; + } + break; + +#ifdef NOTYET + case ISAKMP_XCHG_NGRP: + case ISAKMP_XCHG_ACK_INFO: +#endif + + default: + plog("unsupported exchange type %s in message" + , enum_show(&exchange_names, md->hdr.isa_xchg)); + SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE); + return; + } + + /* We have found a from_state, and perhaps a state object. + * If we need to build a new state object, + * we wait until the packet has been sanity checked. + */ + + /* We don't support the Commit Flag. It is such a bad feature. + * It isn't protected -- neither encrypted nor authenticated. + * A man in the middle turns it on, leading to DoS. + * We just ignore it, with a warning. + * By placing the check here, we could easily add a policy bit + * to a connection to suppress the warning. This might be useful + * because the Commit Flag is expected from some peers. + */ + if (md->hdr.isa_flags & ISAKMP_FLAG_COMMIT) + { + plog("IKE message has the Commit Flag set but Pluto doesn't implement this feature; ignoring flag"); + } + + /* Set smc to describe this state's properties. + * Look up the appropriate microcode based on state and + * possibly Oakley Auth type. + */ + passert(STATE_IKE_FLOOR <= from_state && from_state <= STATE_IKE_ROOF); + smc = ike_microcode_index[from_state - STATE_IKE_FLOOR]; + + if (st != NULL) + { + u_int16_t auth; + + switch (st->st_oakley.auth) + { + case XAUTHInitPreShared: + case XAUTHRespPreShared: + auth = OAKLEY_PRESHARED_KEY; + break; + case XAUTHInitRSA: + case XAUTHRespRSA: + auth = OAKLEY_RSA_SIG; + break; + default: + auth = st->st_oakley.auth; + } + + while (!LHAS(smc->flags, auth)) + { + smc++; + passert(smc->state == from_state); + } + } + + /* Ignore a packet if the state has a suspended state transition + * Probably a duplicated packet but the original packet is not yet + * recorded in st->st_rpacket, so duplicate checking won't catch. + * ??? Should the packet be recorded earlier to improve diagnosis? + */ + if (st != NULL && st->st_suspended_md != NULL) + { + loglog(RC_LOG, "discarding packet received during DNS lookup in %s" + , enum_name(&state_names, st->st_state)); + return; + } + + /* Detect and handle duplicated packets. + * This won't work for the initial packet of an exchange + * because we won't have a state object to remember it. + * If we are in a non-receiving state (terminal), and the preceding + * state did transmit, then the duplicate may indicate that that + * transmission wasn't received -- retransmit it. + * Otherwise, just discard it. + * ??? Notification packets are like exchanges -- I hope that + * they are idempotent! + */ + if (st != NULL + && st->st_rpacket.ptr != NULL + && st->st_rpacket.len == pbs_room(&md->packet_pbs) + && memcmp(st->st_rpacket.ptr, md->packet_pbs.start, st->st_rpacket.len) == 0) + { + if (smc->flags & SMF_RETRANSMIT_ON_DUPLICATE) + { + if (st->st_retransmit < MAXIMUM_RETRANSMISSIONS) + { + st->st_retransmit++; + loglog(RC_RETRANSMISSION + , "retransmitting in response to duplicate packet; already %s" + , enum_name(&state_names, st->st_state)); + send_packet(st, "retransmit in response to duplicate"); + } + else + { + loglog(RC_LOG_SERIOUS, "discarding duplicate packet -- exhausted retransmission; already %s" + , enum_name(&state_names, st->st_state)); + } + } + else + { + loglog(RC_LOG_SERIOUS, "discarding duplicate packet; already %s" + , enum_name(&state_names, st->st_state)); + } + return; + } + + if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION) + { + DBG(DBG_CRYPT, DBG_log("received encrypted packet from %s:%u" + , ip_str(&md->sender), (unsigned)md->sender_port)); + + if (st == NULL) + { + plog("discarding encrypted message for an unknown ISAKMP SA"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */); + return; + } + if (st->st_skeyid_e.ptr == (u_char *) NULL) + { + loglog(RC_LOG_SERIOUS, "discarding encrypted message" + " because we haven't yet negotiated keying materiel"); + SEND_NOTIFICATION(INVALID_FLAGS); + return; + } + + /* Mark as encrypted */ + md->encrypted = TRUE; + + DBG(DBG_CRYPT, DBG_log("decrypting %u bytes using algorithm %s" + , (unsigned) pbs_left(&md->message_pbs) + , enum_show(&oakley_enc_names, st->st_oakley.encrypt))); + + /* do the specified decryption + * + * IV is from st->st_iv or (if new_iv_set) st->st_new_iv. + * The new IV is placed in st->st_new_iv + * + * See RFC 2409 "IKE" Appendix B + * + * XXX The IV should only be updated really if the packet + * is successfully processed. + * We should keep this value, check for a success return + * value from the parsing routines and then replace. + * + * Each post phase 1 exchange generates IVs from + * the last phase 1 block, not the last block sent. + */ + { + const struct encrypt_desc *e = st->st_oakley.encrypter; + + if (pbs_left(&md->message_pbs) % e->enc_blocksize != 0) + { + loglog(RC_LOG_SERIOUS, "malformed message: not a multiple of encryption blocksize"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + + /* XXX Detect weak keys */ + + /* grab a copy of raw packet (for duplicate packet detection) */ + clonetochunk(md->raw_packet, md->packet_pbs.start + , pbs_room(&md->packet_pbs), "raw packet"); + + /* Decrypt everything after header */ + if (!new_iv_set) + { + /* use old IV */ + passert(st->st_iv_len <= sizeof(st->st_new_iv)); + st->st_new_iv_len = st->st_iv_len; + memcpy(st->st_new_iv, st->st_iv, st->st_new_iv_len); + } + crypto_cbc_encrypt(e, FALSE, md->message_pbs.cur, + pbs_left(&md->message_pbs) , st); + if (restore_iv) + { + memcpy(st->st_new_iv, new_iv, new_iv_len); + st->st_new_iv_len = new_iv_len; + } + } + + DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur + , md->message_pbs.roof - md->message_pbs.cur); + + DBG_cond_dump(DBG_CRYPT, "next IV:" + , st->st_new_iv, st->st_new_iv_len); + } + else + { + /* packet was not encryped -- should it have been? */ + + if (smc->flags & SMF_INPUT_ENCRYPTED) + { + loglog(RC_LOG_SERIOUS, "packet rejected: should have been encrypted"); + SEND_NOTIFICATION(INVALID_FLAGS); + return; + } + } + + /* Digest the message. + * Padding must be removed to make hashing work. + * Padding comes from encryption (so this code must be after decryption). + * Padding rules are described before the definition of + * struct isakmp_hdr in packet.h. + */ + { + struct payload_digest *pd = md->digest; + int np = md->hdr.isa_np; + lset_t needed = smc->req_payloads; + const char *excuse + = LIN(SMF_PSK_AUTH | SMF_FIRST_ENCRYPTED_INPUT, smc->flags) + ? "probable authentication failure (mismatch of preshared secrets?): " + : ""; + + while (np != ISAKMP_NEXT_NONE) + { + struct_desc *sd = np < ISAKMP_NEXT_ROOF? payload_descs[np] : NULL; + + if (pd == &md->digest[PAYLIMIT]) + { + loglog(RC_LOG_SERIOUS, "more than %d payloads in message; ignored", PAYLIMIT); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + + switch (np) + { + case ISAKMP_NEXT_NATD_RFC: + case ISAKMP_NEXT_NATOA_RFC: + if (!st || !(st->nat_traversal & NAT_T_WITH_RFC_VALUES)) + { + /* + * don't accept NAT-D/NAT-OA reloc directly in message, unless + * we're using NAT-T RFC + */ + sd = NULL; + } + break; + } + + if (sd == NULL) + { + /* payload type is out of range or requires special handling */ + switch (np) + { + case ISAKMP_NEXT_ID: + sd = IS_PHASE1(from_state) + ? &isakmp_identification_desc : &isakmp_ipsec_identification_desc; + break; + case ISAKMP_NEXT_NATD_DRAFTS: + np = ISAKMP_NEXT_NATD_RFC; /* NAT-D relocated */ + sd = payload_descs[np]; + break; + case ISAKMP_NEXT_NATOA_DRAFTS: + np = ISAKMP_NEXT_NATOA_RFC; /* NAT-OA relocated */ + sd = payload_descs[np]; + break; + default: + loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains an unknown or" + " unexpected payload type (%s) at the outermost level" + , excuse, enum_show(&payload_names, np)); + SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE); + return; + } + } + + { + lset_t s = LELEM(np); + + if (LDISJOINT(s + , needed | smc->opt_payloads| LELEM(ISAKMP_NEXT_N) | LELEM(ISAKMP_NEXT_D))) + { + loglog(RC_LOG_SERIOUS, "%smessage ignored because it " + "contains an unexpected payload type (%s)" + , excuse, enum_show(&payload_names, np)); + SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE); + return; + } + needed &= ~s; + } + + if (!in_struct(&pd->payload, sd, &md->message_pbs, &pd->pbs)) + { + loglog(RC_LOG_SERIOUS, "%smalformed payload in packet", excuse); + if (md->hdr.isa_xchg != ISAKMP_XCHG_INFO) + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + + /* place this payload at the end of the chain for this type */ + { + struct payload_digest **p; + + for (p = &md->chain[np]; *p != NULL; p = &(*p)->next) + ; + *p = pd; + pd->next = NULL; + } + + np = pd->payload.generic.isag_np; + pd++; + + /* since we've digested one payload happily, it is probably + * the case that any decryption worked. So we will not suggest + * encryption failure as an excuse for subsequent payload + * problems. + */ + excuse = ""; + } + + md->digest_roof = pd; + + DBG(DBG_PARSING, + if (pbs_left(&md->message_pbs) != 0) + DBG_log("removing %d bytes of padding", (int) pbs_left(&md->message_pbs))); + + md->message_pbs.roof = md->message_pbs.cur; + + /* check that all mandatory payloads appeared */ + + if (needed != 0) + { + loglog(RC_LOG_SERIOUS, "message for %s is missing payloads %s" + , enum_show(&state_names, from_state) + , bitnamesof(payload_name, needed)); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + } + + /* more sanity checking: enforce most ordering constraints */ + + if (IS_PHASE1(from_state)) + { + /* rfc2409: The Internet Key Exchange (IKE), 5 Exchanges: + * "The SA payload MUST precede all other payloads in a phase 1 exchange." + */ + if (md->chain[ISAKMP_NEXT_SA] != NULL + && md->hdr.isa_np != ISAKMP_NEXT_SA) + { + loglog(RC_LOG_SERIOUS, "malformed Phase 1 message: does not start with an SA payload"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + } + else if (IS_QUICK(from_state)) + { + /* rfc2409: The Internet Key Exchange (IKE), 5.5 Phase 2 - Quick Mode + * + * "In Quick Mode, a HASH payload MUST immediately follow the ISAKMP + * header and a SA payload MUST immediately follow the HASH." + * [NOTE: there may be more than one SA payload, so this is not + * totally reasonable. Probably all SAs should be so constrained.] + * + * "If ISAKMP is acting as a client negotiator on behalf of another + * party, the identities of the parties MUST be passed as IDci and + * then IDcr." + * + * "With the exception of the HASH, SA, and the optional ID payloads, + * there are no payload ordering restrictions on Quick Mode." + */ + + if (md->hdr.isa_np != ISAKMP_NEXT_HASH) + { + loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: does not start with a HASH payload"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + + { + struct payload_digest *p; + int i; + + for (p = md->chain[ISAKMP_NEXT_SA], i = 1; p != NULL + ; p = p->next, i++) + { + if (p != &md->digest[i]) + { + loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: SA payload is in wrong position"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + } + } + + /* rfc2409: The Internet Key Exchange (IKE), 5.5 Phase 2 - Quick Mode: + * "If ISAKMP is acting as a client negotiator on behalf of another + * party, the identities of the parties MUST be passed as IDci and + * then IDcr." + */ + { + struct payload_digest *id = md->chain[ISAKMP_NEXT_ID]; + + if (id != NULL) + { + if (id->next == NULL || id->next->next != NULL) + { + loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:" + " if any ID payload is present," + " there must be exactly two"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + if (id+1 != id->next) + { + loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:" + " the ID payloads are not adjacent"); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); + return; + } + } + } + } + + /* Ignore payloads that we don't handle: + * Delete, Notification, VendorID + */ + /* XXX Handle deletions */ + /* XXX Handle Notifications */ + /* XXX Handle VID payloads */ + { + struct payload_digest *p; + + for (p = md->chain[ISAKMP_NEXT_N]; p != NULL; p = p->next) + { + if (p->payload.notification.isan_type != R_U_THERE + && p->payload.notification.isan_type != R_U_THERE_ACK) + { + loglog(RC_LOG_SERIOUS, "ignoring informational payload, type %s" + , enum_show(¬ification_names, p->payload.notification.isan_type)); + } + DBG_cond_dump(DBG_PARSING, "info:", p->pbs.cur, pbs_left(&p->pbs)); + } + + for (p = md->chain[ISAKMP_NEXT_D]; p != NULL; p = p->next) + { + accept_delete(st, md, p); + DBG_cond_dump(DBG_PARSING, "del:", p->pbs.cur, pbs_left(&p->pbs)); + } + + for (p = md->chain[ISAKMP_NEXT_VID]; p != NULL; p = p->next) + { + handle_vendorid(md, p->pbs.cur, pbs_left(&p->pbs)); + } + } + md->from_state = from_state; + md->smc = smc; + md->st = st; + + /* possibly fill in hdr */ + if (smc->first_out_payload != ISAKMP_NEXT_NONE) + echo_hdr(md, (smc->flags & SMF_OUTPUT_ENCRYPTED) != 0 + , smc->first_out_payload); + + complete_state_transition(mdp, smc->processor(md)); +} + +/* complete job started by the state-specific state transition function */ + +void +complete_state_transition(struct msg_digest **mdp, stf_status result) +{ + bool has_xauth_policy; + bool is_xauth_server; + struct msg_digest *md = *mdp; + const struct state_microcode *smc = md->smc; + enum state_kind from_state = md->from_state; + struct state *st; + + cur_state = st = md->st; /* might have changed */ + + /* If state has DPD support, import it */ + if (st && md->dpd) + st->st_dpd = TRUE; + + switch (result) + { + case STF_IGNORE: + break; + + case STF_SUSPEND: + /* the stf didn't complete its job: don't relase md */ + *mdp = NULL; + break; + + case STF_OK: + /* advance the state */ + st->st_state = smc->next_state; + + /* Delete previous retransmission event. + * New event will be scheduled below. + */ + delete_event(st); + + /* replace previous receive packet with latest */ + + pfreeany(st->st_rpacket.ptr); + + if (md->encrypted) + { + /* if encrypted, duplication already done */ + st->st_rpacket = md->raw_packet; + md->raw_packet.ptr = NULL; + } + else + { + clonetochunk(st->st_rpacket + , md->packet_pbs.start + , pbs_room(&md->packet_pbs), "raw packet"); + } + + /* free previous transmit packet */ + freeanychunk(st->st_tpacket); + + /* if requested, send the new reply packet */ + if (smc->flags & SMF_REPLY) + { + close_output_pbs(&md->reply); /* good form, but actually a no-op */ + + clonetochunk(st->st_tpacket, md->reply.start + , pbs_offset(&md->reply), "reply packet"); + + if (nat_traversal_enabled) + nat_traversal_change_port_lookup(md, md->st); + + /* actually send the packet + * Note: this is a great place to implement "impairments" + * for testing purposes. Suppress or duplicate the + * send_packet call depending on st->st_state. + */ + send_packet(st, enum_name(&state_names, from_state)); + } + + /* Schedule for whatever timeout is specified */ + { + time_t delay; + enum event_type kind = smc->timeout_event; + bool agreed_time = FALSE; + struct connection *c = st->st_connection; + + switch (kind) + { + case EVENT_RETRANSMIT: /* Retransmit packet */ + delay = EVENT_RETRANSMIT_DELAY_0; + break; + + case EVENT_SA_REPLACE: /* SA replacement event */ + if (IS_PHASE1(st->st_state)) + { + /* Note: we will defer to the "negotiated" (dictated) + * lifetime if we are POLICY_DONT_REKEY. + * This allows the other side to dictate + * a time we would not otherwise accept + * but it prevents us from having to initiate + * rekeying. The negative consequences seem + * minor. + */ + delay = c->sa_ike_life_seconds; + if ((c->policy & POLICY_DONT_REKEY) + || delay >= st->st_oakley.life_seconds) + { + agreed_time = TRUE; + delay = st->st_oakley.life_seconds; + } + } + else + { + /* Delay is min of up to four things: + * each can limit the lifetime. + */ + delay = c->sa_ipsec_life_seconds; + if (st->st_ah.present + && delay >= st->st_ah.attrs.life_seconds) + { + agreed_time = TRUE; + delay = st->st_ah.attrs.life_seconds; + } + if (st->st_esp.present + && delay >= st->st_esp.attrs.life_seconds) + { + agreed_time = TRUE; + delay = st->st_esp.attrs.life_seconds; + } + if (st->st_ipcomp.present + && delay >= st->st_ipcomp.attrs.life_seconds) + { + agreed_time = TRUE; + delay = st->st_ipcomp.attrs.life_seconds; + } + } + + /* By default, we plan to rekey. + * + * If there isn't enough time to rekey, plan to + * expire. + * + * If we are --dontrekey, a lot more rules apply. + * If we are the Initiator, use REPLACE_IF_USED. + * If we are the Responder, and the dictated time + * was unacceptable (too large), plan to REPLACE + * (the only way to ratchet down the time). + * If we are the Responder, and the dictated time + * is acceptable, plan to EXPIRE. + * + * Important policy lies buried here. + * For example, we favour the initiator over the + * responder by making the initiator start rekeying + * sooner. Also, fuzz is only added to the + * initiator's margin. + * + * Note: for ISAKMP SA, we let the negotiated + * time stand (implemented by earlier logic). + */ + if (agreed_time + && (c->policy & POLICY_DONT_REKEY)) + { + kind = (smc->flags & SMF_INITIATOR) + ? EVENT_SA_REPLACE_IF_USED + : EVENT_SA_EXPIRE; + } + if (kind != EVENT_SA_EXPIRE) + { + unsigned long marg = c->sa_rekey_margin; + + if (smc->flags & SMF_INITIATOR) + marg += marg + * c->sa_rekey_fuzz / 100.E0 + * (rand() / (RAND_MAX + 1.E0)); + else + marg /= 2; + + if ((unsigned long)delay > marg) + { + delay -= marg; + st->st_margin = marg; + } + else + { + kind = EVENT_SA_EXPIRE; + } + } + break; + + case EVENT_NULL: /* non-event */ + case EVENT_REINIT_SECRET: /* Refresh cookie secret */ + default: + bad_case(kind); + } + event_schedule(kind, delay, st); + } + + /* tell whack and log of progress */ + { + const char *story = state_story[st->st_state - STATE_MAIN_R0]; + enum rc_type w = RC_NEW_STATE + st->st_state; + char sadetails[128]; + + sadetails[0]='\0'; + + if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) + { + char *b = sadetails; + const char *ini = " {"; + const char *fin = ""; + + /* -1 is to leave space for "fin" */ + + if (st->st_esp.present) + { + snprintf(b, sizeof(sadetails)-(b-sadetails)-1 + , "%sESP=>0x%08x <0x%08x" + , ini + , ntohl(st->st_esp.attrs.spi) + , ntohl(st->st_esp.our_spi)); + ini = " "; + fin = "}"; + } + /* advance b to end of string */ + b = b + strlen(b); + + if (st->st_ah.present) + { + snprintf(b, sizeof(sadetails)-(b-sadetails)-1 + , "%sAH=>0x%08x <0x%08x" + , ini + , ntohl(st->st_ah.attrs.spi) + , ntohl(st->st_ah.our_spi)); + ini = " "; + fin = "}"; + } + /* advance b to end of string */ + b = b + strlen(b); + + if (st->st_ipcomp.present) + { + snprintf(b, sizeof(sadetails)-(b-sadetails)-1 + , "%sIPCOMP=>0x%08x <0x%08x" + , ini + , ntohl(st->st_ipcomp.attrs.spi) + , ntohl(st->st_ipcomp.our_spi)); + ini = " "; + fin = "}"; + } + /* advance b to end of string */ + b = b + strlen(b); + + if (st->nat_traversal) + { + char oa[ADDRTOT_BUF]; + addrtot(&st->nat_oa, 0, oa, sizeof(oa)); + snprintf(b, sizeof(sadetails)-(b-sadetails)-1 + , "%sNATOA=%s" + , ini, oa); + ini = " "; + fin = "}"; + } + + /* advance b to end of string */ + b = b + strlen(b); + + if (st->st_dpd) + { + snprintf(b, sizeof(sadetails)-(b-sadetails)-1 + , "%sDPD" + , ini); + ini = " "; + fin = "}"; + } + + strcat(b, fin); + } + + if (IS_ISAKMP_SA_ESTABLISHED(st->st_state) + || IS_IPSEC_SA_ESTABLISHED(st->st_state)) + { + /* log our success */ + plog("%s%s", story, sadetails); + w = RC_SUCCESS; + } + + /* tell whack our progress */ + whack_log(w + , "%s: %s%s" + , enum_name(&state_names, st->st_state) + , story, sadetails); + } + + has_xauth_policy = (st->st_connection->policy + & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK)) + != LEMPTY; + is_xauth_server = (st->st_connection->policy + & POLICY_XAUTH_SERVER) + != LEMPTY; + + /* Should we start XAUTH as a server */ + if (has_xauth_policy && is_xauth_server + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !st->st_xauth.started) + { + DBG(DBG_CONTROL, + DBG_log("starting XAUTH server") + ) + xauth_send_request(st); + break; + } + + /* Wait for XAUTH request from server */ + if (has_xauth_policy && !is_xauth_server + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !st->st_xauth.started) + { + DBG(DBG_CONTROL, + DBG_log("waiting for XAUTH request from server") + ) + break; + } + + /* Should we start ModeConfig as a client? */ + if (st->st_connection->spd.this.modecfg + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !(st->st_connection->policy & POLICY_MODECFG_PUSH) + && !st->st_modecfg.started) + { + DBG(DBG_CONTROL, + DBG_log("starting ModeCfg client in pull mode") + ) + modecfg_send_request(st); + break; + } + + /* Should we start ModeConfig as a server? */ + if (st->st_connection->spd.that.modecfg + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !st->st_modecfg.started + && (st->st_connection->policy & POLICY_MODECFG_PUSH)) + { + DBG(DBG_CONTROL, + DBG_log("starting ModeCfg server in push mode") + ) + modecfg_send_set(st); + break; + } + + /* Wait for ModeConfig set from server */ + if (st->st_connection->spd.this.modecfg + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !st->st_modecfg.vars_set) + { + DBG(DBG_CONTROL, + DBG_log("waiting for ModeCfg set from server") + ) + break; + } + + if (smc->flags & SMF_RELEASE_PENDING_P2) + { + /* Initiate any Quick Mode negotiations that + * were waiting to piggyback on this Keying Channel. + * + * ??? there is a potential race condition + * if we are the responder: the initial Phase 2 + * message might outrun the final Phase 1 message. + * I think that retransmission will recover. + */ + unpend(st); + } + + if (IS_ISAKMP_SA_ESTABLISHED(st->st_state) + || IS_IPSEC_SA_ESTABLISHED(st->st_state)) + release_whack(st); + break; + + case STF_INTERNAL_ERROR: + whack_log(RC_INTERNALERR + md->note + , "%s: internal error" + , enum_name(&state_names, st->st_state)); + + DBG(DBG_CONTROL, + DBG_log("state transition function for %s had internal error" + , enum_name(&state_names, from_state))); + break; + + default: /* a shortcut to STF_FAIL, setting md->note */ + passert(result > STF_FAIL); + md->note = result - STF_FAIL; + result = STF_FAIL; + /* FALL THROUGH ... */ + case STF_FAIL: + /* As it is, we act as if this message never happened: + * whatever retrying was in place, remains in place. + */ + whack_log(RC_NOTIFICATION + md->note + , "%s: %s" + , enum_name(&state_names, (st == NULL)? STATE_MAIN_R0:st->st_state) + , enum_name(¬ification_names, md->note)); + + SEND_NOTIFICATION(md->note); + + DBG(DBG_CONTROL, + DBG_log("state transition function for %s failed: %s" + , enum_name(&state_names, from_state) + , enum_name(¬ification_names, md->note))); + break; + } +} diff --git a/src/pluto/demux.h b/src/pluto/demux.h new file mode 100644 index 000000000..373dd6315 --- /dev/null +++ b/src/pluto/demux.h @@ -0,0 +1,93 @@ +/* demultiplex incoming IKE messages + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: demux.h,v 1.4 2004/07/22 22:57:25 as Exp $ + */ + +#include "packet.h" + +struct state; /* forward declaration of tag */ +extern void init_demux(void); +extern bool send_packet(struct state *st, const char *where); +extern void comm_handle(const struct iface *ifp); + +extern u_int8_t reply_buffer[MAX_OUTPUT_UDP_SIZE]; + +/* State transition function infrastructure + * + * com_handle parses a message, decides what state object it applies to, + * and calls the appropriate state transition function (STF). + * These declarations define the interface to these functions. + * + * Each STF must be able to be restarted up to any failure point: + * a later message will cause the state to be re-entered. This + * explains the use of the replace macro and the care in handling + * MP_INT members of struct state. + */ + +struct payload_digest { + pb_stream pbs; + union payload payload; + struct payload_digest *next; /* of same kind */ +}; + +/* message digest + * Note: raw_packet and packet_pbs are "owners" of space on heap. + */ + +struct msg_digest { + struct msg_digest *next; /* for free list */ + chunk_t raw_packet; /* if encrypted, received packet before decryption */ + const struct iface *iface; /* interface on which message arrived */ + ip_address sender; /* where message came from */ + u_int16_t sender_port; /* host order */ + pb_stream packet_pbs; /* whole packet */ + pb_stream message_pbs; /* message to be processed */ + struct isakmp_hdr hdr; /* message's header */ + bool encrypted; /* was it encrypted? */ + enum state_kind from_state; /* state we started in */ + const struct state_microcode *smc; /* microcode for initial state */ + struct state *st; /* current state object */ + pb_stream reply; /* room for reply */ + pb_stream rbody; /* room for reply body (after header) */ + notification_t note; /* reason for failure */ + bool dpd; /* peer supports RFC 3706 DPD */ + bool openpgp; /* peer supports OpenPGP certificates */ + +# define PAYLIMIT 40 + struct payload_digest + digest[PAYLIMIT], + *digest_roof, + *chain[ISAKMP_NEXT_ROOF]; + unsigned short nat_traversal_vid; +}; + +extern void release_md(struct msg_digest *md); + +/* status for state-transition-function + * Note: STF_FAIL + notification_t means fail with that notification + */ + +typedef enum { + STF_IGNORE, /* don't respond */ + STF_SUSPEND, /* unfinished -- don't release resources */ + STF_OK, /* success */ + STF_INTERNAL_ERROR, /* discard everything, we failed */ + STF_FAIL /* discard everything, something failed. notification_t added. */ +} stf_status; + +typedef stf_status state_transition_fn(struct msg_digest *md); + +extern void complete_state_transition(struct msg_digest **mdp, stf_status result); + +extern void free_md_pool(void); diff --git a/src/pluto/dnskey.c b/src/pluto/dnskey.c new file mode 100644 index 000000000..23863b0a2 --- /dev/null +++ b/src/pluto/dnskey.c @@ -0,0 +1,1962 @@ +/* Find public key in DNS + * Copyright (C) 2000-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: dnskey.c,v 1.5 2005/09/08 16:26:30 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* ??? for h_errno */ +#include + +#include +#include + +#include "constants.h" +#include "adns.h" /* needs */ +#include "defs.h" +#include "log.h" +#include "id.h" +#include "connections.h" +#include "keys.h" /* needs connections.h */ +#include "dnskey.h" +#include "packet.h" +#include "timer.h" + +/* somebody has to decide */ +#define MAX_TXT_RDATA ((MAX_KEY_BYTES * 8 / 6) + 40) /* somewhat arbitrary overkill */ + +/* ADNS stuff */ + +int adns_qfd = NULL_FD, /* file descriptor for sending queries to adns (O_NONBLOCK) */ + adns_afd = NULL_FD; /* file descriptor for receiving answers from adns */ +static pid_t adns_pid = 0; +const char *pluto_adns_option = NULL; /* path from --pluto_adns */ + +int adns_restart_count; +#define ADNS_RESTART_MAX 20 + +void +init_adns(void) +{ + const char *adns_path = pluto_adns_option; +#ifndef USE_LWRES + static const char adns_name[] = "_pluto_adns"; + const char *helper_bin_dir = getenv("IPSEC_LIBDIR"); +#else /* USE_LWRES */ + static const char adns_name[] = "lwdnsq"; + const char *helper_bin_dir = getenv("IPSEC_EXECDIR"); +#endif /* USE_LWRES */ + char adns_path_space[4096]; /* plenty long? */ + int qfds[2]; + int afds[2]; + + /* find a pathname to the ADNS program */ + if (adns_path == NULL) + { + /* pathname was not specified as an option: build it. + * First, figure out the directory to be used. + */ + ssize_t n; + + if (helper_bin_dir != NULL) + { + n = strlen(helper_bin_dir); + if ((size_t)n <= sizeof(adns_path_space) - sizeof(adns_name)) + { + strcpy(adns_path_space, helper_bin_dir); + if (n > 0 && adns_path_space[n -1] != '/') + adns_path_space[n++] = '/'; + } + } + else + { + /* The program will be in the same directory as Pluto, + * so we use the sympolic link /proc/self/exe to + * tell us of the path prefix. + */ + n = readlink("/proc/self/exe", adns_path_space, sizeof(adns_path_space)); + + if (n < 0) + exit_log_errno((e + , "readlink(\"/proc/self/exe\") failed in init_adns()")); + + } + + if ((size_t)n > sizeof(adns_path_space) - sizeof(adns_name)) + exit_log("path to %s is too long", adns_name); + + while (n > 0 && adns_path_space[n - 1] != '/') + n--; + + strcpy(adns_path_space + n, adns_name); + adns_path = adns_path_space; + } + if (access(adns_path, X_OK) < 0) + exit_log_errno((e, "%s missing or not executable", adns_path)); + + if (pipe(qfds) != 0 || pipe(afds) != 0) + exit_log_errno((e, "pipe(2) failed in init_adns()")); + + adns_pid = fork(); + switch (adns_pid) + { + case -1: + exit_log_errno((e, "fork() failed in init_adns()")); + + case 0: + /* child */ + { + /* Make stdin and stdout our pipes. + * Take care to handle case where pipes already use these fds. + */ + if (afds[1] == 0) + afds[1] = dup(afds[1]); /* avoid being overwritten */ + if (qfds[0] != 0) + { + dup2(qfds[0], 0); + close(qfds[0]); + } + if (afds[1] != 1) + { + dup2(afds[1], 1); + close(qfds[1]); + } + if (afds[0] > 1) + close(afds[0]); + if (afds[1] > 1) + close(afds[1]); + + DBG(DBG_DNS, execlp(adns_path, adns_name, "-d", NULL)); + + execlp(adns_path, adns_name, NULL); + exit_log_errno((e, "execlp of %s failed", adns_path)); + } + + default: + /* parent */ + close(qfds[0]); + adns_qfd = qfds[1]; + adns_afd = afds[0]; + close(afds[1]); + fcntl(adns_qfd, F_SETFD, FD_CLOEXEC); + fcntl(adns_afd, F_SETFD, FD_CLOEXEC); + fcntl(adns_qfd, F_SETFL, O_NONBLOCK); + break; + } +} + +void +stop_adns(void) +{ + close_any(adns_qfd); + adns_qfd = NULL_FD; + close_any(adns_afd); + adns_afd = NULL_FD; + + if (adns_pid != 0) + { + int status; + pid_t p = waitpid(adns_pid, &status, 0); + + if (p == -1) + { + log_errno((e, "waitpid for ADNS process failed")); + } + else if (WIFEXITED(status)) + { + if (WEXITSTATUS(status) != 0) + plog("ADNS process exited with status %d" + , (int) WEXITSTATUS(status)); + } + else if (WIFSIGNALED(status)) + { + plog("ADNS process terminated by signal %d", (int)WTERMSIG(status)); + } + else + { + plog("wait for end of ADNS process returned odd status 0x%x\n" + , status); + } + } +} + + + +/* tricky macro to pass any hot potato */ +#define TRY(x) { err_t ugh = x; if (ugh != NULL) return ugh; } + + +/* Process TXT X-IPsec-Server record, accumulating relevant ones + * in cr->gateways_from_dns, a list sorted by "preference". + * + * Format of TXT record body: X-IPsec-Server ( nnn ) = iii kkk + * nnn is a 16-bit unsigned integer preference + * iii is @FQDN or dotted-decimal IPv4 address or colon-hex IPv6 address + * kkk is an optional RSA public signing key in base 64. + * + * NOTE: we've got to be very wary of anything we find -- bad guys + * might have prepared it. + */ + +#define our_TXT_attr_string "X-IPsec-Server" +static const char our_TXT_attr[] = our_TXT_attr_string; + +static err_t +decode_iii(u_char **pp, struct id *gw_id) +{ + u_char *p = *pp + strspn(*pp, " \t"); + u_char *e = p + strcspn(p, " \t"); + u_char under = *e; + + if (p == e) + return "TXT " our_TXT_attr_string " badly formed (no gateway specified)"; + + *e = '\0'; + if (*p == '@') + { + /* gateway specification in this record is @FQDN */ + err_t ugh = atoid(p, gw_id, FALSE); + + if (ugh != NULL) + return builddiag("malformed FQDN in TXT " our_TXT_attr_string ": %s" + , ugh); + } + else + { + /* gateway specification is numeric */ + ip_address ip; + err_t ugh = tnatoaddr(p, e-p + , strchr(p, ':') == NULL? AF_INET : AF_INET6 + , &ip); + + if (ugh != NULL) + return builddiag("malformed IP address in TXT " our_TXT_attr_string ": %s" + , ugh); + + if (isanyaddr(&ip)) + return "gateway address must not be 0.0.0.0 or 0::0"; + + iptoid(&ip, gw_id); + } + + *e = under; + *pp = e + strspn(e, " \t"); + + return NULL; +} + +static err_t +process_txt_rr_body(u_char *str +, bool doit /* should we capture information? */ +, enum dns_auth_level dns_auth_level +, struct adns_continuation *const cr) +{ + const struct id *client_id = &cr->id; /* subject of query */ + u_char *p = str; + unsigned long pref = 0; + struct gw_info gi; + + p += strspn(p, " \t"); /* ignore leading whitespace */ + + /* is this for us? */ + if (strncasecmp(p, our_TXT_attr, sizeof(our_TXT_attr)-1) != 0) + return NULL; /* neither interesting nor bad */ + + p += sizeof(our_TXT_attr) - 1; /* ignore our attribute name */ + p += strspn(p, " \t"); /* ignore leading whitespace */ + + /* decode '(' nnn ')' */ + if (*p != '(') + return "X-IPsec-Server missing '('"; + + { + char *e; + + p++; + pref = strtoul(p, &e, 0); + if ((u_char *)e == p) + return "malformed X-IPsec-Server priority"; + + p = e + strspn(e, " \t"); + + if (*p != ')') + return "X-IPsec-Server priority missing ')'"; + + p++; + p += strspn(p, " \t"); + + if (pref > 0xFFFF) + return "X-IPsec-Server priority larger than 0xFFFF"; + } + + /* time for '=' */ + + if (*p != '=') + return "X-IPsec-Server priority missing '='"; + + p++; + p += strspn(p, " \t"); + + /* Decode iii (Security Gateway ID). */ + + zero(&gi); /* before first use */ + + TRY(decode_iii(&p, &gi.gw_id)); /* will need to unshare_id_content */ + + if (!cr->sgw_specified) + { + /* we don't know the peer's ID (because we are initiating + * and we don't know who to initiate with. + * So we're looking for gateway specs with an IP address + */ + if (!id_is_ipaddr(&gi.gw_id)) + { + DBG(DBG_DNS, + { + char cidb[BUF_LEN]; + char gwidb[BUF_LEN]; + + idtoa(client_id, cidb, sizeof(cidb)); + idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + DBG_log("TXT %s record for %s: security gateway %s;" + " ignored because gateway's IP is unspecified" + , our_TXT_attr, cidb, gwidb); + }); + return NULL; /* we cannot use this record, but it isn't wrong */ + } + } + else + { + /* We do know the peer's ID (because we are responding) + * So we're looking for gateway specs specifying this known ID. + */ + const struct id *peer_id = &cr->sgw_id; + + if (!same_id(peer_id, &gi.gw_id)) + { + DBG(DBG_DNS, + { + char cidb[BUF_LEN]; + char gwidb[BUF_LEN]; + char pidb[BUF_LEN]; + + idtoa(client_id, cidb, sizeof(cidb)); + idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + idtoa(peer_id, pidb, sizeof(pidb)); + DBG_log("TXT %s record for %s: security gateway %s;" + " ignored -- looking to confirm %s as gateway" + , our_TXT_attr, cidb, gwidb, pidb); + }); + return NULL; /* we cannot use this record, but it isn't wrong */ + } + } + + if (doit) + { + /* really accept gateway */ + struct gw_info **gwip; /* gateway insertion point */ + + gi.client_id = *client_id; /* will need to unshare_id_content */ + + /* decode optional kkk: base 64 encoding of key */ + + gi.gw_key_present = *p != '\0'; + if (gi.gw_key_present) + { + /* Decode base 64 encoding of key. + * Similar code is in process_lwdnsq_key. + */ + u_char kb[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ + chunk_t kbc; + struct RSA_public_key r; + + err_t ugh = ttodatav(p, 0, 64, kb, sizeof(kb), &kbc.len + , diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS); + + if (ugh != NULL) + return builddiag("malformed key data: %s", ugh); + + if (kbc.len > sizeof(kb)) + return builddiag("key data larger than %lu bytes" + , (unsigned long) sizeof(kb)); + + kbc.ptr = kb; + ugh = unpack_RSA_public_key(&r, &kbc); + if (ugh != NULL) + return builddiag("invalid key data: %s", ugh); + + /* now find a key entry to put it in */ + gi.key = public_key_from_rsa(&r); + + free_RSA_public_content(&r); + + unreference_key(&cr->last_info); + cr->last_info = reference_key(gi.key); + } + + /* we're home free! Allocate everything and add to gateways list. */ + gi.refcnt = 1; + gi.pref = pref; + gi.key->dns_auth_level = dns_auth_level; + gi.key->last_tried_time = gi.key->last_worked_time = NO_TIME; + + /* find insertion point */ + for (gwip = &cr->gateways_from_dns; *gwip != NULL && (*gwip)->pref < pref; gwip = &(*gwip)->next) + ; + + DBG(DBG_DNS, + { + char cidb[BUF_LEN]; + char gwidb[BUF_LEN]; + + idtoa(client_id, cidb, sizeof(cidb)); + idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + if (gi.gw_key_present) + { + DBG_log("gateway for %s is %s with key %s" + , cidb, gwidb, gi.key->u.rsa.keyid); + } + else + { + DBG_log("gateway for %s is %s; no key specified" + , cidb, gwidb); + } + }); + + gi.next = *gwip; + *gwip = clone_thing(gi, "gateway info"); + unshare_id_content(&(*gwip)->gw_id); + unshare_id_content(&(*gwip)->client_id); + } + + return NULL; +} + +static const char * +rr_typename(int type) +{ + switch (type) + { + case T_TXT: + return "TXT"; + case T_KEY: + return "KEY"; + default: + return "???"; + } +} + + +#ifdef USE_LWRES + +# ifdef USE_KEYRR +static err_t +process_lwdnsq_key(u_char *str +, enum dns_auth_level dns_auth_level +, struct adns_continuation *const cr) +{ + /* fields of KEY record. See RFC 2535 3.1 KEY RDATA format. */ + unsigned long flags /* 16 bits */ + , protocol /* 8 bits */ + , algorithm; /* 8 bits */ + + char *rest = str + , *p + , *endofnumber; + + /* flags */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq KEY: missing flags"; + + flags = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq KEY: malformed flags"; + + /* protocol */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq KEY: missing protocol"; + + protocol = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq KEY: malformed protocol"; + + /* algorithm */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq KEY: missing algorithm"; + + algorithm = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq KEY: malformed algorithm"; + + /* is this key interesting? */ + if (protocol == 4 /* IPSEC (RFC 2535 3.1.3) */ + && algorithm == 1 /* RSA/MD5 (RFC 2535 3.2) */ + && (flags & 0x8000ul) == 0 /* use for authentication (3.1.2) */ + && (flags & 0x2CF0ul) == 0) /* must be zero */ + { + /* Decode base 64 encoding of key. + * Similar code is in process_txt_rr_body. + */ + u_char kb[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ + chunk_t kbc; + err_t ugh = ttodatav(rest, 0, 64, kb, sizeof(kb), &kbc.len + , diag_space, sizeof(diag_space), TTODATAV_IGNORESPACE); + + if (ugh != NULL) + return builddiag("malformed key data: %s", ugh); + + if (kbc.len > sizeof(kb)) + return builddiag("key data larger than %lu bytes" + , (unsigned long) sizeof(kb)); + + kbc.ptr = kb; + TRY(add_public_key(&cr->id, dns_auth_level, PUBKEY_ALG_RSA, &kbc + , &cr->keys_from_dns)); + + /* keep a reference to last one */ + unreference_key(&cr->last_info); + cr->last_info = reference_key(cr->keys_from_dns->key); + } + return NULL; +} +# endif /* USE_KEYRR */ + +#else /* ! USE_LWRES */ + +/* structure of Query Reply (RFC 1035 4.1.1): + * + * +---------------------+ + * | Header | + * +---------------------+ + * | Question | the question for the name server + * +---------------------+ + * | Answer | RRs answering the question + * +---------------------+ + * | Authority | RRs pointing toward an authority + * +---------------------+ + * | Additional | RRs holding additional information + * +---------------------+ + */ + +/* Header section format (as modified by RFC 2535 6.1): + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ +struct qr_header { + u_int16_t id; /* 16-bit identifier to match query */ + + u_int16_t stuff; /* packed crud: */ + +#define QRS_QR 0x8000 /* QR: on if this is a response */ + +#define QRS_OPCODE_SHIFT 11 /* OPCODE field */ +#define QRS_OPCODE_MASK 0xF +#define QRSO_QUERY 0 /* standard query */ +#define QRSO_IQUERY 1 /* inverse query */ +#define QRSO_STATUS 2 /* server status request query */ + +#define QRS_AA 0x0400 /* AA: on if Authoritative Answer */ +#define QRS_TC 0x0200 /* TC: on if truncation happened */ +#define QRS_RD 0x0100 /* RD: on if recursion desired */ +#define QRS_RA 0x0080 /* RA: on if recursion available */ +#define QRS_Z 0x0040 /* Z: reserved; must be zero */ +#define QRS_AD 0x0020 /* AD: on if authentic data (RFC 2535) */ +#define QRS_CD 0x0010 /* AD: on if checking disabled (RFC 2535) */ + +#define QRS_RCODE_SHIFT 0 /* RCODE field: response code */ +#define QRS_RCODE_MASK 0xF +#define QRSR_OK 0 + + + u_int16_t qdcount; /* number of entries in question section */ + u_int16_t ancount; /* number of resource records in answer section */ + u_int16_t nscount; /* number of name server resource records in authority section */ + u_int16_t arcount; /* number of resource records in additional records section */ +}; + +static field_desc qr_header_fields[] = { + { ft_nat, 16/BITS_PER_BYTE, "ID", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "stuff", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "QD Count", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Answer Count", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Authority Count", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Additional Count", NULL }, + { ft_end, 0, NULL, NULL } +}; + +static struct_desc qr_header_desc = { + "Query Response Header", + qr_header_fields, + sizeof(struct qr_header) +}; + +/* Messages for codes in RCODE (see RFC 1035 4.1.1) */ +static const err_t rcode_text[QRS_RCODE_MASK + 1] = { + NULL, /* not an error */ + "Format error - The name server was unable to interpret the query", + "Server failure - The name server was unable to process this query" + " due to a problem with the name server", + "Name Error - Meaningful only for responses from an authoritative name" + " server, this code signifies that the domain name referenced in" + " the query does not exist", + "Not Implemented - The name server does not support the requested" + " kind of query", + "Refused - The name server refuses to perform the specified operation" + " for policy reasons", + /* the rest are reserved for future use */ + }; + +/* throw away a possibly compressed domain name */ + +static err_t +eat_name(pb_stream *pbs) +{ + u_char name_buf[NS_MAXDNAME + 2]; + u_char *ip = pbs->cur; + unsigned oi = 0; + unsigned jump_count = 0; + + for (;;) + { + u_int8_t b; + + if (ip >= pbs->roof) + return "ran out of message while skipping domain name"; + + b = *ip++; + if (jump_count == 0) + pbs->cur = ip; + + if (b == 0) + break; + + switch (b & 0xC0) + { + case 0x00: + /* we grab the next b characters */ + if (oi + b > NS_MAXDNAME) + return "domain name too long"; + + if (pbs->roof - ip <= b) + return "domain name falls off end of message"; + + if (oi != 0) + name_buf[oi++] = '.'; + + memcpy(name_buf + oi, ip, b); + oi += b; + ip += b; + if (jump_count == 0) + pbs->cur = ip; + break; + + case 0xC0: + { + unsigned ix; + + if (ip >= pbs->roof) + return "ran out of message in middle of compressed domain name"; + + ix = ((b & ~0xC0u) << 8) | *ip++; + if (jump_count == 0) + pbs->cur = ip; + + if (ix >= pbs_room(pbs)) + return "impossible compressed domain name"; + + /* Avoid infinite loop. + * There can be no more jumps than there are bytes + * in the packet. Not a tight limit, but good enough. + */ + jump_count++; + if (jump_count > pbs_room(pbs)) + return "loop in compressed domain name"; + + ip = pbs->start + ix; + } + break; + + default: + return "invalid code in label"; + } + } + + name_buf[oi++] = '\0'; + + DBG(DBG_DNS, DBG_log("skipping name %s", name_buf)); + + return NULL; +} + +static err_t +eat_name_helpfully(pb_stream *pbs, const char *context) +{ + err_t ugh = eat_name(pbs); + + return ugh == NULL? ugh + : builddiag("malformed name within DNS record of %s: %s", context, ugh); +} + +/* non-variable part of 4.1.2 Question Section entry: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +struct qs_fixed { + u_int16_t qtype; + u_int16_t qclass; +}; + +static field_desc qs_fixed_fields[] = { + { ft_loose_enum, 16/BITS_PER_BYTE, "QTYPE", &rr_qtype_names }, + { ft_loose_enum, 16/BITS_PER_BYTE, "QCLASS", &rr_class_names }, + { ft_end, 0, NULL, NULL } +}; + +static struct_desc qs_fixed_desc = { + "Question Section entry fixed part", + qs_fixed_fields, + sizeof(struct qs_fixed) +}; + +/* 4.1.3. Resource record format: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / / + * / NAME / + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | CLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TTL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RDLENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + * / RDATA / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +struct rr_fixed { + u_int16_t type; + u_int16_t class; + u_int32_t ttl; /* actually signed */ + u_int16_t rdlength; +}; + + +static field_desc rr_fixed_fields[] = { + { ft_loose_enum, 16/BITS_PER_BYTE, "type", &rr_type_names }, + { ft_loose_enum, 16/BITS_PER_BYTE, "class", &rr_class_names }, + { ft_nat, 32/BITS_PER_BYTE, "TTL", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "RD length", NULL }, + { ft_end, 0, NULL, NULL } +}; + +static struct_desc rr_fixed_desc = { + "Resource Record fixed part", + rr_fixed_fields, + /* note: following is tricky: avoids padding problems */ + offsetof(struct rr_fixed, rdlength) + sizeof(u_int16_t) +}; + +/* RFC 1035 3.3.14: TXT RRs have text in the RDATA field. + * It is in the form of a sequence of s as described in 3.3. + * unpack_txt_rdata() deals with this peculiar representation. + */ + +/* RFC 2535 3.1 KEY RDATA format: + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | flags | protocol | algorithm | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | / + * / public key / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + */ + +struct key_rdata { + u_int16_t flags; + u_int8_t protocol; + u_int8_t algorithm; +}; + +static field_desc key_rdata_fields[] = { + { ft_nat, 16/BITS_PER_BYTE, "flags", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "protocol", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "algorithm", NULL }, + { ft_end, 0, NULL, NULL } +}; + +static struct_desc key_rdata_desc = { + "KEY RR RData fixed part", + key_rdata_fields, + sizeof(struct key_rdata) +}; + +/* RFC 2535 4.1 SIG RDATA format: + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | type covered | algorithm | labels | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | original TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | signature expiration | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | signature inception | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | key tag | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ signer's name + + * | / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-/ + * / / + * / signature / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +struct sig_rdata { + u_int16_t type_covered; + u_int8_t algorithm; + u_int8_t labels; + u_int32_t original_ttl; + u_int32_t sig_expiration; + u_int32_t sig_inception; + u_int16_t key_tag; +}; + +static field_desc sig_rdata_fields[] = { + { ft_nat, 16/BITS_PER_BYTE, "type_covered", NULL}, + { ft_nat, 8/BITS_PER_BYTE, "algorithm", NULL}, + { ft_nat, 8/BITS_PER_BYTE, "labels", NULL}, + { ft_nat, 32/BITS_PER_BYTE, "original ttl", NULL}, + { ft_nat, 32/BITS_PER_BYTE, "sig expiration", NULL}, + { ft_nat, 32/BITS_PER_BYTE, "sig inception", NULL}, + { ft_nat, 16/BITS_PER_BYTE, "key tag", NULL}, + { ft_end, 0, NULL, NULL } +}; + +static struct_desc sig_rdata_desc = { + "SIG RR RData fixed part", + sig_rdata_fields, + sizeof(struct sig_rdata) +}; + +/* handle a KEY Resource Record. */ + +#ifdef USE_KEYRR +static err_t +process_key_rr(u_char *ptr, size_t len +, bool doit /* should we capture information? */ +, enum dns_auth_level dns_auth_level +, struct adns_continuation *const cr) +{ + pb_stream pbs; + struct key_rdata kr; + + if (len < sizeof(struct key_rdata)) + return "KEY Resource Record's RD Length is too small"; + + init_pbs(&pbs, ptr, len, "KEY RR"); + + if (!in_struct(&kr, &key_rdata_desc, &pbs, NULL)) + return "failed to get fixed part of KEY Resource Record RDATA"; + + if (kr.protocol == 4 /* IPSEC (RFC 2535 3.1.3) */ + && kr.algorithm == 1 /* RSA/MD5 (RFC 2535 3.2) */ + && (kr.flags & 0x8000) == 0 /* use for authentication (3.1.2) */ + && (kr.flags & 0x2CF0) == 0) /* must be zero */ + { + /* we have what seems to be a tasty key */ + + if (doit) + { + chunk_t k; + + setchunk(k, pbs.cur, pbs_left(&pbs)); + TRY(add_public_key(&cr->id, dns_auth_level, PUBKEY_ALG_RSA, &k + , &cr->keys_from_dns)); + } + } + return NULL; +} +#endif /* USE_KEYRR */ + + +/* unpack TXT rr RDATA into C string. + * A sequence of s as described in RFC 1035 3.3. + * We concatenate them. + */ +static err_t +unpack_txt_rdata(u_char *d, size_t dlen, const u_char *s, size_t slen) +{ + size_t i = 0 + , o = 0; + + while (i < slen) + { + size_t cl = s[i++]; + + if (i + cl > slen) + return "TXT rr RDATA representation malformed"; + + if (o + cl >= dlen) + return "TXT rr RDATA too large"; + + memcpy(d + o, s + i, cl); + i += cl; + o += cl; + } + d[o] = '\0'; + if (strlen(d) != o) + return "TXT rr RDATA contains a NUL"; + + return NULL; +} + +static err_t +process_txt_rr(u_char *rdata, size_t rdlen +, bool doit /* should we capture information? */ +, enum dns_auth_level dns_auth_level +, struct adns_continuation *const cr) +{ + u_char str[RSA_MAX_ENCODING_BYTES * 8 / 6 + 20]; /* space for unpacked RDATA */ + + TRY(unpack_txt_rdata(str, sizeof(str), rdata, rdlen)); + return process_txt_rr_body(str, doit, dns_auth_level, cr); +} + +static err_t +process_answer_section(pb_stream *pbs +, bool doit /* should we capture information? */ +, enum dns_auth_level *dns_auth_level +, u_int16_t ancount /* number of RRs in the answer section */ +, struct adns_continuation *const cr) +{ + const int type = cr->query.type; /* type of RR of interest */ + unsigned c; + + DBG(DBG_DNS, DBG_log("*Answer Section:")); + + for (c = 0; c != ancount; c++) + { + struct rr_fixed rrf; + size_t tail; + + /* ??? do we need to match the name? */ + + TRY(eat_name_helpfully(pbs, "Answer Section")); + + if (!in_struct(&rrf, &rr_fixed_desc, pbs, NULL)) + return "failed to get fixed part of Answer Section Resource Record"; + + if (rrf.rdlength > pbs_left(pbs)) + return "RD Length extends beyond end of message"; + + /* ??? should we care about ttl? */ + + tail = rrf.rdlength; + + if (rrf.type == type && rrf.class == C_IN) + { + err_t ugh = NULL; + + switch (type) + { +#ifdef USE_KEYRR + case T_KEY: + ugh = process_key_rr(pbs->cur, tail, doit, *dns_auth_level, cr); + break; +#endif /* USE_KEYRR */ + case T_TXT: + ugh = process_txt_rr(pbs->cur, tail, doit, *dns_auth_level, cr); + break; + case T_SIG: + /* Check if SIG RR authenticates what we are learning. + * The RRset covered by a SIG must have the same owner, + * class, and type. + * For us, the class is always C_IN, so that matches. + * We decode the SIG RR's fixed part to check + * that the type_covered field matches our query type + * (this may be redundant). + * We don't check the owner (apparently this is the + * name on the record) -- we assume that it matches + * or we would not have been given this SIG in the + * Answer Section. + * + * We only look on first pass, and only if we've something + * to learn. This cuts down on useless decoding. + */ + if (!doit && *dns_auth_level == DAL_UNSIGNED) + { + struct sig_rdata sr; + + if (!in_struct(&sr, &sig_rdata_desc, pbs, NULL)) + ugh = "failed to get fixed part of SIG Resource Record RDATA"; + else if (sr.type_covered == type) + *dns_auth_level = DAL_SIGNED; + } + break; + default: + ugh = builddiag("unexpected RR type %d", type); + break; + } + if (ugh != NULL) + return ugh; + } + in_raw(NULL, tail, pbs, "RR RDATA"); + } + + return doit + && cr->gateways_from_dns == NULL +#ifdef USE_KEYRR + && cr->keys_from_dns == NULL +#endif /* USE_KEYRR */ + ? builddiag("no suitable %s record found in DNS", rr_typename(type)) + : NULL; +} + +/* process DNS answer -- TXT or KEY query */ + +static err_t +process_dns_answer(struct adns_continuation *const cr +, u_char ans[], int anslen) +{ + const int type = cr->query.type; /* type of record being sought */ + int r; /* all-purpose return value holder */ + u_int16_t c; /* number of current RR in current answer section */ + pb_stream pbs; + u_int8_t *ans_start; /* saved position of answer section */ + struct qr_header qr_header; + enum dns_auth_level dns_auth_level; + + init_pbs(&pbs, ans, anslen, "Query Response Message"); + + /* decode and check header */ + + if (!in_struct(&qr_header, &qr_header_desc, &pbs, NULL)) + return "malformed header"; + + /* ID: nothing to do with us */ + + /* stuff -- lots of things */ + if ((qr_header.stuff & QRS_QR) == 0) + return "not a response?!?"; + + if (((qr_header.stuff >> QRS_OPCODE_SHIFT) & QRS_OPCODE_MASK) != QRSO_QUERY) + return "unexpected opcode"; + + /* I don't think we care about AA */ + + if (qr_header.stuff & QRS_TC) + return "response truncated"; + + /* I don't think we care about RD, RA, or CD */ + + /* AD means "authentic data" */ + dns_auth_level = qr_header.stuff & QRS_AD? DAL_UNSIGNED : DAL_NOTSEC; + + if (qr_header.stuff & QRS_Z) + return "Z bit is not zero"; + + r = (qr_header.stuff >> QRS_RCODE_SHIFT) & QRS_RCODE_MASK; + if (r != 0) + return r < (int)elemsof(rcode_text)? rcode_text[r] : "unknown rcode"; + + if (qr_header.ancount == 0) + return builddiag("no %s RR found by DNS", rr_typename(type)); + + /* end of header checking */ + + /* Question Section processing */ + + /* 4.1.2. Question section format: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + + DBG(DBG_DNS, DBG_log("*Question Section:")); + + for (c = 0; c != qr_header.qdcount; c++) + { + struct qs_fixed qsf; + + TRY(eat_name_helpfully(&pbs, "Question Section")); + + if (!in_struct(&qsf, &qs_fixed_desc, &pbs, NULL)) + return "failed to get fixed part of Question Section"; + + if (qsf.qtype != type) + return "unexpected QTYPE in Question Section"; + + if (qsf.qclass != C_IN) + return "unexpected QCLASS in Question Section"; + } + + /* rest of sections are made up of Resource Records */ + + /* Answer Section processing -- error checking, noting T_SIG */ + + ans_start = pbs.cur; /* remember start of answer section */ + + TRY(process_answer_section(&pbs, FALSE, &dns_auth_level + , qr_header.ancount, cr)); + + /* Authority Section processing (just sanity checking) */ + + DBG(DBG_DNS, DBG_log("*Authority Section:")); + + for (c = 0; c != qr_header.nscount; c++) + { + struct rr_fixed rrf; + size_t tail; + + TRY(eat_name_helpfully(&pbs, "Authority Section")); + + if (!in_struct(&rrf, &rr_fixed_desc, &pbs, NULL)) + return "failed to get fixed part of Authority Section Resource Record"; + + if (rrf.rdlength > pbs_left(&pbs)) + return "RD Length extends beyond end of message"; + + /* ??? should we care about ttl? */ + + tail = rrf.rdlength; + + in_raw(NULL, tail, &pbs, "RR RDATA"); + } + + /* Additional Section processing (just sanity checking) */ + + DBG(DBG_DNS, DBG_log("*Additional Section:")); + + for (c = 0; c != qr_header.arcount; c++) + { + struct rr_fixed rrf; + size_t tail; + + TRY(eat_name_helpfully(&pbs, "Additional Section")); + + if (!in_struct(&rrf, &rr_fixed_desc, &pbs, NULL)) + return "failed to get fixed part of Additional Section Resource Record"; + + if (rrf.rdlength > pbs_left(&pbs)) + return "RD Length extends beyond end of message"; + + /* ??? should we care about ttl? */ + + tail = rrf.rdlength; + + in_raw(NULL, tail, &pbs, "RR RDATA"); + } + + /* done all sections */ + + /* ??? is padding legal, or can we complain if more left in record? */ + + /* process Answer Section again -- accept contents */ + + pbs.cur = ans_start; /* go back to start of answer section */ + + return process_answer_section(&pbs, TRUE, &dns_auth_level + , qr_header.ancount, cr); +} + +#endif /* ! USE_LWRES */ + + +/****************************************************************/ + +static err_t +build_dns_name(u_char name_buf[NS_MAXDNAME + 2] +, unsigned long serial USED_BY_DEBUG +, const struct id *id +, const char *typename USED_BY_DEBUG +, const char *gwname USED_BY_DEBUG) +{ + /* note: all end in "." to suppress relative searches */ + id = resolve_myid(id); + switch (id->kind) + { + case ID_IPV4_ADDR: + { + /* XXX: this is really ugly and only temporary until addrtot can + * generate the correct format + */ + const unsigned char *b; + size_t bl USED_BY_DEBUG = addrbytesptr(&id->ip_addr, &b); + + passert(bl == 4); + snprintf(name_buf, NS_MAXDNAME + 2, "%d.%d.%d.%d.in-addr.arpa." + , b[3], b[2], b[1], b[0]); + break; + } + + case ID_IPV6_ADDR: + { + /* ??? is this correct? */ + const unsigned char *b; + size_t bl; + u_char *op = name_buf; + static const char suffix[] = "IP6.INT."; + + for (bl = addrbytesptr(&id->ip_addr, &b); bl-- != 0; ) + { + if (op + 4 + sizeof(suffix) >= name_buf + NS_MAXDNAME + 1) + return "IPv6 reverse name too long"; + op += sprintf(op, "%x.%x.", b[bl] & 0xF, b[bl] >> 4); + } + strcpy(op, suffix); + break; + } + + case ID_FQDN: + /* strip trailing "." characters, then add one */ + { + size_t il = id->name.len; + + while (il > 0 && id->name.ptr[il - 1] == '.') + il--; + if (il > NS_MAXDNAME) + return "FQDN too long for domain name"; + + memcpy(name_buf, id->name.ptr, il); + strcpy(name_buf + il, "."); + } + break; + + default: + return "can only query DNS for key for ID that is a FQDN, IPV4_ADDR, or IPV6_ADDR"; + } + + DBG(DBG_CONTROL | DBG_DNS, DBG_log("DNS query %lu for %s for %s (gw: %s)" + , serial, typename, name_buf, gwname)); + return NULL; +} + +void +gw_addref(struct gw_info *gw) +{ + if (gw != NULL) + { + DBG(DBG_DNS, DBG_log("gw_addref: %p refcnt: %d++", gw, gw->refcnt)) + gw->refcnt++; + } +} + +void +gw_delref(struct gw_info **gwp) +{ + struct gw_info *gw = *gwp; + + if (gw != NULL) + { + DBG(DBG_DNS, DBG_log("gw_delref: %p refcnt: %d--", gw, gw->refcnt)); + + passert(gw->refcnt != 0); + gw->refcnt--; + if (gw->refcnt == 0) + { + free_id_content(&gw->client_id); + free_id_content(&gw->gw_id); + if (gw->gw_key_present) + unreference_key(&gw->key); + gw_delref(&gw->next); + pfree(gw); /* trickery could make this a tail-call */ + } + *gwp = NULL; + } +} + +static int adns_in_flight = 0; /* queries outstanding */ + +/* Start an asynchronous DNS query. + * + * For KEY record, the result will be a list in cr->keys_from_dns. + * For TXT records, the result will be a list in cr->gateways_from_dns. + * + * If sgw_id is null, only consider TXT records that specify an + * IP address for the gatway: we need this in the initiation case. + * + * If sgw_id is non-null, only consider TXT records that specify + * this id as the security gatway; this is useful to the Responder + * for confirming claims of gateways. + * + * Continuation cr gives information for continuing when the result shows up. + * + * Two kinds of errors must be handled: synchronous (immediate) + * and asynchronous. Synchronous errors are indicated by the returned + * value of start_adns_query; in this case, the continuation will + * have been freed and the continuation routine will not be called. + * Asynchronous errors are indicated by the ugh parameter passed to the + * continuation routine. + * + * After the continuation routine has completed, handle_adns_answer + * will free the continuation. The continuation routine should have + * freed any axiliary resources. + * + * Note: in the synchronous error case, start_adns_query will have + * freed the continuation; this means that the caller will have to + * be very careful to release any auxiliary resources that were in + * the continuation record without using the continuation record. + * + * Either there will be an error result passed to the continuation routine, + * or the results will be in cr->keys_from_dns or cr->gateways_from_dns. + * The result variables must by left NULL by the continutation routine. + * The continuation routine is responsible for establishing and + * disestablishing any logging context (whack_log_fd, cur_*). + */ + +static struct adns_continuation *continuations = NULL; /* newest of queue */ +static struct adns_continuation *next_query = NULL; /* oldest not sent */ + +static struct adns_continuation * +continuation_for_qtid(unsigned long qtid) +{ + struct adns_continuation *cr = NULL; + + if (qtid != 0) + for (cr = continuations; cr != NULL && cr->qtid != qtid; cr = cr->previous) + ; + return cr; +} + +static void +release_adns_continuation(struct adns_continuation *cr) +{ + passert(cr != next_query); + gw_delref(&cr->gateways_from_dns); +#ifdef USE_KEYRR + free_public_keys(&cr->keys_from_dns); +#endif /* USE_KEYRR */ + unshare_id_content(&cr->id); + unshare_id_content(&cr->sgw_id); + + /* unlink from doubly-linked list */ + if (cr->next == NULL) + { + passert(continuations == cr); + continuations = cr->previous; + } + else + { + passert(cr->next->previous == cr); + cr->next->previous = cr->previous; + } + + if (cr->previous != NULL) + { + passert(cr->previous->next == cr); + cr->previous->next = cr->next; + } + + pfree(cr); +} + +err_t +start_adns_query(const struct id *id /* domain to query */ +, const struct id *sgw_id /* if non-null, any accepted gw_info must match */ +, int type /* T_TXT or T_KEY, selecting rr type of interest */ +, cont_fn_t cont_fn +, struct adns_continuation *cr) +{ + static unsigned long qtid = 1; /* query transaction id; NOTE: static */ + const char *typename = rr_typename(type); + char gwidb[BUF_LEN]; + + if(adns_pid == 0 + && adns_restart_count < ADNS_RESTART_MAX) + { + plog("ADNS helper was not running. Restarting attempt %d",adns_restart_count); + init_adns(); + } + + + /* Splice this in at head of doubly-linked list of continuations. + * Note: this must be done before any release_adns_continuation(). + */ + cr->next = NULL; + cr->previous = continuations; + if (continuations != NULL) + { + passert(continuations->next == NULL); + continuations->next = cr; + } + continuations = cr; + + cr->qtid = qtid++; + cr->type = type; + cr->cont_fn = cont_fn; + cr->id = *id; + unshare_id_content(&cr->id); + cr->sgw_specified = sgw_id != NULL; + cr->sgw_id = cr->sgw_specified? *sgw_id : empty_id; + unshare_id_content(&cr->sgw_id); + cr->gateways_from_dns = NULL; +#ifdef USE_KEYRR + cr->keys_from_dns = NULL; +#endif /* USE_KEYRR */ + +#ifdef DEBUG + cr->debugging = cur_debugging; +#else + cr->debugging = LEMPTY; +#endif + + idtoa(&cr->sgw_id, gwidb, sizeof(gwidb)); + + zero(&cr->query); + + { + err_t ugh = build_dns_name(cr->query.name_buf, cr->qtid + , id, typename, gwidb); + + if (ugh != NULL) + { + release_adns_continuation(cr); + return ugh; + } + } + + if (next_query == NULL) + next_query = cr; + + unsent_ADNS_queries = TRUE; + + return NULL; +} + +/* send remaining ADNS queries (until pipe full or none left) + * + * This is a co-routine, so it uses static variables to + * preserve state across calls. + */ +bool unsent_ADNS_queries = FALSE; + +void +send_unsent_ADNS_queries(void) +{ + static const unsigned char *buf_end = NULL; /* NOTE STATIC */ + static const unsigned char *buf_cur = NULL; /* NOTE STATIC */ + + if (adns_qfd == NULL_FD) + return; /* nothing useful to do */ + + for (;;) + { + if (buf_cur != buf_end) + { + static int try = 0; /* NOTE STATIC */ + size_t n = buf_end - buf_cur; + ssize_t r = write(adns_qfd, buf_cur, n); + + if (r == -1) + { + switch (errno) + { + case EINTR: + continue; /* try again now */ + case EAGAIN: + DBG(DBG_DNS, DBG_log("EAGAIN writing to ADNS")); + break; /* try again later */ + default: + try++; + log_errno((e, "error %d writing DNS query", try)); + break; /* try again later */ + } + unsent_ADNS_queries = TRUE; + break; /* done! */ + } + else + { + passert(r >= 0); + try = 0; + buf_cur += r; + } + } + else + { + if (next_query == NULL) + { + unsent_ADNS_queries = FALSE; + break; /* done! */ + } + +#ifdef USE_LWRES + next_query->used = FALSE; + { + /* NOTE STATIC: */ + static unsigned char qbuf[LWDNSQ_CMDBUF_LEN + 1]; /* room for NUL */ + + snprintf(qbuf, sizeof(qbuf), "%s %lu %s\n" + , rr_typename(next_query->type) + , next_query->qtid + , next_query->query.name_buf); + DBG(DBG_DNS, DBG_log("lwdnsq query: %.*s", (int)(strlen(qbuf) - 1), qbuf)); + buf_cur = qbuf; + buf_end = qbuf + strlen(qbuf); + } +#else /* !USE_LWRES */ + next_query->query.debugging = next_query->debugging; + next_query->query.serial = next_query->qtid; + next_query->query.len = sizeof(next_query->query); + next_query->query.qmagic = ADNS_Q_MAGIC; + next_query->query.type = next_query->type; + buf_cur = (const void *)&next_query->query; + buf_end = buf_cur + sizeof(next_query->query); +#endif /* !USE_LWRES */ + next_query = next_query->next; + adns_in_flight++; + } + } +} + +#ifdef USE_LWRES +/* Process a line of lwdnsq answer. + * Returns with error message iff lwdnsq result is malformed. + * Most errors will be in DNS data and will be handled by cr->cont_fn. + */ +static err_t +process_lwdnsq_answer(char *ts) +{ + err_t ugh = NULL; + char *rest; + char *p; + char *endofnumber; + struct adns_continuation *cr = NULL; + unsigned long qtid; + time_t anstime; /* time of answer */ + char *atype; /* type of answer */ + long ttl; /* ttl of answer; int, but long for conversion */ + bool AuthenticatedData = FALSE; + static char scratch_null_str[] = ""; /* cannot be const, but isn't written */ + + /* query transaction id */ + rest = ts; + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq: answer missing query transaction ID"; + + qtid = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq: malformed query transaction ID"; + + cr = continuation_for_qtid(qtid); + if (qtid != 0 && cr == NULL) + return "lwdnsq: unrecognized qtid"; /* can't happen! */ + + /* time */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq: missing time"; + + anstime = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq: malformed time"; + + /* TTL */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq: missing TTL"; + + ttl = strtol(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq: malformed TTL"; + + /* type */ + atype = strsep(&rest, " \t"); + if (atype == NULL) + return "lwdnsq: missing type"; + + /* if rest is NULL, make it "", otherwise eat whitespace after type */ + rest = rest == NULL? scratch_null_str : rest + strspn(rest, " \t"); + + if (strncasecmp(atype, "AD-", 3) == 0) + { + AuthenticatedData = TRUE; + atype += 3; + } + + /* deal with each type */ + + if (cr == NULL) + { + /* we don't actually know which this applies to */ + return builddiag("lwdnsq: 0 qtid invalid with %s", atype); + } + else if (strcaseeq(atype, "START")) + { + /* ignore */ + } + else if (strcaseeq(atype, "DONE")) + { + if (!cr->used) + { + /* "no results returned by lwdnsq" should not happen */ + cr->cont_fn(cr + , cr->gateways_from_dns == NULL +#ifdef USE_KEYRR + && cr->keys_from_dns == NULL +#endif /* USE_KEYRR */ + ? "no results returned by lwdnsq" : NULL); + cr->used = TRUE; + } + reset_globals(); + release_adns_continuation(cr); + adns_in_flight--; + } + else if (strcaseeq(atype, "RETRY")) + { + if (!cr->used) + { + cr->cont_fn(cr, rest); + cr->used = TRUE; + } + } + else if (strcaseeq(atype, "FATAL")) + { + if (!cr->used) + { + cr->cont_fn(cr, rest); + cr->used = TRUE; + } + } + else if (strcaseeq(atype, "DNSSEC")) + { + /* ignore */ + } + else if (strcaseeq(atype, "NAME")) + { + /* ignore */ + } + else if (strcaseeq(atype, "TXT")) + { + char *end = rest + strlen(rest); + err_t txt_ugh; + + if (*rest == '"' && end[-1] == '"') + { + /* strip those pesky quotes */ + rest++; + *--end = '\0'; + } + + txt_ugh = process_txt_rr_body(rest + , TRUE + , AuthenticatedData? DAL_SIGNED : DAL_NOTSEC + , cr); + + if (txt_ugh != NULL) + { + DBG(DBG_DNS, + DBG_log("error processing TXT resource record (%s) while processing: %s" + , txt_ugh, rest)); + cr->cont_fn(cr, txt_ugh); + cr->used = TRUE; + } + } + else if (strcaseeq(atype, "SIG")) + { + /* record the SIG records for posterity */ + if (cr->last_info != NULL) + { + pfreeany(cr->last_info->dns_sig); + cr->last_info->dns_sig = clone_str(rest, "sigrecord"); + } + } + else if (strcaseeq(atype, "A")) + { + /* ignore */ + } + else if (strcaseeq(atype, "AAAA")) + { + /* ignore */ + } + else if (strcaseeq(atype, "CNAME")) + { + /* ignore */ + } + else if (strcaseeq(atype, "CNAMEFROM")) + { + /* ignore */ + } + else if (strcaseeq(atype, "PTR")) + { + /* ignore */ + } +#ifdef USE_KEYRR + else if (strcaseeq(atype, "KEY")) + { + err_t key_ugh = process_lwdnsq_key(rest + , AuthenticatedData? DAL_SIGNED : DAL_NOTSEC + , cr); + + if (key_ugh != NULL) + { + DBG(DBG_DNS, + DBG_log("error processing KEY resource record (%s) while processing: %s" + , key_ugh, rest)); + cr->cont_fn(cr, key_ugh); + cr->used = TRUE; + } + } +#endif /* USE_KEYRR */ + else + { + ugh = "lwdnsq: unrecognized type"; + } + return ugh; +} +#endif /* USE_LWRES */ + +static void +recover_adns_die(void) +{ + struct adns_continuation *cr = NULL; + + adns_pid = 0; + if(adns_restart_count < ADNS_RESTART_MAX) { + adns_restart_count++; + + /* next DNS query will restart it */ + + /* we have to walk the list of the outstanding requests, + * and redo them! + */ + + cr = continuations; + + /* find the head of the list */ + if(continuations != NULL) { + for (; cr->previous != NULL; cr = cr->previous); + } + + next_query = cr; + + if(next_query != NULL) { + unsent_ADNS_queries = TRUE; + } + } +} + +void reset_adns_restart_count(void) +{ + adns_restart_count=0; +} + +void +handle_adns_answer(void) +{ + /* These are retained across calls to handle_adns_answer. */ + static size_t buflen = 0; /* bytes in answer buffer */ +#ifndef USE_LWRES + static struct adns_answer buf; +#else /* USE_LWRES */ + static char buf[LWDNSQ_RESULT_LEN_MAX]; + static char buf_copy[LWDNSQ_RESULT_LEN_MAX]; +#endif /* USE_LWRES */ + + ssize_t n; + + passert(buflen < sizeof(buf)); + n = read(adns_afd, (unsigned char *)&buf + buflen, sizeof(buf) - buflen); + + if (n < 0) + { + if (errno != EINTR) + { + log_errno((e, "error reading answer from adns")); + /* ??? how can we recover? */ + } + n = 0; /* now n reflects amount read */ + } + else if (n == 0) + { + /* EOF */ + if (adns_in_flight != 0) + { + plog("EOF from ADNS with %d queries outstanding (restarts %d)" + , adns_in_flight, adns_restart_count); + recover_adns_die(); + } + if (buflen != 0) + { + plog("EOF from ADNS with %lu bytes of a partial answer outstanding" + "(restarts %d)" + , (unsigned long)buflen + , adns_restart_count); + recover_adns_die(); + } + stop_adns(); + return; + } + else + { + passert(adns_in_flight > 0); + } + + buflen += n; +#ifndef USE_LWRES + while (buflen >= offsetof(struct adns_answer, ans) && buflen >= buf.len) + { + /* we've got a tasty answer -- process it */ + err_t ugh; + struct adns_continuation *cr = continuation_for_qtid(buf.serial); /* assume it works */ + const char *typename = rr_typename(cr->query.type); + const char *name_buf = cr->query.name_buf; + +#ifdef USE_KEYRR + passert(cr->keys_from_dns == NULL); +#endif /* USE_KEYRR */ + passert(cr->gateways_from_dns == NULL); + adns_in_flight--; + if (buf.result == -1) + { + /* newer resolvers support statp->res_h_errno as well as h_errno. + * That might be better, but older resolvers don't. + * See resolver(3), if you have it. + * The undocumented(!) h_errno values are defined in + * /usr/include/netdb.h. + */ + switch (buf.h_errno_val) + { + case NO_DATA: + ugh = builddiag("no %s record for %s", typename, name_buf); + break; + case HOST_NOT_FOUND: + ugh = builddiag("no host %s for %s record", name_buf, typename); + break; + default: + ugh = builddiag("failure querying DNS for %s of %s: %s" + , typename, name_buf, hstrerror(buf.h_errno_val)); + break; + } + } + else if (buf.result > (int) sizeof(buf.ans)) + { + ugh = builddiag("(INTERNAL ERROR) answer too long (%ld) for buffer" + , (long)buf.result); + } + else + { + ugh = process_dns_answer(cr, buf.ans, buf.result); + if (ugh != NULL) + ugh = builddiag("failure processing %s record of DNS answer for %s: %s" + , typename, name_buf, ugh); + } + DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL | DBG_DNS, + DBG_log(BLANK_FORMAT); + if (ugh == NULL) + DBG_log("asynch DNS answer %lu for %s of %s" + , cr->query.serial, typename, name_buf); + else + DBG_log("asynch DNS answer %lu %s", cr->query.serial, ugh); + ); + + passert(GLOBALS_ARE_RESET()); + cr->cont_fn(cr, ugh); + reset_globals(); + release_adns_continuation(cr); + + /* shift out answer that we've consumed */ + buflen -= buf.len; + memmove((unsigned char *)&buf, (unsigned char *)&buf + buf.len, buflen); + } +#else /* USE_LWRES */ + for (;;) + { + err_t ugh; + char *nlp = memchr(buf, '\n', buflen); + + if (nlp == NULL) + break; + + /* we've got a line */ + *nlp++ = '\0'; + + DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL | DBG_DNS + , DBG_log("lwdns: %s", buf)); + + /* process lwdnsq_answer may modify buf, so make a copy. */ + buf_copy[0]='\0'; + strncat(buf_copy, buf, sizeof(buf_copy)); + + ugh = process_lwdnsq_answer(buf_copy); + if (ugh != NULL) + plog("failure processing lwdnsq output: %s; record: %s" + , ugh, buf); + + passert(GLOBALS_ARE_RESET()); + reset_globals(); + + /* shift out answer that we've consumed */ + buflen -= nlp - buf; + memmove(buf, nlp, buflen); + } +#endif /* USE_LWRES */ +} diff --git a/src/pluto/dnskey.h b/src/pluto/dnskey.h new file mode 100644 index 000000000..0b9f0ee33 --- /dev/null +++ b/src/pluto/dnskey.h @@ -0,0 +1,84 @@ +/* Find public key in DNS + * Copyright (C) 2000-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: dnskey.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +extern int + adns_qfd, /* file descriptor for sending queries to adns */ + adns_afd; /* file descriptor for receiving answers from adns */ +extern const char *pluto_adns_option; /* path from --pluto_adns */ +extern void init_adns(void); +extern void stop_adns(void); +extern void handle_adns_answer(void); + +extern bool unsent_ADNS_queries; +extern void send_unsent_ADNS_queries(void); + +/* (common prefix of) stuff remembered between async query and answer. + * Filled in by start_adns_query. + * Freed by call to release_adns_continuation. + */ + +struct adns_continuation; /* forward declaration (not far!) */ + +typedef void (*cont_fn_t)(struct adns_continuation *cr, err_t ugh); + +struct adns_continuation { + unsigned long qtid; /* query transaction id number */ + int type; /* T_TXT or T_KEY, selecting rr type of interest */ + cont_fn_t cont_fn; /* function to carry on suspended work */ + struct id id; /* subject of query */ + bool sgw_specified; + struct id sgw_id; /* peer, if constrained */ + lset_t debugging; /* only used #ifdef DEBUG, but don't want layout to change */ + struct gw_info *gateways_from_dns; /* answer, if looking for our TXT rrs */ +#ifdef USE_KEYRR + struct pubkey_list *keys_from_dns; /* answer, if looking for KEY rrs */ +#endif + struct adns_continuation *previous, *next; + struct pubkey *last_info; /* the last structure we accumulated */ +#ifdef USE_LWRES + bool used; /* have we called the cont_fn yet? */ + struct { + u_char name_buf[NS_MAXDNAME + 2]; + } query; +#else /* ! USE_LWRES */ + struct adns_query query; +#endif /* ! USE_LWRES */ +}; + +extern err_t start_adns_query(const struct id *id /* domain to query */ + , const struct id *sgw_id /* if non-null, any accepted gw_info must match */ + , int type /* T_TXT or T_KEY, selecting rr type of interest */ + , cont_fn_t cont_fn /* continuation function */ + , struct adns_continuation *cr); + + +/* Gateway info gleaned from reverse DNS of client */ +struct gw_info { + unsigned refcnt; /* reference counted! */ + unsigned pref; /* preference: lower is better */ +#define NO_TIME ((time_t) -2) /* time_t value meaning "not_yet" */ + struct id client_id; /* id of client of peer */ + struct id gw_id; /* id of peer (if id_is_ipaddr, .ip_addr is address) */ + bool gw_key_present; + struct pubkey *key; + struct gw_info *next; +}; + +extern void gw_addref(struct gw_info *gw) + , gw_delref(struct gw_info **gwp); + +extern void reset_adns_restart_count(void); + diff --git a/src/pluto/dsa.c b/src/pluto/dsa.c new file mode 100644 index 000000000..c5982fbf4 --- /dev/null +++ b/src/pluto/dsa.c @@ -0,0 +1,476 @@ +/* dsa.c - DSA signature scheme + * Copyright (C) 1998 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifdef PLUTO +#include +#include +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "rnd.h" +#include "gcryptfix.h" +#else /*! PLUTO */ +/* #include */ +#endif /* !PLUTO */ + +#include +#include +#include + +#ifndef PLUTO +/* #include */ +/* #include "util.h" */ +/* #include "mpi.h" */ +/* #include "cipher.h" */ +#endif + +#include "dsa.h" + +typedef struct { + MPI p; /* prime */ + MPI q; /* group order */ + MPI g; /* group generator */ + MPI y; /* g^x mod p */ +} DSA_public_key; + + +typedef struct { + MPI p; /* prime */ + MPI q; /* group order */ + MPI g; /* group generator */ + MPI y; /* g^x mod p */ + MPI x; /* secret exponent */ +} DSA_secret_key; + + +static MPI gen_k( MPI q ); +static void test_keys( DSA_secret_key *sk, unsigned qbits ); +static int check_secret_key( DSA_secret_key *sk ); +static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ); +static void sign(MPI r, MPI s, MPI input, DSA_secret_key *skey); +static int verify(MPI r, MPI s, MPI input, DSA_public_key *pkey); + +static void +progress( int c ) +{ + fputc( c, stderr ); +} + + +/**************** + * Generate a random secret exponent k less than q + */ +static MPI +gen_k( MPI q ) +{ + MPI k = mpi_alloc_secure( mpi_get_nlimbs(q) ); + unsigned int nbits = mpi_get_nbits(q); + unsigned int nbytes = (nbits+7)/8; + char *rndbuf = NULL; + + if( DBG_CIPHER ) + log_debug("choosing a random k "); + for(;;) { + if( DBG_CIPHER ) + progress('.'); + + if( !rndbuf || nbits < 32 ) { + m_free(rndbuf); + rndbuf = get_random_bits( nbits, 1, 1 ); + } + else { /* change only some of the higher bits */ + /* we could imporove this by directly requesting more memory + * at the first call to get_random_bits() and use this the here + * maybe it is easier to do this directly in random.c */ + char *pp = get_random_bits( 32, 1, 1 ); + memcpy( rndbuf,pp, 4 ); + m_free(pp); + } + mpi_set_buffer( k, rndbuf, nbytes, 0 ); + if( mpi_test_bit( k, nbits-1 ) ) + mpi_set_highbit( k, nbits-1 ); + else { + mpi_set_highbit( k, nbits-1 ); + mpi_clear_bit( k, nbits-1 ); + } + + if( !(mpi_cmp( k, q ) < 0) ) { /* check: k < q */ + if( DBG_CIPHER ) + progress('+'); + continue; /* no */ + } + if( !(mpi_cmp_ui( k, 0 ) > 0) ) { /* check: k > 0 */ + if( DBG_CIPHER ) + progress('-'); + continue; /* no */ + } + break; /* okay */ + } + m_free(rndbuf); + if( DBG_CIPHER ) + progress('\n'); + + return k; +} + + +static void +test_keys( DSA_secret_key *sk, unsigned qbits ) +{ + DSA_public_key pk; + MPI test = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); + MPI out1_a = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); + MPI out1_b = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); + + pk.p = sk->p; + pk.q = sk->q; + pk.g = sk->g; + pk.y = sk->y; + /*mpi_set_bytes( test, qbits, get_random_byte, 0 );*/ + { char *p = get_random_bits( qbits, 0, 0 ); + mpi_set_buffer( test, p, (qbits+7)/8, 0 ); + m_free(p); + } + + sign( out1_a, out1_b, test, sk ); + if( !verify( out1_a, out1_b, test, &pk ) ) + log_fatal("DSA:: sign, verify failed\n"); + + mpi_free( test ); + mpi_free( out1_a ); + mpi_free( out1_b ); +} + + + +/**************** + * Generate a DSA key pair with a key of size NBITS + * Returns: 2 structures filled with all needed values + * and an array with the n-1 factors of (p-1) + */ +static void +generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ) +{ + MPI p; /* the prime */ + MPI q; /* the 160 bit prime factor */ + MPI g; /* the generator */ + MPI y; /* g^x mod p */ + MPI x; /* the secret exponent */ + MPI h, e; /* helper */ + unsigned qbits; + byte *rndbuf; + + assert( nbits >= 512 && nbits <= 1024 ); + + qbits = 160; + p = generate_elg_prime( 1, nbits, qbits, NULL, ret_factors ); + /* get q out of factors */ + q = mpi_copy((*ret_factors)[0]); + if( mpi_get_nbits(q) != qbits ) + BUG(); + + /* find a generator g (h and e are helpers)*/ + /* e = (p-1)/q */ + e = mpi_alloc( mpi_get_nlimbs(p) ); + mpi_sub_ui( e, p, 1 ); + mpi_fdiv_q( e, e, q ); + g = mpi_alloc( mpi_get_nlimbs(p) ); + h = mpi_alloc_set_ui( 1 ); /* we start with 2 */ + do { + mpi_add_ui( h, h, 1 ); + /* g = h^e mod p */ + mpi_powm( g, h, e, p ); + } while( !mpi_cmp_ui( g, 1 ) ); /* continue until g != 1 */ + + /* select a random number which has these properties: + * 0 < x < q-1 + * This must be a very good random number because this + * is the secret part. */ + if( DBG_CIPHER ) + log_debug("choosing a random x "); + assert( qbits >= 160 ); + x = mpi_alloc_secure( mpi_get_nlimbs(q) ); + mpi_sub_ui( h, q, 1 ); /* put q-1 into h */ + rndbuf = NULL; + do { + if( DBG_CIPHER ) + progress('.'); + if( !rndbuf ) + rndbuf = get_random_bits( qbits, 2, 1 ); + else { /* change only some of the higher bits (= 2 bytes)*/ + char *r = get_random_bits( 16, 2, 1 ); + memcpy(rndbuf, r, 16/8 ); + m_free(r); + } + mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 ); + mpi_clear_highbit( x, qbits+1 ); + } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) ); + m_free(rndbuf); + mpi_free( e ); + mpi_free( h ); + + /* y = g^x mod p */ + y = mpi_alloc( mpi_get_nlimbs(p) ); + mpi_powm( y, g, x, p ); + + if( DBG_CIPHER ) { + progress('\n'); + log_mpidump("dsa p= ", p ); + log_mpidump("dsa q= ", q ); + log_mpidump("dsa g= ", g ); + log_mpidump("dsa y= ", y ); + log_mpidump("dsa x= ", x ); + } + + /* copy the stuff to the key structures */ + sk->p = p; + sk->q = q; + sk->g = g; + sk->y = y; + sk->x = x; + + /* now we can test our keys (this should never fail!) */ + test_keys( sk, qbits ); +} + + + +/**************** + * Test whether the secret key is valid. + * Returns: if this is a valid key. + */ +static int +check_secret_key( DSA_secret_key *sk ) +{ + int rc; + MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); + + mpi_powm( y, sk->g, sk->x, sk->p ); + rc = !mpi_cmp( y, sk->y ); + mpi_free( y ); + return rc; +} + + + +/**************** + * Make a DSA signature from HASH and put it into r and s. + */ + +static void +sign(MPI r, MPI s, MPI hash, DSA_secret_key *skey ) +{ + MPI k; + MPI kinv; + MPI tmp; + + /* select a random k with 0 < k < q */ + k = gen_k( skey->q ); + + /* r = (a^k mod p) mod q */ + mpi_powm( r, skey->g, k, skey->p ); + mpi_fdiv_r( r, r, skey->q ); + + /* kinv = k^(-1) mod q */ + kinv = mpi_alloc( mpi_get_nlimbs(k) ); + mpi_invm(kinv, k, skey->q ); + + /* s = (kinv * ( hash + x * r)) mod q */ + tmp = mpi_alloc( mpi_get_nlimbs(skey->p) ); + mpi_mul( tmp, skey->x, r ); + mpi_add( tmp, tmp, hash ); + mpi_mulm( s , kinv, tmp, skey->q ); + + mpi_free(k); + mpi_free(kinv); + mpi_free(tmp); +} + + +/**************** + * Returns true if the signature composed from R and S is valid. + */ +static int +verify(MPI r, MPI s, MPI hash, DSA_public_key *pkey ) +{ + int rc; + MPI w, u1, u2, v; + MPI base[3]; + MPI exp[3]; + + + if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) ) + return 0; /* assertion 0 < r < q failed */ + if( !(mpi_cmp_ui( s, 0 ) > 0 && mpi_cmp( s, pkey->q ) < 0) ) + return 0; /* assertion 0 < s < q failed */ + + w = mpi_alloc( mpi_get_nlimbs(pkey->q) ); + u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); + u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); + v = mpi_alloc( mpi_get_nlimbs(pkey->p) ); + + /* w = s^(-1) mod q */ + mpi_invm( w, s, pkey->q ); + + /* u1 = (hash * w) mod q */ + mpi_mulm( u1, hash, w, pkey->q ); + + /* u2 = r * w mod q */ + mpi_mulm( u2, r, w, pkey->q ); + + /* v = g^u1 * y^u2 mod p mod q */ + base[0] = pkey->g; exp[0] = u1; + base[1] = pkey->y; exp[1] = u2; + base[2] = NULL; exp[2] = NULL; + mpi_mulpowm( v, base, exp, pkey->p ); + mpi_fdiv_r( v, v, pkey->q ); + + rc = !mpi_cmp( v, r ); + + mpi_free(w); + mpi_free(u1); + mpi_free(u2); + mpi_free(v); + return rc; +} + + +/********************************************* + ************** interface ****************** + *********************************************/ + +int +dsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) +{ + DSA_secret_key sk; + + if( algo != PUBKEY_ALGO_DSA ) + return G10ERR_PUBKEY_ALGO; + + generate( &sk, nbits, retfactors ); + skey[0] = sk.p; + skey[1] = sk.q; + skey[2] = sk.g; + skey[3] = sk.y; + skey[4] = sk.x; + return 0; +} + + +int +dsa_check_secret_key( int algo, MPI *skey ) +{ + DSA_secret_key sk; + + if( algo != PUBKEY_ALGO_DSA ) + return G10ERR_PUBKEY_ALGO; + if( !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] ) + return G10ERR_BAD_MPI; + + sk.p = skey[0]; + sk.q = skey[1]; + sk.g = skey[2]; + sk.y = skey[3]; + sk.x = skey[4]; + if( !check_secret_key( &sk ) ) + return G10ERR_BAD_SECKEY; + + return 0; +} + + + +int +dsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) +{ + DSA_secret_key sk; + + if( algo != PUBKEY_ALGO_DSA ) + return G10ERR_PUBKEY_ALGO; + if( !data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] ) + return G10ERR_BAD_MPI; + + sk.p = skey[0]; + sk.q = skey[1]; + sk.g = skey[2]; + sk.y = skey[3]; + sk.x = skey[4]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); + resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); + sign( resarr[0], resarr[1], data, &sk ); + return 0; +} + +int +dsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *, MPI) UNUSED, void *opaquev UNUSED) +{ + DSA_public_key pk; + + if( algo != PUBKEY_ALGO_DSA ) + return G10ERR_PUBKEY_ALGO; + if( !data[0] || !data[1] || !hash + || !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] ) + return G10ERR_BAD_MPI; + + pk.p = pkey[0]; + pk.q = pkey[1]; + pk.g = pkey[2]; + pk.y = pkey[3]; + if( !verify( data[0], data[1], hash, &pk ) ) + return G10ERR_BAD_SIGN; + return 0; +} + + + +unsigned +dsa_get_nbits( int algo, MPI *pkey ) +{ + if( algo != PUBKEY_ALGO_DSA ) + return 0; + return mpi_get_nbits( pkey[0] ); +} + + +/**************** + * Return some information about the algorithm. We need algo here to + * distinguish different flavors of the algorithm. + * Returns: A pointer to string describing the algorithm or NULL if + * the ALGO is invalid. + * Usage: Bit 0 set : allows signing + * 1 set : allows encryption + */ +const char * +dsa_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, + int *use ) +{ + *npkey = 4; + *nskey = 5; + *nenc = 0; + *nsig = 2; + + switch( algo ) { + case PUBKEY_ALGO_DSA: *use = PUBKEY_USAGE_SIG; return "DSA"; + default: *use = 0; return NULL; + } +} + + diff --git a/src/pluto/dsa.h b/src/pluto/dsa.h new file mode 100644 index 000000000..1456d65b6 --- /dev/null +++ b/src/pluto/dsa.h @@ -0,0 +1,32 @@ +/* dsa.h - DSA signature scheme + * Copyright (C) 1998 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef G10_DSA_H +#define G10_DSA_H + +int dsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ); +int dsa_check_secret_key( int algo, MPI *skey ); +int dsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ); +int dsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *, MPI), void *opaquev ); +unsigned dsa_get_nbits( int algo, MPI *pkey ); +const char *dsa_get_info( int algo, int *npkey, int *nskey, + int *nenc, int *nsig, int *use ); + +#endif /*G10_DSA_H*/ diff --git a/src/pluto/elgamal.c b/src/pluto/elgamal.c new file mode 100644 index 000000000..0c099bb90 --- /dev/null +++ b/src/pluto/elgamal.c @@ -0,0 +1,613 @@ +/* elgamal.c - ElGamal Public Key encryption + * Copyright (C) 1998 Free Software Foundation, Inc. + * + * For a description of the algorithm, see: + * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996. + * ISBN 0-471-11709-9. Pages 476 ff. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifdef PLUTO +#include +#include +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "rnd.h" +#include "gcryptfix.h" +#else /*! PLUTO */ +/* #include */ +#endif /* !PLUTO */ + +#include +#include +#include + +#ifndef PLUTO +/* #include "util.h" */ +/* #include "mpi.h" */ +/* #include "cipher.h" */ +#endif + +#include "elgamal.h" + +typedef struct { + MPI p; /* prime */ + MPI g; /* group generator */ + MPI y; /* g^x mod p */ +} ELG_public_key; + + +typedef struct { + MPI p; /* prime */ + MPI g; /* group generator */ + MPI y; /* g^x mod p */ + MPI x; /* secret exponent */ +} ELG_secret_key; + + +static void test_keys( ELG_secret_key *sk, unsigned nbits ); +static MPI gen_k( MPI p ); +static void generate( ELG_secret_key *sk, unsigned nbits, MPI **factors ); +static int check_secret_key( ELG_secret_key *sk ); +static void encrypt(MPI a, MPI b, MPI input, ELG_public_key *pkey ); +static void decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ); +static void sign(MPI a, MPI b, MPI input, ELG_secret_key *skey); +static int verify(MPI a, MPI b, MPI input, ELG_public_key *pkey); + + +static void +progress( int c ) +{ + fputc( c, stderr ); +} + + +static void +test_keys( ELG_secret_key *sk, unsigned nbits ) +{ + ELG_public_key pk; + MPI test = mpi_alloc( 0 ); + MPI out1_a = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); + MPI out1_b = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); + MPI out2 = mpi_alloc( nbits / BITS_PER_MPI_LIMB ); + + pk.p = sk->p; + pk.g = sk->g; + pk.y = sk->y; + + /*mpi_set_bytes( test, nbits, get_random_byte, 0 );*/ + { char *p = get_random_bits( nbits, 0, 0 ); + mpi_set_buffer( test, p, (nbits+7)/8, 0 ); + m_free(p); + } + + encrypt( out1_a, out1_b, test, &pk ); + decrypt( out2, out1_a, out1_b, sk ); + if( mpi_cmp( test, out2 ) ) + log_fatal("ElGamal operation: encrypt, decrypt failed\n"); + + sign( out1_a, out1_b, test, sk ); + if( !verify( out1_a, out1_b, test, &pk ) ) + log_fatal("ElGamal operation: sign, verify failed\n"); + + mpi_free( test ); + mpi_free( out1_a ); + mpi_free( out1_b ); + mpi_free( out2 ); +} + + +/**************** + * generate a random secret exponent k from prime p, so + * that k is relatively prime to p-1 + */ +static MPI +gen_k( MPI p ) +{ + MPI k = mpi_alloc_secure( 0 ); + MPI temp = mpi_alloc( mpi_get_nlimbs(p) ); + MPI p_1 = mpi_copy(p); + unsigned int nbits = mpi_get_nbits(p); + unsigned int nbytes = (nbits+7)/8; + char *rndbuf = NULL; + + if( DBG_CIPHER ) + log_debug("choosing a random k "); + mpi_sub_ui( p_1, p, 1); + for(;;) { + if( DBG_CIPHER ) + progress('.'); + if( !rndbuf || nbits < 32 ) { + m_free(rndbuf); + rndbuf = get_random_bits( nbits, 1, 1 ); + } + else { /* change only some of the higher bits */ + /* we could imporove this by directly requesting more memory + * at the first call to get_random_bits() and use this the here + * maybe it is easier to do this directly in random.c */ + char *pp = get_random_bits( 32, 1, 1 ); + memcpy( rndbuf,pp, 4 ); + m_free(pp); + } + mpi_set_buffer( k, rndbuf, nbytes, 0 ); + + for(;;) { + /* make sure that the number is of the exact lenght */ + if( mpi_test_bit( k, nbits-1 ) ) + mpi_set_highbit( k, nbits-1 ); + else { + mpi_set_highbit( k, nbits-1 ); + mpi_clear_bit( k, nbits-1 ); + } + if( !(mpi_cmp( k, p_1 ) < 0) ) { /* check: k < (p-1) */ + if( DBG_CIPHER ) + progress('+'); + break; /* no */ + } + if( !(mpi_cmp_ui( k, 0 ) > 0) ) { /* check: k > 0 */ + if( DBG_CIPHER ) + progress('-'); + break; /* no */ + } + if( mpi_gcd( temp, k, p_1 ) ) + goto found; /* okay, k is relatively prime to (p-1) */ + mpi_add_ui( k, k, 1 ); + } + } + found: + m_free(rndbuf); + if( DBG_CIPHER ) + progress('\n'); + mpi_free(p_1); + mpi_free(temp); + + return k; +} + +/**************** + * Generate a key pair with a key of size NBITS + * Returns: 2 structures filles with all needed values + * and an array with n-1 factors of (p-1) + */ +static void +generate( ELG_secret_key *sk, unsigned nbits, MPI **ret_factors ) +{ + MPI p; /* the prime */ + MPI p_min1; + MPI g; + MPI x; /* the secret exponent */ + MPI y; + MPI temp; + unsigned qbits; + byte *rndbuf; + + p_min1 = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + temp = mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + if( nbits < 512 ) + qbits = 120; + else if( nbits <= 1024 ) + qbits = 160; + else if( nbits <= 2048 ) + qbits = 200; + else + qbits = 240; + g = mpi_alloc(1); + p = generate_elg_prime( 0, nbits, qbits, g, ret_factors ); + mpi_sub_ui(p_min1, p, 1); + + + /* select a random number which has these properties: + * 0 < x < p-1 + * This must be a very good random number because this is the + * secret part. The prime is public and may be shared anyway, + * so a random generator level of 1 is used for the prime. + */ + x = mpi_alloc_secure( nbits/BITS_PER_MPI_LIMB ); + if( DBG_CIPHER ) + log_debug("choosing a random x "); + rndbuf = NULL; + do { + if( DBG_CIPHER ) + progress('.'); + if( rndbuf ) { /* change only some of the higher bits */ + if( nbits < 16 ) {/* should never happen ... */ + m_free(rndbuf); + rndbuf = get_random_bits( nbits, 2, 1 ); + } + else { + char *r = get_random_bits( 16, 2, 1 ); + memcpy(rndbuf, r, 16/8 ); + m_free(r); + } + } + else + rndbuf = get_random_bits( nbits, 2, 1 ); + mpi_set_buffer( x, rndbuf, (nbits+7)/8, 0 ); + mpi_clear_highbit( x, nbits+1 ); + } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, p_min1 )<0 ) ); + m_free(rndbuf); + + y = mpi_alloc(nbits/BITS_PER_MPI_LIMB); + mpi_powm( y, g, x, p ); + + if( DBG_CIPHER ) { + progress('\n'); + log_mpidump("elg p= ", p ); + log_mpidump("elg g= ", g ); + log_mpidump("elg y= ", y ); + log_mpidump("elg x= ", x ); + } + + /* copy the stuff to the key structures */ + sk->p = p; + sk->g = g; + sk->y = y; + sk->x = x; + + /* now we can test our keys (this should never fail!) */ + test_keys( sk, nbits - 64 ); + + mpi_free( p_min1 ); + mpi_free( temp ); +} + + +/**************** + * Test whether the secret key is valid. + * Returns: if this is a valid key. + */ +static int +check_secret_key( ELG_secret_key *sk ) +{ + int rc; + MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); + + mpi_powm( y, sk->g, sk->x, sk->p ); + rc = !mpi_cmp( y, sk->y ); + mpi_free( y ); + return rc; +} + + +static void +encrypt(MPI a, MPI b, MPI input, ELG_public_key *pkey ) +{ + MPI k; + + /* Note: maybe we should change the interface, so that it + * is possible to check that input is < p and return an + * error code. + */ + + k = gen_k( pkey->p ); + mpi_powm( a, pkey->g, k, pkey->p ); + /* b = (y^k * input) mod p + * = ((y^k mod p) * (input mod p)) mod p + * and because input is < p + * = ((y^k mod p) * input) mod p + */ + mpi_powm( b, pkey->y, k, pkey->p ); + mpi_mulm( b, b, input, pkey->p ); + #if 0 + if( DBG_CIPHER ) { + log_mpidump("elg encrypted y= ", pkey->y); + log_mpidump("elg encrypted p= ", pkey->p); + log_mpidump("elg encrypted k= ", k); + log_mpidump("elg encrypted M= ", input); + log_mpidump("elg encrypted a= ", a); + log_mpidump("elg encrypted b= ", b); + } + #endif + mpi_free(k); +} + + + + +static void +decrypt(MPI output, MPI a, MPI b, ELG_secret_key *skey ) +{ + MPI t1 = mpi_alloc_secure( mpi_get_nlimbs( skey->p ) ); + + /* output = b/(a^x) mod p */ + + mpi_powm( t1, a, skey->x, skey->p ); + mpi_invm( t1, t1, skey->p ); + mpi_mulm( output, b, t1, skey->p ); + #if 0 + if( DBG_CIPHER ) { + log_mpidump("elg decrypted x= ", skey->x); + log_mpidump("elg decrypted p= ", skey->p); + log_mpidump("elg decrypted a= ", a); + log_mpidump("elg decrypted b= ", b); + log_mpidump("elg decrypted M= ", output); + } + #endif + mpi_free(t1); +} + + +/**************** + * Make an Elgamal signature out of INPUT + */ + +static void +sign(MPI a, MPI b, MPI input, ELG_secret_key *skey ) +{ + MPI k; + MPI t = mpi_alloc( mpi_get_nlimbs(a) ); + MPI inv = mpi_alloc( mpi_get_nlimbs(a) ); + MPI p_1 = mpi_copy(skey->p); + + /* + * b = (t * inv) mod (p-1) + * b = (t * inv(k,(p-1),(p-1)) mod (p-1) + * b = (((M-x*a) mod (p-1)) * inv(k,(p-1),(p-1))) mod (p-1) + * + */ + mpi_sub_ui(p_1, p_1, 1); + k = gen_k( skey->p ); + mpi_powm( a, skey->g, k, skey->p ); + mpi_mul(t, skey->x, a ); + mpi_subm(t, input, t, p_1 ); + while( mpi_is_neg(t) ) + mpi_add(t, t, p_1); + mpi_invm(inv, k, p_1 ); + mpi_mulm(b, t, inv, p_1 ); + + #if 0 + if( DBG_CIPHER ) { + log_mpidump("elg sign p= ", skey->p); + log_mpidump("elg sign g= ", skey->g); + log_mpidump("elg sign y= ", skey->y); + log_mpidump("elg sign x= ", skey->x); + log_mpidump("elg sign k= ", k); + log_mpidump("elg sign M= ", input); + log_mpidump("elg sign a= ", a); + log_mpidump("elg sign b= ", b); + } + #endif + mpi_free(k); + mpi_free(t); + mpi_free(inv); + mpi_free(p_1); +} + + +/**************** + * Returns true if the signature composed of A and B is valid. + */ +static int +verify(MPI a, MPI b, MPI input, ELG_public_key *pkey ) +{ + int rc; + MPI t1; + MPI t2; + MPI base[4]; + MPI exp[4]; + + if( !(mpi_cmp_ui( a, 0 ) > 0 && mpi_cmp( a, pkey->p ) < 0) ) + return 0; /* assertion 0 < a < p failed */ + + t1 = mpi_alloc( mpi_get_nlimbs(a) ); + t2 = mpi_alloc( mpi_get_nlimbs(a) ); + + #if 0 + /* t1 = (y^a mod p) * (a^b mod p) mod p */ + mpi_powm( t1, pkey->y, a, pkey->p ); + mpi_powm( t2, a, b, pkey->p ); + mpi_mulm( t1, t1, t2, pkey->p ); + + /* t2 = g ^ input mod p */ + mpi_powm( t2, pkey->g, input, pkey->p ); + + rc = !mpi_cmp( t1, t2 ); + #elif 0 + /* t1 = (y^a mod p) * (a^b mod p) mod p */ + base[0] = pkey->y; exp[0] = a; + base[1] = a; exp[1] = b; + base[2] = NULL; exp[2] = NULL; + mpi_mulpowm( t1, base, exp, pkey->p ); + + /* t2 = g ^ input mod p */ + mpi_powm( t2, pkey->g, input, pkey->p ); + + rc = !mpi_cmp( t1, t2 ); + #else + /* t1 = g ^ - input * y ^ a * a ^ b mod p */ + mpi_invm(t2, pkey->g, pkey->p ); + base[0] = t2 ; exp[0] = input; + base[1] = pkey->y; exp[1] = a; + base[2] = a; exp[2] = b; + base[3] = NULL; exp[3] = NULL; + mpi_mulpowm( t1, base, exp, pkey->p ); + rc = !mpi_cmp_ui( t1, 1 ); + + #endif + + mpi_free(t1); + mpi_free(t2); + return rc; +} + +/********************************************* + ************** interface ****************** + *********************************************/ + +int +elg_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) +{ + ELG_secret_key sk; + + if( !is_ELGAMAL(algo) ) + return G10ERR_PUBKEY_ALGO; + + generate( &sk, nbits, retfactors ); + skey[0] = sk.p; + skey[1] = sk.g; + skey[2] = sk.y; + skey[3] = sk.x; + return 0; +} + + +int +elg_check_secret_key( int algo, MPI *skey ) +{ + ELG_secret_key sk; + + if( !is_ELGAMAL(algo) ) + return G10ERR_PUBKEY_ALGO; + if( !skey[0] || !skey[1] || !skey[2] || !skey[3] ) + return G10ERR_BAD_MPI; + + sk.p = skey[0]; + sk.g = skey[1]; + sk.y = skey[2]; + sk.x = skey[3]; + if( !check_secret_key( &sk ) ) + return G10ERR_BAD_SECKEY; + + return 0; +} + + + +int +elg_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ) +{ + ELG_public_key pk; + + if( !is_ELGAMAL(algo) ) + return G10ERR_PUBKEY_ALGO; + if( !data || !pkey[0] || !pkey[1] || !pkey[2] ) + return G10ERR_BAD_MPI; + + pk.p = pkey[0]; + pk.g = pkey[1]; + pk.y = pkey[2]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.p ) ); + resarr[1] = mpi_alloc( mpi_get_nlimbs( pk.p ) ); + encrypt( resarr[0], resarr[1], data, &pk ); + return 0; +} + +int +elg_decrypt( int algo, MPI *result, MPI *data, MPI *skey ) +{ + ELG_secret_key sk; + + if( !is_ELGAMAL(algo) ) + return G10ERR_PUBKEY_ALGO; + if( !data[0] || !data[1] + || !skey[0] || !skey[1] || !skey[2] || !skey[3] ) + return G10ERR_BAD_MPI; + + sk.p = skey[0]; + sk.g = skey[1]; + sk.y = skey[2]; + sk.x = skey[3]; + *result = mpi_alloc_secure( mpi_get_nlimbs( sk.p ) ); + decrypt( *result, data[0], data[1], &sk ); + return 0; +} + +int +elg_sign( int algo, MPI *resarr, MPI data, MPI *skey ) +{ + ELG_secret_key sk; + + if( !is_ELGAMAL(algo) ) + return G10ERR_PUBKEY_ALGO; + if( !data || !skey[0] || !skey[1] || !skey[2] || !skey[3] ) + return G10ERR_BAD_MPI; + + sk.p = skey[0]; + sk.g = skey[1]; + sk.y = skey[2]; + sk.x = skey[3]; + resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); + resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); + sign( resarr[0], resarr[1], data, &sk ); + return 0; +} + +int +elg_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *, MPI) UNUSED, void *opaquev UNUSED) +{ + ELG_public_key pk; + + if( !is_ELGAMAL(algo) ) + return G10ERR_PUBKEY_ALGO; + if( !data[0] || !data[1] || !hash + || !pkey[0] || !pkey[1] || !pkey[2] ) + return G10ERR_BAD_MPI; + + pk.p = pkey[0]; + pk.g = pkey[1]; + pk.y = pkey[2]; + if( !verify( data[0], data[1], hash, &pk ) ) + return G10ERR_BAD_SIGN; + return 0; +} + + + +unsigned +elg_get_nbits( int algo, MPI *pkey ) +{ + if( !is_ELGAMAL(algo) ) + return 0; + return mpi_get_nbits( pkey[0] ); +} + + +/**************** + * Return some information about the algorithm. We need algo here to + * distinguish different flavors of the algorithm. + * Returns: A pointer to string describing the algorithm or NULL if + * the ALGO is invalid. + * Usage: Bit 0 set : allows signing + * 1 set : allows encryption + * NOTE: This function allows signing also for ELG-E, which is not + * okay but a bad hack to allow to work with old gpg keys. The real check + * is done in the gnupg ocde depending on the packet version. + */ +const char * +elg_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, + int *use ) +{ + *npkey = 3; + *nskey = 4; + *nenc = 2; + *nsig = 2; + + switch( algo ) { + case PUBKEY_ALGO_ELGAMAL: + *use = PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC; + return "ELG"; + case PUBKEY_ALGO_ELGAMAL_E: + *use = PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC; + return "ELG-E"; + default: *use = 0; return NULL; + } +} + + diff --git a/src/pluto/elgamal.h b/src/pluto/elgamal.h new file mode 100644 index 000000000..f104c2a52 --- /dev/null +++ b/src/pluto/elgamal.h @@ -0,0 +1,35 @@ +/* elgamal.h + * Copyright (C) 1998 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef G10_ELGAMAL_H +#define G10_ELGAMAL_H + +int elg_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ); +int elg_check_secret_key( int algo, MPI *skey ); +int elg_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey ); +int elg_decrypt( int algo, MPI *result, MPI *data, MPI *skey ); +int elg_sign( int algo, MPI *resarr, MPI data, MPI *skey ); +int elg_verify( int algo, MPI hash, MPI *data, MPI *pkey, + int (*cmp)(void *, MPI), void *opaquev ); +unsigned elg_get_nbits( int algo, MPI *pkey ); +const char *elg_get_info( int algo, int *npkey, int *nskey, + int *nenc, int *nsig, int *use ); + + +#endif /*G10_ELGAMAL_H*/ diff --git a/src/pluto/fetch.c b/src/pluto/fetch.c new file mode 100644 index 000000000..e3e56d3a8 --- /dev/null +++ b/src/pluto/fetch.c @@ -0,0 +1,1081 @@ +/* Dynamic fetching of X.509 CRLs + * Copyright (C) 2002 Stephane Laroche + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: fetch.c,v 1.12 2006/05/16 14:19:27 as Exp $ + */ + +#include +#include +#include +#include +#include + +#ifdef THREADS +#include +#endif + +#ifdef LIBCURL +#include +#endif + +#include + +#ifdef LIBLDAP +#include +#endif + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "id.h" +#include "asn1.h" +#include "pem.h" +#include "x509.h" +#include "ca.h" +#include "whack.h" +#include "ocsp.h" +#include "crl.h" +#include "fetch.h" + +fetch_req_t empty_fetch_req = { + NULL , /* next */ + 0 , /* installed */ + 0 , /* trials */ + { NULL, 0}, /* issuer */ + { NULL, 0}, /* authKeyID */ + { NULL, 0}, /* authKeySerialNumber */ + NULL /* distributionPoints */ +}; + +/* chained list of crl fetch requests */ +static fetch_req_t *crl_fetch_reqs = NULL; + +/* chained list of ocsp fetch requests */ +static ocsp_location_t *ocsp_fetch_reqs = NULL; + +#ifdef THREADS +static pthread_t thread; +static pthread_mutex_t certs_and_keys_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t authcert_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t crl_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t ocsp_cache_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t ca_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t crl_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t ocsp_fetch_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t fetch_wake_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t fetch_wake_cond = PTHREAD_COND_INITIALIZER; + +/* + * lock access to my certs and keys + */ +void +lock_certs_and_keys(const char *who) +{ + pthread_mutex_lock(&certs_and_keys_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("certs and keys locked by '%s'", who) + ) +} + +/* + * unlock access to my certs and keys + */ +void +unlock_certs_and_keys(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("certs and keys unlocked by '%s'", who) + ) + pthread_mutex_unlock(&certs_and_keys_mutex); +} + +/* + * lock access to the chained authcert list + */ +void +lock_authcert_list(const char *who) +{ + pthread_mutex_lock(&authcert_list_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("authcert list locked by '%s'", who) + ) +} + +/* + * unlock access to the chained authcert list + */ +void +unlock_authcert_list(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("authcert list unlocked by '%s'", who) + ) + pthread_mutex_unlock(&authcert_list_mutex); +} + +/* + * lock access to the chained crl list + */ +void +lock_crl_list(const char *who) +{ + pthread_mutex_lock(&crl_list_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("crl list locked by '%s'", who) + ) +} + +/* + * unlock access to the chained crl list + */ +void +unlock_crl_list(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("crl list unlocked by '%s'", who) + ) + pthread_mutex_unlock(&crl_list_mutex); +} + +/* + * lock access to the ocsp cache + */ +extern void +lock_ocsp_cache(const char *who) +{ + pthread_mutex_lock(&ocsp_cache_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("ocsp cache locked by '%s'", who) + ) +} + +/* + * unlock access to the ocsp cache + */ +extern void +unlock_ocsp_cache(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("ocsp cache unlocked by '%s'", who) + ) + pthread_mutex_unlock(&ocsp_cache_mutex); +} + +/* + * lock access to the ca info list + */ +extern void +lock_ca_info_list(const char *who) +{ + pthread_mutex_lock(&ca_info_list_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("ca info list locked by '%s'", who) + ) +} + +/* + * unlock access to the ca info list + */ +extern void +unlock_ca_info_list(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("ca info list unlocked by '%s'", who) + ) + pthread_mutex_unlock(&ca_info_list_mutex); +} + +/* + * lock access to the chained crl fetch request list + */ +static void +lock_crl_fetch_list(const char *who) +{ + pthread_mutex_lock(&crl_fetch_list_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("crl fetch request list locked by '%s'", who) + ) +} + +/* + * unlock access to the chained crl fetch request list + */ +static void +unlock_crl_fetch_list(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("crl fetch request list unlocked by '%s'", who) + ) + pthread_mutex_unlock(&crl_fetch_list_mutex); +} + +/* + * lock access to the chained ocsp fetch request list + */ +static void +lock_ocsp_fetch_list(const char *who) +{ + pthread_mutex_lock(&ocsp_fetch_list_mutex); + DBG(DBG_CONTROLMORE, + DBG_log("ocsp fetch request list locked by '%s'", who) + ) +} + +/* + * unlock access to the chained ocsp fetch request list + */ +static void +unlock_ocsp_fetch_list(const char *who) +{ + DBG(DBG_CONTROLMORE, + DBG_log("ocsp fetch request list unlocked by '%s'", who) + ) + pthread_mutex_unlock(&ocsp_fetch_list_mutex); +} + +/* + * wakes up the sleeping fetch thread + */ +void +wake_fetch_thread(const char *who) +{ + if (crl_check_interval > 0) + { + DBG(DBG_CONTROLMORE, + DBG_log("fetch thread wake call by '%s'", who) + ) + pthread_mutex_lock(&fetch_wake_mutex); + pthread_cond_signal(&fetch_wake_cond); + pthread_mutex_unlock(&fetch_wake_mutex); + } +} +#else /* !THREADS */ +#define lock_crl_fetch_list(who) /* do nothing */ +#define unlock_crl_fetch_list(who) /* do nothing */ +#define lock_ocsp_fetch_list(who) /* do nothing */ +#define unlock_ocsp_fetch_list(who) /* do nothing */ +#endif /* !THREADS */ + +/* + * free the dynamic memory used to store fetch requests + */ +static void +free_fetch_request(fetch_req_t *req) +{ + pfree(req->issuer.ptr); + pfreeany(req->authKeySerialNumber.ptr); + pfreeany(req->authKeyID.ptr); + free_generalNames(req->distributionPoints, TRUE); + pfree(req); +} + +/* writes data into a dynamically resizeable chunk_t + * needed for libcurl responses + */ +size_t +write_buffer(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + chunk_t *mem = (chunk_t*)data; + + mem->ptr = (u_char *)realloc(mem->ptr, mem->len + realsize); + if (mem->ptr) { + memcpy(&(mem->ptr[mem->len]), ptr, realsize); + mem->len += realsize; + } + return realsize; +} + +#ifdef THREADS +/* + * fetches a binary blob from a url with libcurl + */ +static err_t +fetch_curl(char *url, chunk_t *blob) +{ +#ifdef LIBCURL + char errorbuffer[CURL_ERROR_SIZE] = ""; + chunk_t response = empty_chunk; + CURLcode res; + + /* get it with libcurl */ + CURL *curl = curl_easy_init(); + + if (curl != NULL) + { + DBG(DBG_CONTROL, + DBG_log("Trying cURL '%s'", url) + ) + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &errorbuffer); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT); + + res = curl_easy_perform(curl); + + if (res == CURLE_OK) + { + blob->len = response.len; + blob->ptr = alloc_bytes(response.len, "curl blob"); + memcpy(blob->ptr, response.ptr, response.len); + } + else + { + plog("fetching uri (%s) with libcurl failed: %s", url, errorbuffer); + } + curl_easy_cleanup(curl); + /* not using freeanychunk because of realloc (no leak detective) */ + curl_free(response.ptr); + } + return strlen(errorbuffer) > 0 ? "libcurl error" : NULL; +#else /* !LIBCURL */ + return "warning: not compiled with libcurl support"; +#endif /* !LIBCURL */ +} + +#ifdef LIBLDAP +/* + * parses the result returned by an ldap query + */ +static err_t +parse_ldap_result(LDAP * ldap, LDAPMessage *result, chunk_t *blob) +{ + err_t ugh = NULL; + + LDAPMessage * entry = ldap_first_entry(ldap, result); + + if (entry != NULL) + { + BerElement *ber = NULL; + char *attr; + + attr = ldap_first_attribute(ldap, entry, &ber); + + if (attr != NULL) + { + struct berval **values = ldap_get_values_len(ldap, entry, attr); + + if (values != NULL) + { + if (values[0] != NULL) + { + blob->len = values[0]->bv_len; + blob->ptr = alloc_bytes(blob->len, "ldap blob"); + memcpy(blob->ptr, values[0]->bv_val, blob->len); + if (values[1] != NULL) + { + plog("warning: more than one value was fetched from LDAP URL"); + } + } + else + { + ugh = "no values in attribute"; + } + ldap_value_free_len(values); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, entry, 0)); + } + ldap_memfree(attr); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, entry, 0)); + } + ber_free(ber, 0); + } + else + { + ugh = ldap_err2string(ldap_result2error(ldap, result, 0)); + } + return ugh; +} + +/* + * fetches a binary blob from an ldap url + */ +static err_t +fetch_ldap_url(char *url, chunk_t *blob) +{ + LDAPURLDesc *lurl; + err_t ugh = NULL; + int rc; + + DBG(DBG_CONTROL, + DBG_log("Trying LDAP URL '%s'", url) + ) + + rc = ldap_url_parse(url, &lurl); + + if (rc == LDAP_SUCCESS) + { + LDAP *ldap = ldap_init(lurl->lud_host, lurl->lud_port); + + if (ldap != NULL) + { + int ldap_version = LDAP_VERSION3; + struct timeval timeout; + + timeout.tv_sec = FETCH_CMD_TIMEOUT; + timeout.tv_usec = 0; + ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); + ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout); + + rc = ldap_simple_bind_s(ldap, NULL, NULL); + + if (rc == LDAP_SUCCESS) + { + LDAPMessage *result; + + timeout.tv_sec = FETCH_CMD_TIMEOUT; + timeout.tv_usec = 0; + + rc = ldap_search_st(ldap, lurl->lud_dn + , lurl->lud_scope + , lurl->lud_filter + , lurl->lud_attrs + , 0, &timeout, &result); + + if (rc == LDAP_SUCCESS) + { + ugh = parse_ldap_result(ldap, result, blob); + ldap_msgfree(result); + } + else + { + ugh = ldap_err2string(rc); + } + } + else + { + ugh = ldap_err2string(rc); + } + ldap_unbind_s(ldap); + } + else + { + ugh = "ldap init"; + } + ldap_free_urldesc(lurl); + } + else + { + ugh = ldap_err2string(rc); + } + return ugh; +} +#else /* !LIBLDAP */ +static err_t +fetch_ldap_url(char *url, chunk_t *blob) +{ + return "LDAP URL fetching not activated in pluto source code"; +} +#endif /* !LIBLDAP */ + +/* + * fetch an ASN.1 blob coded in PEM or DER format from a URL + */ +static err_t +fetch_asn1_blob(char *url, chunk_t *blob) +{ + err_t ugh = NULL; + + if (strlen(url) >= 4 && strncasecmp(url, "ldap", 4) == 0) + { + ugh = fetch_ldap_url(url, blob); + } + else + { + ugh = fetch_curl(url, blob); + } + if (ugh != NULL) + return ugh; + + if (is_asn1(*blob)) + { + DBG(DBG_PARSING, + DBG_log(" fetched blob coded in DER format") + ) + } + else + { + bool pgp = FALSE; + + ugh = pemtobin(blob, NULL, "", &pgp); + if (ugh == NULL) + { + if (is_asn1(*blob)) + { + DBG(DBG_PARSING, + DBG_log(" fetched blob coded in PEM format") + ) + } + else + { + ugh = "blob coded in unknown format"; + pfree(blob->ptr); + } + } + else + { + pfree(blob->ptr); + } + } + return ugh; +} + +/* + * complete a distributionPoint URI with ca information + */ +static char* +complete_uri(chunk_t distPoint, const char *ldaphost) +{ + char *uri; + char *ptr = distPoint.ptr; + size_t len = distPoint.len; + + char *symbol = memchr(ptr, ':', len); + + if (symbol != NULL) + { + size_t type_len = symbol - ptr; + + if (type_len >= 4 && strncasecmp(ptr, "ldap", 4) == 0) + { + ptr = symbol + 1; + len -= (type_len + 1); + + if (len > 2 && *ptr++ == '/' && *ptr++ == '/') + { + len -= 2; + symbol = memchr(ptr, '/', len); + + if (symbol != NULL && symbol - ptr == 0 && ldaphost != NULL) + { + uri = alloc_bytes(distPoint.len+strlen(ldaphost)+1, "uri"); + + /* insert the ldaphost into the uri */ + sprintf(uri, "%.*s%s%.*s" + , (int)(distPoint.len - len), distPoint.ptr + , ldaphost + , (int)len, symbol); + return uri; + } + } + } + } + + /* default action: copy distributionPoint without change */ + uri = alloc_bytes(distPoint.len+1, "uri"); + sprintf(uri, "%.*s", (int)distPoint.len, distPoint.ptr); + return uri; +} + +/* + * try to fetch the crls defined by the fetch requests + */ +static void +fetch_crls(bool cache_crls) +{ + fetch_req_t *req; + fetch_req_t **reqp; + + lock_crl_fetch_list("fetch_crls"); + req = crl_fetch_reqs; + reqp = &crl_fetch_reqs; + + while (req != NULL) + { + bool valid_crl = FALSE; + chunk_t blob = empty_chunk; + generalName_t *gn = req->distributionPoints; + const char *ldaphost; + ca_info_t *ca; + + lock_ca_info_list("fetch_crls"); + + ca = get_ca_info(req->issuer, req->authKeySerialNumber, req->authKeyID); + ldaphost = (ca == NULL)? NULL : ca->ldaphost; + + while (gn != NULL) + { + char *uri = complete_uri(gn->name, ldaphost); + + err_t ugh = fetch_asn1_blob(uri, &blob); + pfree(uri); + + if (ugh != NULL) + { + plog("fetch failed: %s", ugh); + } + else + { + chunk_t crl_uri; + + clonetochunk(crl_uri, gn->name.ptr, gn->name.len, "crl uri"); + if (insert_crl(blob, crl_uri, cache_crls)) + { + DBG(DBG_CONTROL, + DBG_log("we have a valid crl") + ) + valid_crl = TRUE; + break; + } + } + gn = gn->next; + } + + unlock_ca_info_list("fetch_crls"); + + if (valid_crl) + { + /* delete fetch request */ + fetch_req_t *req_free = req; + + req = req->next; + *reqp = req; + free_fetch_request(req_free); + } + else + { + /* try again next time */ + req->trials++; + reqp = &req->next; + req = req->next; + } + } + unlock_crl_fetch_list("fetch_crls"); +} + +static void +fetch_ocsp_status(ocsp_location_t* location) +{ +#ifdef LIBCURL + chunk_t request; + chunk_t response = empty_chunk; + + CURL* curl; + CURLcode res; + + request = build_ocsp_request(location); + + DBG(DBG_CONTROL, + DBG_log("sending ocsp request to location '%.*s'" + , (int)location->uri.len, location->uri.ptr) + ) + DBG(DBG_RAW, + DBG_dump_chunk("OCSP request", request) + ) + + /* send via http post using libcurl */ + curl = curl_easy_init(); + + if (curl != NULL) + { + char errorbuffer[CURL_ERROR_SIZE]; + struct curl_slist *headers = NULL; + char* uri = alloc_bytes(location->uri.len+1, "ocsp uri"); + + /* we need a null terminated string for curl */ + memcpy(uri, location->uri.ptr, location->uri.len); + *(uri + location->uri.len) = '\0'; + + /* set content type header */ + headers = curl_slist_append(headers, "Content-Type: application/ocsp-request"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + curl_easy_setopt(curl, CURLOPT_URL, uri); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_buffer); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.ptr); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.len); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &errorbuffer); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FETCH_CMD_TIMEOUT); + + res = curl_easy_perform(curl); + + if (res == CURLE_OK) + { + DBG(DBG_CONTROL, + DBG_log("received ocsp response") + ) + DBG(DBG_RAW, + DBG_dump_chunk("OCSP response:\n", response) + ) + parse_ocsp(location, response); + } + else + { + plog("failed to fetch ocsp status from '%s': %s", uri, errorbuffer); + } + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + pfree(uri); + /* not using freeanychunk because of realloc (no leak detective) */ + curl_free(response.ptr); + } + freeanychunk(location->nonce); + freeanychunk(request); + + /* increment the trial counter of the unresolved fetch requests */ + { + ocsp_certinfo_t *certinfo = location->certinfo; + + while (certinfo != NULL) + { + certinfo->trials++; + certinfo = certinfo->next; + } + } + return; +#else /* !LIBCURL */ + plog("ocsp error: pluto wasn't compiled with libcurl support"); +#endif /* !LIBCURL */ +} + +/* + * try to fetch the necessary ocsp information + */ +static void +fetch_ocsp(void) +{ + ocsp_location_t *location; + + lock_ocsp_fetch_list("fetch_ocsp"); + location = ocsp_fetch_reqs; + + /* fetch the ocps status for all locations */ + while (location != NULL) + { + if (location->certinfo != NULL) + fetch_ocsp_status(location); + location = location->next; + } + + unlock_ocsp_fetch_list("fetch_ocsp"); +} + +static void* +fetch_thread(void *arg) +{ + struct timespec wait_interval; + + DBG(DBG_CONTROL, + DBG_log("fetch thread started") + ) + + pthread_mutex_lock(&fetch_wake_mutex); + + while(1) + { + int status; + + wait_interval.tv_nsec = 0; + wait_interval.tv_sec = time(NULL) + crl_check_interval; + + DBG(DBG_CONTROL, + DBG_log("next regular crl check in %ld seconds", crl_check_interval) + ) + status = pthread_cond_timedwait(&fetch_wake_cond, &fetch_wake_mutex + , &wait_interval); + + if (status == ETIMEDOUT) + { + DBG(DBG_CONTROL, + DBG_log(" "); + DBG_log("*time to check crls and the ocsp cache") + ) + check_ocsp(); + check_crls(); + } + else + { + DBG(DBG_CONTROL, + DBG_log("fetch thread was woken up") + ) + } + fetch_ocsp(); + fetch_crls(cache_crls); + } +} +#endif /* THREADS*/ + +/* + * initializes curl and starts the fetching thread + */ +void +init_fetch(void) +{ + int status; + +#ifdef LIBCURL + /* init curl */ + status = curl_global_init(CURL_GLOBAL_NOTHING); + if (status != CURLE_OK) + { + plog("libcurl could not be initialized, status = %d", status); + } +#endif /* LIBCURL */ + + if (crl_check_interval > 0) + { +#ifdef THREADS + status = pthread_create( &thread, NULL, fetch_thread, NULL); + if (status != 0) + { + plog("fetching thread could not be started, status = %d", status); + } +#else /* !THREADS */ + plog("warning: not compiled with pthread support"); +#endif /* !THREADS */ + } +} + +void +free_crl_fetch(void) +{ + lock_crl_fetch_list("free_crl_fetch"); + + while (crl_fetch_reqs != NULL) + { + fetch_req_t *req = crl_fetch_reqs; + crl_fetch_reqs = req->next; + free_fetch_request(req); + } + + unlock_crl_fetch_list("free_crl_fetch"); + +#ifdef LIBCURL + if (crl_check_interval > 0) + { + /* cleanup curl */ + curl_global_cleanup(); + } +#endif /* LIBCURL */ +} + +/* + * free the chained list of ocsp requests + */ +void +free_ocsp_fetch(void) +{ + lock_ocsp_fetch_list("free_ocsp_fetch"); + free_ocsp_locations(&ocsp_fetch_reqs); + unlock_ocsp_fetch_list("free_ocsp_fetch"); +} + + +/* + * add additional distribution points + */ +void +add_distribution_points(const generalName_t *newPoints ,generalName_t **distributionPoints) +{ + while (newPoints != NULL) + { + /* skip empty distribution point */ + if (newPoints->name.len > 0) + { + bool add = TRUE; + generalName_t *gn = *distributionPoints; + + while (gn != NULL) + { + if (gn->kind == newPoints->kind + && gn->name.len == newPoints->name.len + && memcmp(gn->name.ptr, newPoints->name.ptr, gn->name.len) == 0) + { + /* skip if the distribution point is already present */ + add = FALSE; + break; + } + gn = gn->next; + } + + if (add) + { + /* clone additional distribution point */ + gn = clone_thing(*newPoints, "generalName"); + clonetochunk(gn->name, newPoints->name.ptr, newPoints->name.len + , "crl uri"); + + /* insert additional CRL distribution point */ + gn->next = *distributionPoints; + *distributionPoints = gn; + } + } + newPoints = newPoints->next; + } +} + +fetch_req_t* +build_crl_fetch_request(chunk_t issuer, chunk_t authKeySerialNumber +, chunk_t authKeyID, const generalName_t *gn) +{ + fetch_req_t *req = alloc_thing(fetch_req_t, "fetch request"); + *req = empty_fetch_req; + + /* note current time */ + req->installed = time(NULL); + + /* clone fields */ + clonetochunk(req->issuer, issuer.ptr, issuer.len, "issuer"); + if (authKeySerialNumber.ptr != NULL) + { + clonetochunk(req->authKeySerialNumber, authKeySerialNumber.ptr + , authKeySerialNumber.len, "authKeySerialNumber"); + } + if (authKeyID.ptr != NULL) + { + clonetochunk(req->authKeyID, authKeyID.ptr, authKeyID.len, "authKeyID"); + } + + /* copy distribution points */ + add_distribution_points(gn, &req->distributionPoints); + + return req; +} + +/* + * add a crl fetch request to the chained list + */ +void +add_crl_fetch_request(fetch_req_t *req) +{ + fetch_req_t *r; + + lock_crl_fetch_list("add_crl_fetch_request"); + r = crl_fetch_reqs; + + while (r != NULL) + { + if ((req->authKeyID.ptr != NULL)? same_keyid(req->authKeyID, r->authKeyID) + : (same_dn(req->issuer, r->issuer) + && same_serial(req->authKeySerialNumber, r->authKeySerialNumber))) + { + /* there is already a fetch request */ + DBG(DBG_CONTROL, + DBG_log("crl fetch request already exists") + ) + + /* there might be new distribution points */ + add_distribution_points(req->distributionPoints, &r->distributionPoints); + + unlock_crl_fetch_list("add_crl_fetch_request"); + free_fetch_request(req); + return; + } + r = r->next; + } + + /* insert new fetch request at the head of the queue */ + req->next = crl_fetch_reqs; + crl_fetch_reqs = req; + + DBG(DBG_CONTROL, + DBG_log("crl fetch request added") + ) + unlock_crl_fetch_list("add_crl_fetch_request"); +} + +/* + * add an ocsp fetch request to the chained list + */ +void +add_ocsp_fetch_request(ocsp_location_t *location, chunk_t serialNumber) +{ + ocsp_certinfo_t certinfo; + + certinfo.serialNumber = serialNumber; + + lock_ocsp_fetch_list("add_ocsp_fetch_request"); + add_certinfo(location, &certinfo, &ocsp_fetch_reqs, TRUE); + unlock_ocsp_fetch_list("add_ocsp_fetch_request"); +} + +/* + * list all distribution points + */ +void +list_distribution_points(const generalName_t *gn) +{ + bool first_gn = TRUE; + + while (gn != NULL) + { + whack_log(RC_COMMENT, " %s '%.*s'", (first_gn)? "distPts: " + :" ", (int)gn->name.len, gn->name.ptr); + first_gn = FALSE; + gn = gn->next; + } +} + +/* + * list all fetch requests in the chained list + */ +void +list_crl_fetch_requests(bool utc) +{ + fetch_req_t *req; + + lock_crl_fetch_list("list_crl_fetch_requests"); + req = crl_fetch_reqs; + + if (req != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of CRL fetch requests:"); + whack_log(RC_COMMENT, " "); + } + + while (req != NULL) + { + u_char buf[BUF_LEN]; + + whack_log(RC_COMMENT, "%s, trials: %d" + , timetoa(&req->installed, utc), req->trials); + dntoa(buf, BUF_LEN, req->issuer); + whack_log(RC_COMMENT, " issuer: '%s'", buf); + if (req->authKeyID.ptr != NULL) + { + datatot(req->authKeyID.ptr, req->authKeyID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authkey: %s", buf); + } + if (req->authKeySerialNumber.ptr != NULL) + { + datatot(req->authKeySerialNumber.ptr, req->authKeySerialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " aserial: %s", buf); + } + list_distribution_points(req->distributionPoints); + req = req->next; + } + unlock_crl_fetch_list("list_crl_fetch_requests"); +} + +void +list_ocsp_fetch_requests(bool utc) +{ + lock_ocsp_fetch_list("list_ocsp_fetch_requests"); + list_ocsp_locations(ocsp_fetch_reqs, TRUE, utc, FALSE); + unlock_ocsp_fetch_list("list_ocsp_fetch_requests"); + +} diff --git a/src/pluto/fetch.h b/src/pluto/fetch.h new file mode 100644 index 000000000..6303f37e4 --- /dev/null +++ b/src/pluto/fetch.h @@ -0,0 +1,79 @@ +/* Dynamic fetching of X.509 CRLs + * Copyright (C) 2002 Stephane Laroche + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: fetch.h,v 1.6 2005/11/25 10:08:00 as Exp $ + */ + +#include "x509.h" + +#define FETCH_CMD_TIMEOUT 10 /* seconds */ + +struct ocsp_location; /* forward declaration of ocsp_location defined in ocsp.h */ + +typedef enum { + FETCH_GET = 1, + FETCH_POST = 2 +} fetch_request_t; + +typedef struct fetch_req fetch_req_t; + +struct fetch_req { + fetch_req_t *next; + time_t installed; + int trials; + chunk_t issuer; + chunk_t authKeyID; + chunk_t authKeySerialNumber; + generalName_t *distributionPoints; +}; + +#ifdef THREADS +extern void lock_crl_list(const char *who); +extern void unlock_crl_list(const char *who); +extern void lock_ocsp_cache(const char *who); +extern void unlock_ocsp_cache(const char *who); +extern void lock_ca_info_list(const char *who); +extern void unlock_ca_info_list(const char *who); +extern void lock_authcert_list(const char *who); +extern void unlock_authcert_list(const char *who); +extern void lock_certs_and_keys(const char *who); +extern void unlock_certs_and_keys(const char *who); +extern void wake_fetch_thread(const char *who); +#else +#define lock_crl_list(who) /* do nothing */ +#define unlock_crl_list(who) /* do nothing */ +#define lock_ocsp_cache(who) /* do nothing */ +#define unlock_ocsp_cache(who) /* do nothing */ +#define lock_ca_info_list(who) /* do nothing */ +#define unlock_ca_info_list(who) /* do nothing */ +#define lock_authcert_list(who) /* do nothing */ +#define unlock_authcert_list(who) /* do nothing */ +#define lock_certs_and_keys(who) /* do nothing */ +#define unlock_certs_and_keys(who) /* do nothing */ +#define wake_fetch_thread(who) /* do nothing */ +#endif +extern void init_fetch(void); +extern void free_crl_fetch(void); +extern void free_ocsp_fetch(void); +extern void add_distribution_points(const generalName_t *newPoints + , generalName_t **distributionPoints); +extern fetch_req_t* build_crl_fetch_request(chunk_t issuer, chunk_t authKeySerialNumber + , chunk_t authKeyID, const generalName_t *gn); +extern void add_crl_fetch_request(fetch_req_t *req); +extern void add_ocsp_fetch_request(struct ocsp_location *location, chunk_t serialNumber); +extern void list_distribution_points(const generalName_t *gn); +extern void list_crl_fetch_requests(bool utc); +extern void list_ocsp_fetch_requests(bool utc); +extern size_t write_buffer(void *ptr, size_t size, size_t nmemb, void *data); + diff --git a/src/pluto/foodgroups.c b/src/pluto/foodgroups.c new file mode 100644 index 000000000..c92bdb3d4 --- /dev/null +++ b/src/pluto/foodgroups.c @@ -0,0 +1,462 @@ +/* Implement policy groups-style control files (aka "foodgroups") + * Copyright (C) 2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: foodgroups.c,v 1.2 2004/04/01 18:28:32 as Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "connections.h" +#include "foodgroups.h" +#include "kernel.h" +#include "lex.h" +#include "log.h" +#include "whack.h" + + +/* Food group config files are found in directory fg_path */ + +#ifndef POLICYGROUPSDIR +#define POLICYGROUPSDIR IPSEC_CONFDIR "/ipsec.d/policies" +#endif + +const char *policygroups_dir = POLICYGROUPSDIR; + +static char *fg_path = NULL; +static size_t fg_path_space = 0; + + +/* Groups is a list of connections that are policy groups. + * The list is updated as group connections are added and deleted. + */ + +struct fg_groups { + struct fg_groups *next; + struct connection *connection; +}; + +static struct fg_groups *groups = NULL; + + +/* Targets is a list of pairs: subnet and its policy group. + * This list is bulk-updated on whack --listen and + * incrementally updated when group connections are deleted. + * + * It is ordered by source subnet, and if those are equal, then target subnet. + * A subnet is compared by comparing the network, and if those are equal, + * comparing the mask. + */ + +struct fg_targets { + struct fg_targets *next; + struct fg_groups *group; + ip_subnet subnet; + char *name; /* name of instance of group conn */ +}; + +static struct fg_targets *targets = NULL; + +struct fg_targets *new_targets; + +/* ipcmp compares the two ip_address values a and b. + * It returns -1, 0, or +1 if a is, respectively, + * less than, equal to, or greater than b. + */ +static int +ipcmp(ip_address *a, ip_address *b) +{ + if (addrtypeof(a) != addrtypeof(b)) + { + return addrtypeof(a) < addrtypeof(b)? -1 : 1; + } + else if (sameaddr(a, b)) + { + return 0; + } + else + { + const struct sockaddr *sa = sockaddrof(a) + , *sb = sockaddrof(b); + + passert(addrtypeof(a) == AF_INET); /* not yet implemented IPv6 version :-( */ + return (ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr) + < ntohl(((const struct sockaddr_in *)sb)->sin_addr.s_addr)) + ? -1 : 1; + } +} + +/* subnetcmp compares the two ip_subnet values a and b. + * It returns -1, 0, or +1 if a is, respectively, + * less than, equal to, or greater than b. + */ +static int +subnetcmp(const ip_subnet *a, const ip_subnet *b) +{ + ip_address neta, maska, netb, maskb; + int r; + + networkof(a, &neta); + maskof(a, &maska); + networkof(b, &netb); + maskof(b, &maskb); + r = ipcmp(&neta, &netb); + if (r == 0) + r = ipcmp(&maska, &maskb); + return r; +} + +static void +read_foodgroup(struct fg_groups *g) +{ + const char *fgn = g->connection->name; + const ip_subnet *lsn = &g->connection->spd.this.client; + size_t plen = strlen(policygroups_dir) + 1 + strlen(fgn) + 1; + struct file_lex_position flp_space; + + if (plen > fg_path_space) + { + pfreeany(fg_path); + fg_path_space = plen + 10; + fg_path = alloc_bytes(fg_path_space, "policy group path"); + } + snprintf(fg_path, fg_path_space, "%s/%s", policygroups_dir, fgn); + if (!lexopen(&flp_space, fg_path, TRUE)) + { + DBG(DBG_CONTROL, DBG_log("no group file \"%s\"", fg_path)); + } + else + { + plog("loading group \"%s\"", fg_path); + for (;;) + { + switch (flp->bdry) + { + case B_none: + { + /* !!! this test is not sufficient for distinguishing address families. + * We need a notation to specify that a FQDN is to be resolved to IPv6. + */ + const struct af_info *afi = strchr(tok, ':') == NULL + ? &af_inet4_info: &af_inet6_info; + ip_subnet sn; + err_t ugh; + + if (strchr(tok, '/') == NULL) + { + /* no /, so treat as /32 or V6 equivalent */ + ip_address t; + + ugh = ttoaddr(tok, 0, afi->af, &t); + if (ugh == NULL) + ugh = addrtosubnet(&t, &sn); + } + else + { + ugh = ttosubnet(tok, 0, afi->af, &sn); + } + + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s \"%s\"" + , flp->filename, flp->lino, ugh, tok); + } + else if (afi->af != AF_INET) + { + loglog(RC_LOG_SERIOUS + , "\"%s\" line %d: unsupported Address Family \"%s\"" + , flp->filename, flp->lino, tok); + } + else + { + /* Find where new entry ought to go in new_targets. */ + struct fg_targets **pp; + int r; + + for (pp = &new_targets; ; pp = &(*pp)->next) + { + if (*pp == NULL) + { + r = -1; /* end of list is infinite */ + break; + } + r = subnetcmp(lsn, &(*pp)->group->connection->spd.this.client); + if (r == 0) + r = subnetcmp(&sn, &(*pp)->subnet); + if (r <= 0) + break; + } + + if (r == 0) + { + char source[SUBNETTOT_BUF]; + + subnettot(lsn, 0, source, sizeof(source)); + loglog(RC_LOG_SERIOUS + , "\"%s\" line %d: subnet \"%s\", source %s, already \"%s\"" + , flp->filename + , flp->lino + , tok + , source + , (*pp)->group->connection->name); + } + else + { + struct fg_targets *f = alloc_thing(struct fg_targets, "fg_target"); + + f->next = *pp; + f->group = g; + f->subnet = sn; + f->name = NULL; + *pp = f; + } + } + } + (void)shift(); /* next */ + continue; + + case B_record: + flp->bdry = B_none; /* eat the Record Boundary */ + (void)shift(); /* get real first token */ + continue; + + case B_file: + break; /* done */ + } + break; /* if we reach here, out of loop */ + } + lexclose(); + } +} + +static void +free_targets(void) +{ + while (targets != NULL) + { + struct fg_targets *t = targets; + + targets = t->next; + pfreeany(t->name); + pfree(t); + } +} + +void +load_groups(void) +{ + passert(new_targets == NULL); + + /* for each group, add config file targets into new_targets */ + { + struct fg_groups *g; + + for (g = groups; g != NULL; g = g->next) + if (oriented(*g->connection)) + read_foodgroup(g); + } + + /* dump new_targets */ + DBG(DBG_CONTROL, + { + struct fg_targets *t; + + for (t = new_targets; t != NULL; t = t->next) + { + char asource[SUBNETTOT_BUF]; + char atarget[SUBNETTOT_BUF]; + + subnettot(&t->group->connection->spd.this.client + , 0, asource, sizeof(asource)); + subnettot(&t->subnet, 0, atarget, sizeof(atarget)); + DBG_log("%s->%s %s" + , asource, atarget + , t->group->connection->name); + } + }); + + /* determine and deal with differences between targets and new_targets. + * structured like a merge. + */ + { + struct fg_targets *op = targets + , *np = new_targets; + + while (op != NULL && np != NULL) + { + int r = subnetcmp(&op->group->connection->spd.this.client + , &np->group->connection->spd.this.client); + + if (r == 0) + r = subnetcmp(&op->subnet, &np->subnet); + + if (r == 0 && op->group == np->group) + { + /* unchanged -- steal name & skip over */ + np->name = op->name; + op->name = NULL; + op = op->next; + np = np->next; + } + else + { + /* note: following cases overlap! */ + if (r <= 0) + { + remove_group_instance(op->group->connection, op->name); + op = op->next; + } + if (r >= 0) + { + np->name = add_group_instance(np->group->connection, &np->subnet); + np = np->next; + } + } + } + for (; op != NULL; op = op->next) + remove_group_instance(op->group->connection, op->name); + for (; np != NULL; np = np->next) + np->name = add_group_instance(np->group->connection, &np->subnet); + + /* update: new_targets replaces targets */ + free_targets(); + targets = new_targets; + new_targets = NULL; + } +} + + +void +add_group(struct connection *c) +{ + struct fg_groups *g = alloc_thing(struct fg_groups, "policy group"); + + g->next = groups; + groups = g; + + g->connection = c; +} + +static struct fg_groups * +find_group(const struct connection *c) +{ + struct fg_groups *g; + + for (g = groups; g != NULL && g->connection != c; g = g->next) + ; + return g; +} + +void +route_group(struct connection *c) +{ + /* it makes no sense to route a connection that is ISAKMP-only */ + if (!NEVER_NEGOTIATE(c->policy) && !HAS_IPSEC_POLICY(c->policy)) + { + loglog(RC_ROUTE, "cannot route an ISAKMP-only group connection"); + } + else + { + struct fg_groups *g = find_group(c); + struct fg_targets *t; + + passert(g != NULL); + g->connection->policy |= POLICY_GROUTED; + for (t = targets; t != NULL; t = t->next) + { + if (t->group == g) + { + struct connection *ci = con_by_name(t->name, FALSE); + + if (ci != NULL) + { + set_cur_connection(ci); + if (!trap_connection(ci)) + whack_log(RC_ROUTE, "could not route"); + set_cur_connection(c); + } + } + } + } +} + +void +unroute_group(struct connection *c) +{ + struct fg_groups *g = find_group(c); + struct fg_targets *t; + + passert(g != NULL); + g->connection->policy &= ~POLICY_GROUTED; + for (t = targets; t != NULL; t = t->next) + { + if (t->group == g) + { + struct connection *ci = con_by_name(t->name, FALSE); + + if (ci != NULL) + { + set_cur_connection(ci); + unroute_connection(ci); + set_cur_connection(c); + } + } + } +} + +void +delete_group(const struct connection *c) +{ + struct fg_groups *g; + + /* find and remove from groups */ + { + struct fg_groups **pp; + + for (pp = &groups; (g = *pp)->connection != c; pp = &(*pp)->next) + ; + + *pp = g->next; + } + + /* find and remove from targets */ + { + struct fg_targets **pp; + + for (pp = &targets; *pp != NULL; ) + { + struct fg_targets *t = *pp; + + if (t->group == g) + { + *pp = t->next; + remove_group_instance(t->group->connection, t->name); + pfree(t); + /* pp is ready for next iteration */ + } + else + { + pp = &t->next; + } + } + } + + pfree(g); +} diff --git a/src/pluto/foodgroups.h b/src/pluto/foodgroups.h new file mode 100644 index 000000000..7cbbccc44 --- /dev/null +++ b/src/pluto/foodgroups.h @@ -0,0 +1,24 @@ +/* Implement policygroups-style control files (aka "foodgroups") + * Copyright (C) 2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: foodgroups.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +struct connection; /* forward declaration */ +extern void add_group(struct connection *c); +extern void route_group(struct connection *c); +extern void unroute_group(struct connection *c); +extern void delete_group(const struct connection *c); + +extern const char *policygroups_dir; +extern void load_groups(void); diff --git a/src/pluto/gcryptfix.c b/src/pluto/gcryptfix.c new file mode 100644 index 000000000..1ebacdcf6 --- /dev/null +++ b/src/pluto/gcryptfix.c @@ -0,0 +1,283 @@ +/* Routines to make gcrypt routines feel at home in Pluto. + * Copyright (C) 1999 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: gcryptfix.c,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#include + +#include +#include +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "rnd.h" +#include "gcryptfix.h" /* includes "defs.h" "rnd.h" */ + +MPI +mpi_alloc( unsigned nlimbs UNUSED ) +{ + MPI n = alloc_bytes(sizeof *n, "mpi_alloc"); + + mpz_init(n); + return n; +} + +MPI +mpi_alloc_secure( unsigned nlimbs ) +{ + return mpi_alloc(nlimbs); +} + +MPI +mpi_alloc_set_ui( unsigned long u) +{ + MPI n = alloc_bytes(sizeof *n, "mpi_copy"); + + mpz_init_set_ui(n, u); + return n; +} + +MPI +mpi_copy( MPI a ) +{ + MPI n = alloc_bytes(sizeof *n, "mpi_copy"); + + mpz_init_set(n, a); + return n; +} + +void +mpi_free( MPI a ) +{ + mpz_clear(a); + pfree(a); +} + +int +mpi_divisible_ui(MPI dividend, ulong divisor ) +{ + ulong rem; + mpz_t remtoo; + + mpz_init(remtoo); + rem = mpz_mod_ui(remtoo, dividend, divisor); + mpz_clear(remtoo); + return rem == 0; +} + +unsigned +mpi_trailing_zeros( MPI a ) +{ + return mpz_scan1(a, 0); +} + +unsigned +mpi_get_nbits( MPI a ) +{ + return mpz_sizeinbase(a, 2); +} + +int +mpi_test_bit( MPI a, unsigned n ) +{ + /* inspired by gmp/mpz/clrbit.c */ + mp_size_t li = n / mp_bits_per_limb; + + if (li >= a->_mp_size) + return 0; + return (a->_mp_d[li] & ((mp_limb_t) 1 << (n % mp_bits_per_limb))) != 0; +} + +void +mpi_set_bit( MPI a, unsigned n ) +{ + mpz_setbit(a, n); +} + +void +mpi_clear_bit( MPI a, unsigned n ) +{ + mpz_clrbit(a, n); +} + +void +mpi_clear_highbit( MPI a, unsigned n ) +{ + /* This seems whacky, but what do I know. */ + mpz_fdiv_r_2exp(a, a, n); +} + +void +mpi_set_highbit( MPI a, unsigned n ) +{ + /* This seems whacky, but what do I know. */ + mpz_fdiv_r_2exp(a, a, n+1); + mpz_setbit(a, n); +} + +void +mpi_set_buffer( MPI a, const u_char *buffer, unsigned nbytes, int sign ) +{ + /* this is a lot like n_to_mpz */ + size_t i; + + passert(sign == 0); /* we won't hit any negative numbers */ + mpz_init_set_ui(a, 0); + + for (i = 0; i != nbytes; i++) + { + mpz_mul_ui(a, a, 1 << BITS_PER_BYTE); + mpz_add_ui(a, a, buffer[i]); + } +} + +u_char * +get_random_bits(size_t nbits, int level UNUSED, int secure UNUSED) +{ + size_t nbytes = (nbits+7)/8; + u_char *b = alloc_bytes(nbytes, "random bytes"); + + get_rnd_bytes(b, nbytes); + return b; +} +/**************** from gnupg-1.0.0/mpi/mpi-mpow.c + * RES = (BASE[0] ^ EXP[0]) * (BASE[1] ^ EXP[1]) * ... * mod M + */ +#define barrett_mulm( w, u, v, m, y, k, r1, r2 ) mpi_mulm( (w), (u), (v), (m) ) + +static int +build_index( MPI *exparray, int k, int i, int t ) +{ + int j, bitno; + int index = 0; + + bitno = t-i; + for(j=k-1; j >= 0; j-- ) { + index <<= 1; + if( mpi_test_bit( exparray[j], bitno ) ) + index |= 1; + } + /*log_debug("t=%d i=%d index=%d\n", t, i, index );*/ + return index; +} + +void +mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI m) +{ + int k; /* number of elements */ + int t; /* bit size of largest exponent */ + int i, j, idx; + MPI *G; /* table with precomputed values of size 2^k */ + MPI tmp; + #ifdef USE_BARRETT + MPI barrett_y, barrett_r1, barrett_r2; + int barrett_k; + #endif + + for(k=0; basearray[k]; k++ ) + ; + passert(k); + for(t=0, i=0; (tmp=exparray[i]); i++ ) { + /*log_mpidump("exp: ", tmp );*/ + j = mpi_get_nbits(tmp); + if( j > t ) + t = j; + } + /*log_mpidump("mod: ", m );*/ + passert(i==k); + passert(t); + passert( k < 10 ); + +#ifdef PLUTO + m_alloc_ptrs_clear(G, 1<= 0 && idx < (1<= 0; i--) + { + buf[i] = mpz_mdivmod_ui(&temp2, NULL, &temp1, 1 << BITS_PER_BYTE); + mpz_set(&temp1, &temp2); + } + + passert(mpz_sgn(&temp1) == 0); /* we must have done all the bits */ + mpz_clear(&temp1); + mpz_clear(&temp2); + +#ifdef DEBUG + DBG_dump(text, buf, len); +#endif /* DEBUG */ +} diff --git a/src/pluto/gcryptfix.h b/src/pluto/gcryptfix.h new file mode 100644 index 000000000..637ecbc8d --- /dev/null +++ b/src/pluto/gcryptfix.h @@ -0,0 +1,111 @@ +/* Definitions to make gcrypt routines feel at home in Pluto. + * Copyright (C) 1999 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: gcryptfix.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#define DBG_CIPHER 1 /* some day we'll do this right */ + +/* Simulate MPI routines with gmp routines. + * gmp's MP_INT is a stuct; MPI's MPI is a pointer to an analogous struct. + * gmp's mpz_t is an array of one of these structs to enable magic pointer + * conversions to make the notation convenient (but confusing). + */ +typedef u_char byte; +typedef MP_INT *MPI; + +#define BITS_PER_MPI_LIMB mp_bits_per_limb + +extern MPI mpi_alloc( unsigned nlimbs ); +extern MPI mpi_alloc_secure( unsigned nlimbs ); +#define mpi_alloc_like(n) mpi_alloc(mpi_get_nlimbs(n)) +extern MPI mpi_alloc_set_ui( unsigned long u); +#define mpi_set_ui(w, u) mpz_set_ui(w, u) +#define mpi_set(w, u) mpz_set(w, u) +extern void mpi_free( MPI a ); +extern MPI mpi_copy( MPI a ); +extern unsigned mpi_get_nbits( MPI a ); +#define mpi_get_nlimbs(a) ((a)->_mp_alloc) /* dirty, but useless */ +extern void mpi_set_buffer( MPI a, const u_char *buffer, unsigned nbytes, int sign ); +extern unsigned mpi_trailing_zeros( MPI a ); +extern int mpi_test_bit( MPI a, unsigned n ); +extern void mpi_set_bit( MPI a, unsigned n ); +extern void mpi_clear_bit( MPI a, unsigned n ); +extern void mpi_clear_highbit( MPI a, unsigned n ); +extern void mpi_set_highbit( MPI a, unsigned n ); +#define mpi_cmp_ui(u, v) mpz_cmp_ui((u), (v)) +#define mpi_cmp(u, v) mpz_cmp((u), (v)) +#define mpi_is_neg(n) (mpz_sgn(n) < 0) +#define mpi_add(w, u, v) mpz_add((w), (u), (v)) +#define mpi_add_ui(w, u, v) mpz_add_ui((w), (u), (v)) +#define mpi_sub_ui(w, u, v) mpz_sub_ui((w), (u), (v)) +#define mpi_subm( w, u, v, m) { mpz_sub( (w), (u), (v)) ; mpz_fdiv_r((w), (w), (m)); } +#define mpi_mul( w, u, v) mpz_mul( (w), (u), (v)) +#define mpi_mul_ui( w, u, v) mpz_mul_ui( (w), (u), (v)) +#define mpi_mulm( w, u, v, m) { mpz_mul( (w), (u), (v)) ; mpz_fdiv_r((w), (w), (m)); } +#define mpi_fdiv_q(quot, dividend, divisor) mpz_fdiv_q((quot), (dividend), (divisor)) +#define mpi_fdiv_r( rem, dividend, divisor ) mpz_fdiv_r( (rem), (dividend), (divisor) ) +#define mpi_fdiv_r_ui( rem, dividend, divisor ) mpz_fdiv_r_ui( (rem), (dividend), (divisor) ) +#define mpi_tdiv_q_2exp( w, u, count ) mpz_tdiv_q_2exp( (w), (u), (count) ) +extern int mpi_divisible_ui(MPI dividend, ulong divisor ); +#define mpi_powm( res, base, exp, mod) mpz_powm( res, base, exp, mod) +extern void mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI mod); +#define mpi_gcd( g, a, b ) ( mpz_gcd( (g), (a), (b) ), !mpi_cmp_ui( (g), 1)) +#define mpi_invm( x, a, n ) mpz_invert( (x), (a), (n) ) + +#ifdef DEBUG +# define log_debug(f...) DBG_log(f) +#else +# define log_debug(f...) do ; while (0) /* do nothing, carefully */ +#endif +#define log_fatal(f...) exit_log(f) /* overreaction? */ +extern void log_mpidump( const char *text, MPI a ); + +#define assert(p) passert(p) +#define BUG() passert(FALSE) + +#define m_alloc_ptrs_clear(pp, n) { \ + int c = (n); \ + (pp) = alloc_bytes((n) * sizeof(*(pp)), "m_alloc_ptrs_clear"); \ + while (c > 0) (pp)[--c] = NULL; \ + } + +extern u_char *get_random_bits(size_t nbits, int level, int secure); +#define m_alloc(sz) alloc_bytes((sz), "m_alloc") /* not initialized */ +#define m_free(n) pfree(n) /* always freeing something from get_random_bits */ + +/* declarations from gnupg-1.0.0/include/cipher.h */ +/*-- primegen.c --*/ +MPI generate_secret_prime( unsigned nbits ); +MPI generate_public_prime( unsigned nbits ); +MPI generate_elg_prime( int mode, unsigned pbits, unsigned qbits, + MPI g, MPI **factors ); + +#define PUBKEY_ALGO_ELGAMAL_E 16 /* encrypt only ElGamal (but not for v3)*/ +#define PUBKEY_ALGO_DSA 17 +#define PUBKEY_ALGO_ELGAMAL 20 /* sign and encrypt elgamal */ + +#define is_ELGAMAL(a) ((a)==PUBKEY_ALGO_ELGAMAL || (a)==PUBKEY_ALGO_ELGAMAL_E) + +#define PUBKEY_USAGE_SIG 1 /* key is good for signatures */ +#define PUBKEY_USAGE_ENC 2 /* key is good for encryption */ + +/* from gnupg-1.0.0/include/errors.h */ + +#define G10ERR_PUBKEY_ALGO 4 /* Unknown pubkey algorithm */ +#define G10ERR_BAD_SECKEY 7 /* Bad secret key */ +#define G10ERR_BAD_SIGN 8 /* Bad signature */ +#define G10ERR_BAD_MPI 30 + +/*-- smallprime.c --*/ +extern ushort small_prime_numbers[]; diff --git a/src/pluto/id.c b/src/pluto/id.c new file mode 100644 index 000000000..4e75ec2e9 --- /dev/null +++ b/src/pluto/id.c @@ -0,0 +1,509 @@ +/* identity representation, as in IKE ID Payloads (RFC 2407 DOI 4.6.2.1) + * Copyright (C) 1999-2001 D. Hugh Redelmeier + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: id.c,v 1.4 2005/08/15 20:07:08 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef HOST_NAME_MAX /* POSIX 1003.1-2001 says defines this */ +# define HOST_NAME_MAX 255 /* upper bound, according to SUSv2 */ +#endif +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "id.h" +#include "log.h" +#include "connections.h" +#include "packet.h" +#include "whack.h" + +const struct id empty_id; /* ID_NONE */ + +enum myid_state myid_state = MYID_UNKNOWN; +struct id myids[MYID_SPECIFIED+1]; /* %myid */ +char *myid_str[MYID_SPECIFIED+1]; /* string form of IDs */ + +/* initialize id module + * Fills in myid from environment variable IPSECmyid or defaultrouteaddr + */ +void +init_id(void) +{ + passert(empty_id.kind == ID_NONE); + myid_state = MYID_UNKNOWN; + { + enum myid_state s; + + for (s = MYID_UNKNOWN; s <= MYID_SPECIFIED; s++) + { + myids[s] = empty_id; + myid_str[s] = NULL; + } + } + set_myid(MYID_SPECIFIED, getenv("IPSECmyid")); + set_myid(MYID_IP, getenv("defaultrouteaddr")); + set_myFQDN(); +} + +static void +calc_myid_str(enum myid_state s) +{ + /* preformat the ID name */ + char buf[BUF_LEN]; + + idtoa(&myids[s], buf, BUF_LEN); + replace(myid_str[s], clone_str(buf, "myid string")); +} + + +void +set_myid(enum myid_state s, char *idstr) +{ + if (idstr != NULL) + { + struct id id; + err_t ugh = atoid(idstr, &id, FALSE); + + if (ugh != NULL) + { + loglog(RC_BADID, "myid malformed: %s \"%s\"", ugh, idstr); + } + else + { + free_id_content(&myids[s]); + unshare_id_content(&id); + myids[s] = id; + if (s == MYID_SPECIFIED) + myid_state = MYID_SPECIFIED; + + calc_myid_str(s); + } + } +} + +void +set_myFQDN(void) +{ + char FQDN[HOST_NAME_MAX + 1]; + int r = gethostname(FQDN, sizeof(FQDN)); + + free_id_content(&myids[MYID_HOSTNAME]); + myids[MYID_HOSTNAME] = empty_id; + if (r != 0) + { + log_errno((e, "gethostname() failed in set_myFQDN")); + } + else + { + FQDN[sizeof(FQDN) - 1] = '\0'; /* insurance */ + + { + size_t len = strlen(FQDN); + + if (len > 0 && FQDN[len-1] == '.') + { + /* nuke trailing . */ + FQDN[len-1]='\0'; + } + } + + if (!strcaseeq(FQDN, "localhost.localdomain")) + { + clonetochunk(myids[MYID_HOSTNAME].name, FQDN, strlen(FQDN), "my FQDN"); + myids[MYID_HOSTNAME].kind = ID_FQDN; + calc_myid_str(MYID_HOSTNAME); + } + } +} + +void +show_myid_status(void) +{ + char idstr[BUF_LEN]; + + (void)idtoa(&myids[myid_state], idstr, sizeof(idstr)); + whack_log(RC_COMMENT, "%%myid = %s", idstr); +} + +/* Convert textual form of id into a (temporary) struct id. + * Note that if the id is to be kept, unshare_id_content will be necessary. + */ +err_t +atoid(char *src, struct id *id, bool myid_ok) +{ + err_t ugh = NULL; + + *id = empty_id; + + if (myid_ok && streq("%myid", src)) + { + id->kind = ID_MYID; + } + else if (strchr(src, '=') != NULL) + { + /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN */ + id->kind = ID_DER_ASN1_DN; + id->name.ptr = temporary_cyclic_buffer(); /* assign temporary buffer */ + id->name.len = 0; + /* convert from LDAP style or openssl x509 -subject style to ASN.1 DN + * discard optional @ character in front of DN + */ + ugh = atodn((*src == '@')?src+1:src, &id->name); + } + else if (strchr(src, '@') == NULL) + { + if (streq(src, "%any") || streq(src, "0.0.0.0")) + { + /* any ID will be accepted */ + id->kind = ID_NONE; + } + else + { + /* !!! this test is not sufficient for distinguishing address families. + * We need a notation to specify that a FQDN is to be resolved to IPv6. + */ + const struct af_info *afi = strchr(src, ':') == NULL + ? &af_inet4_info: &af_inet6_info; + + id->kind = afi->id_addr; + ugh = ttoaddr(src, 0, afi->af, &id->ip_addr); + } + } + else + { + if (*src == '@') + { + if (*(src+1) == '#') + { + /* if there is a second specifier (#) on the line + * we interprete this as ID_KEY_ID + */ + id->kind = ID_KEY_ID; + id->name.ptr = src; + /* discard @~, convert from hex to bin */ + ugh = ttodata(src+2, 0, 16, id->name.ptr, strlen(src), &id->name.len); + } + else if (*(src+1) == '~') + { + /* if there is a second specifier (~) on the line + * we interprete this as a binary ID_DER_ASN1_DN + */ + id->kind = ID_DER_ASN1_DN; + id->name.ptr = src; + /* discard @~, convert from hex to bin */ + ugh = ttodata(src+2, 0, 16, id->name.ptr, strlen(src), &id->name.len); + } + else + { + id->kind = ID_FQDN; + id->name.ptr = src+1; /* discard @ */ + id->name.len = strlen(src)-1; + } + } + else + { + /* We leave in @, as per DOI 4.6.2.4 + * (but DNS wants . instead). + */ + id->kind = ID_USER_FQDN; + id->name.ptr = src; + id->name.len = strlen(src); + } + } + return ugh; +} + + +/* + * Converts a binary key ID into hexadecimal format + */ +int +keyidtoa(char *dst, size_t dstlen, chunk_t keyid) +{ + int n = datatot(keyid.ptr, keyid.len, 'x', dst, dstlen); + return (((size_t)n < dstlen)? n : dstlen) - 1; +} + +void +iptoid(const ip_address *ip, struct id *id) +{ + *id = empty_id; + + switch (addrtypeof(ip)) + { + case AF_INET: + id->kind = ID_IPV4_ADDR; + break; + case AF_INET6: + id->kind = ID_IPV6_ADDR; + break; + default: + bad_case(addrtypeof(ip)); + } + id->ip_addr = *ip; +} + +int +idtoa(const struct id *id, char *dst, size_t dstlen) +{ + int n; + + id = resolve_myid(id); + switch (id->kind) + { + case ID_NONE: + n = snprintf(dst, dstlen, "(none)"); + break; + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + n = (int)addrtot(&id->ip_addr, 0, dst, dstlen) - 1; + break; + case ID_FQDN: + n = snprintf(dst, dstlen, "@%.*s", (int)id->name.len, id->name.ptr); + break; + case ID_USER_FQDN: + n = snprintf(dst, dstlen, "%.*s", (int)id->name.len, id->name.ptr); + break; + case ID_DER_ASN1_DN: + n = dntoa(dst, dstlen, id->name); + break; + case ID_KEY_ID: + n = keyidtoa(dst, dstlen, id->name); + break; + default: + n = snprintf(dst, dstlen, "unknown id kind %d", id->kind); + break; + } + + /* "Sanitize" string so that log isn't endangered: + * replace unprintable characters with '?'. + */ + if (n > 0) + { + for ( ; *dst != '\0'; dst++) + if (!isprint(*dst)) + *dst = '?'; + } + + return n; +} + +/* Replace the shell metacharacters ', \, ", `, and $ in a character string + * by escape sequences consisting of their octal values + */ +void +escape_metachar(const char *src, char *dst, size_t dstlen) +{ + while (*src != '\0' && dstlen > 4) + { + switch (*src) + { + case '\'': + case '\\': + case '"': + case '`': + case '$': + sprintf(dst,"\\%s%o", (*src < 64)?"0":"", *src); + dst += 4; + dstlen -= 4; + break; + default: + *dst++ = *src; + dstlen--; + } + src++; + } + *dst = '\0'; +} + + +/* Make private copy of string in struct id. + * This is needed if the result of atoid is to be kept. + */ +void +unshare_id_content(struct id *id) +{ + switch (id->kind) + { + case ID_FQDN: + case ID_USER_FQDN: + case ID_DER_ASN1_DN: + case ID_KEY_ID: + id->name.ptr = clone_bytes(id->name.ptr, id->name.len, "keep id name"); + break; + case ID_MYID: + case ID_NONE: + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + break; + default: + bad_case(id->kind); + } +} + +void +free_id_content(struct id *id) +{ + switch (id->kind) + { + case ID_FQDN: + case ID_USER_FQDN: + case ID_DER_ASN1_DN: + case ID_KEY_ID: + freeanychunk(id->name); + break; + case ID_MYID: + case ID_NONE: + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + break; + default: + bad_case(id->kind); + } +} + +/* compare two struct id values */ +bool +same_id(const struct id *a, const struct id *b) +{ + a = resolve_myid(a); + b = resolve_myid(b); + if (a->kind != b->kind) + return FALSE; + switch (a->kind) + { + case ID_NONE: + return TRUE; /* kind of vacuous */ + + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + return sameaddr(&a->ip_addr, &b->ip_addr); + + case ID_FQDN: + case ID_USER_FQDN: + /* assumptions: + * - case should be ignored + * - trailing "." should be ignored (even if the only character?) + */ + { + size_t al = a->name.len + , bl = b->name.len; + + while (al > 0 && a->name.ptr[al - 1] == '.') + al--; + while (bl > 0 && b->name.ptr[bl - 1] == '.') + bl--; + return al == bl + && strncasecmp(a->name.ptr, b->name.ptr, al) == 0; + } + + case ID_DER_ASN1_DN: + return same_dn(a->name, b->name); + + case ID_KEY_ID: + return a->name.len == b->name.len + && memcmp(a->name.ptr, b->name.ptr, a->name.len) == 0; + + default: + bad_case(a->kind); + } + return FALSE; +} + +/* compare two struct id values, DNs can contain wildcards */ +bool +match_id(const struct id *a, const struct id *b, int *wildcards) +{ + if (b->kind == ID_NONE) + { + *wildcards = MAX_WILDCARDS; + return TRUE; + } + if (a->kind != b->kind) + return FALSE; + if (a->kind == ID_DER_ASN1_DN) + return match_dn(a->name, b->name, wildcards); + else + { + *wildcards = 0; + return same_id(a, b); + } +} + +/* count the numer of wildcards in an id */ +int +id_count_wildcards(const struct id *id) +{ + switch (id->kind) + { + case ID_NONE: + return MAX_WILDCARDS; + case ID_DER_ASN1_DN: + return dn_count_wildcards(id->name); + default: + return 0; + } +} + +/* build an ID payload + * Note: no memory is allocated for the body of the payload (tl->ptr). + * We assume it will end up being a pointer into a sufficiently + * stable datastructure. It only needs to last a short time. + */ +void +build_id_payload(struct isakmp_ipsec_id *hd, chunk_t *tl, struct end *end) +{ + const struct id *id = resolve_myid(&end->id); + + zero(hd); + hd->isaiid_idtype = id->kind; + switch (id->kind) + { + case ID_NONE: + hd->isaiid_idtype = aftoinfo(addrtypeof(&end->host_addr))->id_addr; + tl->len = addrbytesptr(&end->host_addr + , (const unsigned char **)&tl->ptr); /* sets tl->ptr too */ + break; + case ID_FQDN: + case ID_USER_FQDN: + case ID_DER_ASN1_DN: + case ID_KEY_ID: + *tl = id->name; + break; + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + tl->len = addrbytesptr(&id->ip_addr + , (const unsigned char **)&tl->ptr); /* sets tl->ptr too */ + break; + default: + bad_case(id->kind); + } +} + +/* + * Local Variables: + * c-basic-offset:4 + * c-style: pluto + * End: + */ diff --git a/src/pluto/id.h b/src/pluto/id.h new file mode 100644 index 000000000..4fe9ef227 --- /dev/null +++ b/src/pluto/id.h @@ -0,0 +1,67 @@ +/* identity representation, as in IKE ID Payloads (RFC 2407 DOI 4.6.2.1) + * Copyright (C) 1999-2001 D. Hugh Redelmeier + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: id.h,v 1.5 2005/08/15 20:07:08 as Exp $ + */ + +#ifndef _ID_H +#define _ID_H + +#include "defs.h" + +struct id { + int kind; /* ID_* value */ + ip_address ip_addr; /* ID_IPV4_ADDR, ID_IPV6_ADDR */ + chunk_t name; /* ID_FQDN, ID_USER_FQDN (with @) */ + /* ID_KEY_ID, ID_DER_ASN_DN */ +}; + +extern void init_id(void); + +extern const struct id empty_id; /* ID_NONE */ + +enum myid_state { + MYID_UNKNOWN, /* not yet figured out */ + MYID_HOSTNAME, /* our current hostname */ + MYID_IP, /* our default IP address */ + MYID_SPECIFIED /* as specified by ipsec.conf */ +}; + +extern enum myid_state myid_state; +extern struct id myids[MYID_SPECIFIED+1]; /* %myid */ +extern char *myid_str[MYID_SPECIFIED+1]; /* strings */ +extern void set_myid(enum myid_state s, char *); +extern void show_myid_status(void); +#define resolve_myid(id) ((id)->kind == ID_MYID? &myids[myid_state] : (id)) +extern void set_myFQDN(void); + +extern err_t atoid(char *src, struct id *id, bool myid_ok); +extern int keyidtoa(char *dst, size_t dstlen, chunk_t keyid); +extern void iptoid(const ip_address *ip, struct id *id); +extern int idtoa(const struct id *id, char *dst, size_t dstlen); +#define IDTOA_BUF 512 +extern void escape_metachar(const char *src, char *dst, size_t dstlen); +struct end; /* forward declaration of tag (defined in connections.h) */ +extern void unshare_id_content(struct id *id); +extern void free_id_content(struct id *id); +extern bool same_id(const struct id *a, const struct id *b); +#define MAX_WILDCARDS 15 +extern bool match_id(const struct id *a, const struct id *b, int *wildcards); +extern int id_count_wildcards(const struct id *id); +#define id_is_ipaddr(id) ((id)->kind == ID_IPV4_ADDR || (id)->kind == ID_IPV6_ADDR) + +struct isakmp_ipsec_id; /* forward declaration of tag (defined in packet.h) */ +extern void + build_id_payload(struct isakmp_ipsec_id *hd, chunk_t *tl, struct end *end); + +#endif /* _ID_H */ diff --git a/src/pluto/ike_alg.c b/src/pluto/ike_alg.c new file mode 100644 index 000000000..1c6514b4b --- /dev/null +++ b/src/pluto/ike_alg.c @@ -0,0 +1,592 @@ +/* IKE modular algorithm handling interface + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ike_alg.c,v 1.6 2004/09/17 21:29:50 as Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" + +#include "state.h" +#include "packet.h" +#include "log.h" +#include "whack.h" +#include "spdb.h" +#include "alg_info.h" +#include "ike_alg.h" +#include "db_ops.h" +#include "connections.h" +#include "kernel.h" + +#define return_on(var, val) do { var=val;goto return_out; } while(0); + +/* + * IKE algorithm list handling - registration and lookup + */ + +/* Modular IKE algorithm storage structure */ + +static struct ike_alg *ike_alg_base[IKE_ALG_MAX+1] = {NULL, NULL}; + +/* + * return ike_algo object by {type, id} + */ +static struct ike_alg * +ike_alg_find(u_int algo_type, u_int algo_id, u_int keysize __attribute__((unused))) +{ + struct ike_alg *e = ike_alg_base[algo_type]; + + while (e != NULL && algo_id > e->algo_id) + { + e = e->algo_next; + } + return (e != NULL && e->algo_id == algo_id) ? e : NULL; +} + +/* + * "raw" ike_alg list adding function + */ +int +ike_alg_add(struct ike_alg* a) +{ + if (a->algo_type > IKE_ALG_MAX) + { + plog("ike_alg: Not added, invalid algorithm type"); + return -EINVAL; + } + + if (ike_alg_find(a->algo_type, a->algo_id, 0) != NULL) + { + plog("ike_alg: Not added, algorithm already exists"); + return -EEXIST; + } + + { + struct ike_alg **ep = &ike_alg_base[a->algo_type]; + struct ike_alg *e = *ep; + + while (e != NULL && a->algo_id > e->algo_id) + { + ep = &e->algo_next; + e = *ep; + } + *ep = a; + a->algo_next = e; + return 0; + } +} + +/* + * get IKE hash algorithm + */ +struct hash_desc *ike_alg_get_hasher(u_int alg) +{ + return (struct hash_desc *) ike_alg_find(IKE_ALG_HASH, alg, 0); +} + +/* + * get IKE encryption algorithm + */ +struct encrypt_desc *ike_alg_get_encrypter(u_int alg) +{ + return (struct encrypt_desc *) ike_alg_find(IKE_ALG_ENCRYPT, alg, 0); +} + +/* + * check if IKE hash algorithm is present + */ +bool +ike_alg_hash_present(u_int halg) +{ + return ike_alg_get_hasher(halg) != NULL; +} + +/* + * check if IKE encryption algorithm is present + */ +bool +ike_alg_enc_present(u_int ealg) +{ + return ike_alg_get_encrypter(ealg) != NULL; +} + +/* + * Validate and register IKE hash algorithm object + */ +int +ike_alg_register_hash(struct hash_desc *hash_desc) +{ + const char *alg_name = NULL; + int ret = 0; + + if (hash_desc->algo_id > OAKLEY_HASH_MAX) + { + plog ("ike_alg: hash alg=%d > max=%d" + , hash_desc->algo_id, OAKLEY_HASH_MAX); + return_on(ret,-EINVAL); + } + + if (hash_desc->hash_ctx_size > sizeof (union hash_ctx)) + { + plog ("ike_alg: hash alg=%d has ctx_size=%d > hash_ctx=%d" + , hash_desc->algo_id + , (int)hash_desc->hash_ctx_size + , (int)sizeof (union hash_ctx)); + return_on(ret,-EOVERFLOW); + } + + if (!(hash_desc->hash_init && hash_desc->hash_update && hash_desc->hash_final)) + { + plog ("ike_alg: hash alg=%d needs hash_init(), hash_update() and hash_final()" + , hash_desc->algo_id); + return_on(ret,-EINVAL); + } + + alg_name = enum_name(&oakley_hash_names, hash_desc->algo_id); + if (!alg_name) + { + plog ("ike_alg: hash alg=%d not found in constants.c:oakley_hash_names" + , hash_desc->algo_id); + alg_name = ""; + } + +return_out: + if (ret == 0) + ret = ike_alg_add((struct ike_alg *)hash_desc); + + plog("ike_alg: Activating %s hash: %s" + ,alg_name, ret == 0 ? "Ok" : "FAILED"); + + return ret; +} + +/* + * Validate and register IKE encryption algorithm object + */ +int +ike_alg_register_enc(struct encrypt_desc *enc_desc) +{ + int ret = ike_alg_add((struct ike_alg *)enc_desc); + + const char *alg_name = enum_name(&oakley_enc_names, enc_desc->algo_id); + + char alg_number[20]; + + /* algorithm is not listed in oakley_enc_names */ + if (alg_name == NULL) + { + snprintf(alg_number, sizeof(alg_number), "OAKLEY_ID_%d" + , enc_desc->algo_id); + alg_name = alg_number; + } + + plog("ike_alg: Activating %s encryption: %s" + , alg_name, ret == 0 ? "Ok" : "FAILED"); + + return ret; +} + +/* + * Get pfsgroup for this connection + */ +const struct oakley_group_desc * +ike_alg_pfsgroup(struct connection *c, lset_t policy) +{ + const struct oakley_group_desc * ret = NULL; + + if ((policy & POLICY_PFS) + && c->alg_info_esp + && c->alg_info_esp->esp_pfsgroup) + ret = lookup_group(c->alg_info_esp->esp_pfsgroup); + return ret; +} + +/* + * Create an OAKLEY proposal based on alg_info and policy + */ +struct db_context * +ike_alg_db_new(struct alg_info_ike *ai , lset_t policy) +{ + struct db_context *db_ctx = NULL; + struct ike_info *ike_info; + struct encrypt_desc *enc_desc; + u_int ealg, halg, modp, eklen = 0; + int i; + + bool is_xauth_server = (policy & POLICY_XAUTH_SERVER) != LEMPTY; + + if (!ai) + { + whack_log(RC_LOG_SERIOUS, "no IKE algorithms " + "for this connection " + "(check ike algorithm string)"); + goto fail; + } + policy &= POLICY_ID_AUTH_MASK; + db_ctx = db_prop_new(PROTO_ISAKMP, 8, 8 * 5); + + /* for each group */ + ALG_INFO_IKE_FOREACH(ai, ike_info, i) + { + ealg = ike_info->ike_ealg; + halg = ike_info->ike_halg; + modp = ike_info->ike_modp; + eklen= ike_info->ike_eklen; + + if (!ike_alg_enc_present(ealg)) + { + DBG_log("ike_alg: ike enc ealg=%d not present" + , ealg); + continue; + } + + if (!ike_alg_hash_present(halg)) + { + DBG_log("ike_alg: ike hash halg=%d not present" + , halg); + continue; + } + + enc_desc = ike_alg_get_encrypter(ealg); + passert(enc_desc != NULL); + + if (eklen + && (eklen < enc_desc->keyminlen || eklen > enc_desc->keymaxlen)) + { + DBG_log("ike_alg: ealg=%d (specified) keylen:%d, not valid min=%d, max=%d" + , ealg + , eklen + , enc_desc->keyminlen + , enc_desc->keymaxlen + ); + continue; + } + + if (policy & POLICY_RSASIG) + { + db_trans_add(db_ctx, KEY_IKE); + db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg); + db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg); + if (eklen) + db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen); + db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG); + db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp); + } + + if (policy & POLICY_PSK) + { + db_trans_add(db_ctx, KEY_IKE); + db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg); + db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg); + if (eklen) + db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen); + db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY); + db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp); + } + + if (policy & POLICY_XAUTH_RSASIG) + { + db_trans_add(db_ctx, KEY_IKE); + db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg); + db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg); + if (eklen) + db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen); + db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD + , is_xauth_server ? XAUTHRespRSA : XAUTHInitRSA); + db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp); + } + + if (policy & POLICY_XAUTH_PSK) + { + db_trans_add(db_ctx, KEY_IKE); + db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg); + db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg); + if (eklen) + db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen); + db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD + , is_xauth_server ? XAUTHRespPreShared : XAUTHInitPreShared); + db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp); + } + } +fail: + return db_ctx; +} + +/* + * Show registered IKE algorithms + */ +void +ike_alg_list(void) +{ + u_int i; + struct ike_alg *a; + + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of registered IKE Encryption Algorithms:"); + whack_log(RC_COMMENT, " "); + + for (a = ike_alg_base[IKE_ALG_ENCRYPT]; a != NULL; a = a->algo_next) + { + struct encrypt_desc *desc = (struct encrypt_desc*)a; + + whack_log(RC_COMMENT, "#%-5d %s, blocksize: %d, keylen: %d-%d-%d" + , a->algo_id + , enum_name(&oakley_enc_names, a->algo_id) + , (int)desc->enc_blocksize*BITS_PER_BYTE + , desc->keyminlen + , desc->keydeflen + , desc->keymaxlen + ); + } + + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of registered IKE Hash Algorithms:"); + whack_log(RC_COMMENT, " "); + + for (a = ike_alg_base[IKE_ALG_HASH]; a != NULL; a = a->algo_next) + { + whack_log(RC_COMMENT, "#%-5d %s, hashsize: %d" + , a->algo_id + , enum_name(&oakley_hash_names, a->algo_id) + , (int)((struct hash_desc *)a)->hash_digest_size*BITS_PER_BYTE + ); + } + + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of registered IKE DH Groups:"); + whack_log(RC_COMMENT, " "); + + for (i = 0; i < elemsof(oakley_group); i++) + { + const struct oakley_group_desc *gdesc=oakley_group + i; + + whack_log(RC_COMMENT, "#%-5d %s, groupsize: %d" + , gdesc->group + , enum_name(&oakley_group_names, gdesc->group) + , (int)gdesc->bytes*BITS_PER_BYTE + ); + } +} + +/* Show IKE algorithms for + * - this connection (result from ike= string) + * - newest SA + */ +void +ike_alg_show_connection(struct connection *c, const char *instance) +{ + char buf[256]; + struct state *st; + + if (c->alg_info_ike) + { + alg_info_snprint(buf, sizeof(buf)-1, (struct alg_info *)c->alg_info_ike); + whack_log(RC_COMMENT + , "\"%s\"%s: IKE algorithms wanted: %s" + , c->name + , instance + , buf + ); + + alg_info_snprint_ike(buf, sizeof(buf)-1, c->alg_info_ike); + whack_log(RC_COMMENT + , "\"%s\"%s: IKE algorithms found: %s" + , c->name + , instance + , buf + ); + } + + st = state_with_serialno(c->newest_isakmp_sa); + if (st) + whack_log(RC_COMMENT + , "\"%s\"%s: IKE algorithm newest: %s_%d-%s-%s" + , c->name + , instance + , enum_show(&oakley_enc_names, st->st_oakley.encrypt) + +7 /* strlen("OAKLEY_") */ + /* , st->st_oakley.encrypter->keydeflen */ + , st->st_oakley.enckeylen + , enum_show(&oakley_hash_names, st->st_oakley.hash) + +7 /* strlen("OAKLEY_") */ + , enum_show(&oakley_group_names, st->st_oakley.group->group) + +13 /* strlen("OAKLEY_GROUP_") */ + ); +} + +/* + * Apply a suite of testvectors to a hash algorithm + */ +static bool +ike_hash_test(const struct hash_desc *desc) +{ + bool hash_results = TRUE; + bool hmac_results = TRUE; + + if (desc->hash_testvectors == NULL) + { + plog(" %s hash self-test not available", enum_name(&oakley_hash_names, desc->algo_id)); + } + else + { + int i; + + for (i = 0; desc->hash_testvectors[i].msg_digest != NULL; i++) + { + u_char digest[MAX_DIGEST_LEN]; + bool result; + + union hash_ctx ctx; + + desc->hash_init(&ctx); + desc->hash_update(&ctx, desc->hash_testvectors[i].msg + ,desc->hash_testvectors[i].msg_size); + desc->hash_final(digest, &ctx); + result = memcmp(digest, desc->hash_testvectors[i].msg_digest + , desc->hash_digest_size) == 0; + DBG(DBG_CRYPT, + DBG_log(" hash testvector %d: %s", i, result ? "ok":"failed") + ) + hash_results &= result; + } + plog(" %s hash self-test %s", enum_name(&oakley_hash_names, desc->algo_id) + , hash_results ? "passed":"failed"); + } + + if (desc->hmac_testvectors == NULL) + { + plog(" %s hmac self-test not available", enum_name(&oakley_hash_names, desc->algo_id)); + } + else + { + int i; + + for (i = 0; desc->hmac_testvectors[i].hmac != NULL; i++) + { + u_char digest[MAX_DIGEST_LEN]; + bool result; + + struct hmac_ctx ctx; + + hmac_init(&ctx, desc, desc->hmac_testvectors[i].key + , desc->hmac_testvectors[i].key_size); + hmac_update(&ctx, desc->hmac_testvectors[i].msg + ,desc->hmac_testvectors[i].msg_size); + hmac_final(digest, &ctx); + result = memcmp(digest, desc->hmac_testvectors[i].hmac + , desc->hash_digest_size) == 0; + DBG(DBG_CRYPT, + DBG_log(" hmac testvector %d: %s", i, result ? "ok":"failed") + ) + hmac_results &= result; + } + plog(" %s hmac self-test %s", enum_name(&oakley_hash_names, desc->algo_id) + , hmac_results ? "passed":"failed"); + } + return hash_results && hmac_results; +} + +/* + * Apply test vectors to registered encryption and hash algorithms + */ +bool +ike_alg_test(void) +{ + bool all_results = TRUE; + struct ike_alg *a; + + plog("Testing registered IKE encryption algorithms:"); + + for (a = ike_alg_base[IKE_ALG_ENCRYPT]; a != NULL; a = a->algo_next) + { + + struct encrypt_desc *desc = (struct encrypt_desc*)a; + + plog(" %s self-test not available", enum_name(&oakley_enc_names, a->algo_id)); + } + + plog("Testing registered IKE hash algorithms:"); + + for (a = ike_alg_base[IKE_ALG_HASH]; a != NULL; a = a->algo_next) + { + struct hash_desc *desc = (struct hash_desc*)a; + + all_results &= ike_hash_test(desc); + } + + if (all_results) + plog("All crypto self-tests passed"); + else + plog("Some crypto self-tests failed"); + return all_results; +} + +/* + * ML: make F_STRICT logic consider enc,hash/auth,modp algorithms + */ +bool +ike_alg_ok_final(u_int ealg, u_int key_len, u_int aalg, u_int group +, struct alg_info_ike *alg_info_ike) +{ + /* + * simple test to discard low key_len, will accept it only + * if specified in "esp" string + */ + bool ealg_insecure = (key_len < 128); + + if (ealg_insecure + || (alg_info_ike && alg_info_ike->alg_info_flags & ALG_INFO_F_STRICT)) + { + int i; + struct ike_info *ike_info; + + if (alg_info_ike) + { + ALG_INFO_IKE_FOREACH(alg_info_ike, ike_info, i) + { + if (ike_info->ike_ealg == ealg + && (ike_info->ike_eklen == 0 || key_len == 0 || ike_info->ike_eklen == key_len) + && ike_info->ike_halg == aalg + && ike_info->ike_modp == group) + { + if (ealg_insecure) + loglog(RC_LOG_SERIOUS, "You should NOT use insecure IKE algorithms (%s)!" + , enum_name(&oakley_enc_names, ealg)); + return TRUE; + } + } + } + plog("Oakley Transform [%s (%d), %s, %s] refused due to %s" + , enum_name(&oakley_enc_names, ealg), key_len + , enum_name(&oakley_hash_names, aalg) + , enum_name(&oakley_group_names, group) + , ealg_insecure ? + "insecure key_len and enc. alg. not listed in \"ike\" string" : "strict flag" + ); + return FALSE; + } + return TRUE; +} + diff --git a/src/pluto/ike_alg.h b/src/pluto/ike_alg.h new file mode 100644 index 000000000..19e2e591c --- /dev/null +++ b/src/pluto/ike_alg.h @@ -0,0 +1,94 @@ +/* IKE modular algorithm handling interface + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ike_alg.h,v 1.3 2004/09/16 23:22:22 as Exp $ + */ + +#ifndef _IKE_ALG_H +#define _IKE_ALG_H + +#include "connections.h" + +struct ike_alg { + u_int16_t algo_type; + u_int16_t algo_id; + struct ike_alg *algo_next; +}; + +struct encrypt_desc { + u_int16_t algo_type; + u_int16_t algo_id; + struct ike_alg *algo_next; + + size_t enc_ctxsize; + size_t enc_blocksize; + u_int keydeflen; + u_int keymaxlen; + u_int keyminlen; + void (*do_crypt)(u_int8_t *dat, size_t datasize, u_int8_t *key, size_t key_size, u_int8_t *iv, bool enc); +}; + +typedef struct hash_testvector hash_testvector_t; + +struct hash_testvector { + const size_t msg_size; + const u_char *msg; + const u_char *msg_digest; +}; + +typedef struct hmac_testvector hmac_testvector_t; + +struct hmac_testvector { + const size_t key_size; + const u_char *key; + const size_t msg_size; + const u_char *msg; + const u_char *hmac; +}; +struct hash_desc { + u_int16_t algo_type; + u_int16_t algo_id; + struct ike_alg *algo_next; + + size_t hash_ctx_size; + size_t hash_block_size; + size_t hash_digest_size; + const hash_testvector_t *hash_testvectors; + const hmac_testvector_t *hmac_testvectors; + void (*hash_init)(void *ctx); + void (*hash_update)(void *ctx, const u_int8_t *in, size_t datasize); + void (*hash_final)(u_int8_t *out, void *ctx); +}; + +#define IKE_ALG_ENCRYPT 0 +#define IKE_ALG_HASH 1 +#define IKE_ALG_MAX IKE_ALG_HASH + +extern int ike_alg_add(struct ike_alg *a); +extern struct hash_desc *ike_alg_get_hasher(u_int alg); +extern struct encrypt_desc *ike_alg_get_encrypter(u_int alg); +extern bool ike_alg_enc_present(u_int ealg); +extern bool ike_alg_hash_present(u_int halg); +extern int ike_alg_register_hash(struct hash_desc *a); +extern int ike_alg_register_enc(struct encrypt_desc *e); +extern const struct oakley_group_desc* ike_alg_pfsgroup(struct connection *c + , lset_t policy); +extern struct db_context * ike_alg_db_new(struct alg_info_ike *ai, lset_t policy); +extern void ike_alg_list(void); +extern void ike_alg_show_connection(struct connection *c, const char *instance); +extern bool ike_alg_test(void); +extern bool ike_alg_ok_final(u_int ealg, u_int key_len, u_int aalg, u_int group + , struct alg_info_ike *alg_info_ike); +extern int ike_alg_init(void); + +#endif /* _IKE_ALG_H */ diff --git a/src/pluto/ipsec.secrets.5 b/src/pluto/ipsec.secrets.5 new file mode 100644 index 000000000..3cce4d3f8 --- /dev/null +++ b/src/pluto/ipsec.secrets.5 @@ -0,0 +1,175 @@ +.TH IPSEC.SECRETS 5 "28 March 1999" +.SH NAME +ipsec.secrets \- secrets for IKE/IPsec authentication +.SH DESCRIPTION +The file \fIipsec.secrets\fP holds a table of secrets. +These secrets are used by \fIipsec_pluto\fP(8), the FreeS/WAN Internet Key +Exchange daemon, to authenticate other hosts. +Currently there are two kinds of secrets: preshared secrets and +.\" the private part of DSS keys. +RSA private keys. +.LP +It is vital that these secrets be protected. The file should be owned +by the super-user, +and its permissions should be set to block all access by others. +.LP +The file is a sequence of entries and include directives. +Here is an example. Each entry or directive must start at the +left margin, but if it continues beyond a single line, each continuation +line must be indented. +.LP +.RS +.nf +# sample /etc/ipsec.secrets file for 10.1.0.1 +10.1.0.1 10.2.0.1: PSK "secret shared by two hosts" + +# an entry may be split across lines, +# but indentation matters +www.xs4all.nl @www.kremvax.ru +\ \ \ \ 10.6.0.1 10.7.0.1 1.8.0.1: PSK "secret shared by 5" + +.\" # Private part of our DSS key, in base 64, +.\" # as generated by BIND 8.2.1's dnskeygen. +.\" # Since this is the default key for this host, +.\" # there is no need to specify indices. +.\" : DSS 0siMs0N/hfRoCBMXA6plPtuv58/+c= +# an RSA private key. +# note that the lines are too wide for a +# man page, so ... has been substituted for +# the truncated part +@my.com: rsa { +\ \ \ \ Modulus:\ 0syXpo/6waam+ZhSs8Lt6jnBzu3C4grtt... +\ \ \ \ PublicExponent:\ 0sAw== +\ \ \ \ PrivateExponent:\ 0shlGbVR1m8Z+7rhzSyenCaBN... +\ \ \ \ Prime1:\ 0s8njV7WTxzVzRz7AP+0OraDxmEAt1BL5l... +\ \ \ \ Prime2:\ 0s1LgR7/oUMo9BvfU8yRFNos1s211KX5K0... +\ \ \ \ Exponent1:\ 0soaXj85ihM5M2inVf/NfHmtLutVz4r... +\ \ \ \ Exponent2:\ 0sjdAL9VFizF+BKU4ohguJFzOd55OG6... +\ \ \ \ Coefficient:\ 0sK1LWwgnNrNFGZsS/2GuMBg9nYVZ... +\ \ \ \ } + +include ipsec.*.secrets # get secrets from other files +.fi +.RE +.LP +Each entry in the file is a list of indices, followed by a secret. +The two parts are separated by a colon (\fB:\fP) that is +followed by whitespace or a newline. For compatability +with the previous form of this file, if the key part is just a +double-quoted string the colon may be left out. +.LP +An index is an IP address, or a Fully Qualified Domain Name, user@FQDN, +\fB%any\fP or \fB%any6\fP (other kinds may come). An IP address may be written +in the familiar dotted quad form or as a domain name to be looked up +when the file is loaded +(or in any of the forms supported by the FreeS/WAN \fIipsec_ttoaddr\fP(3) +routine). In many cases it is a bad idea to use domain names because +the name server may not be running or may be insecure. To denote a +Fully Qualified Domain Name (as opposed to an IP address denoted by +its domain name), precede the name with an at sign (\fB@\fP). +.LP +Matching IDs with indices is fairly straightforward: they have to be +equal. In the case of a ``Road Warrior'' connection, if an equal +match is not found for the Peer's ID, and it is in the form of an IP +address, an index of \fB%any\fP will match the peer's IP address if IPV4 +and \fB%any6\fP will match a the peer's IP address if IPV6. +Currently, the obsolete notation \fB0.0.0.0\fP may be used in place of +\fB%any\fP. +.LP +An additional complexity +arises in the case of authentication by preshared secret: the +responder will need to look up the secret before the Peer's ID payload has +been decoded, so the ID used will be the IP address. +.LP +To authenticate a connection between two hosts, the entry that most +specifically matches the host and peer IDs is used. An entry with no +index will match any host and peer. More specifically, an entry with one index will +match a host and peer if the index matches the host's ID (the peer isn't +considered). Still more specifically, an entry with multiple indices will match a host and +peer if the host ID and peer ID each match one of the indices. If the key +is for an asymmetric authentication technique (i.e. a public key +system such as RSA), an entry with multiple indices will match a host +and peer even if only the host ID matches an index (it is presumed that the +multiple indices are all identities of the host). +It is acceptable for two entries to be the best match as +long as they agree about the secret or private key. +.LP +Authentication by preshared secret requires that both systems find the +identical secret (the secret is not actually transmitted by the IKE +protocol). If both the host and peer appear in the index list, the +same entry will be suitable for both systems so verbatim copying +between systems can be used. This naturally extends to larger groups +sharing the same secret. Thus multiple-index entries are best for PSK +authentication. +.LP +Authentication by RSA Signatures requires that each host have its own private +key. A host could reasonably use a different private keys +for different interfaces and for different peers. But it would not +be normal to share entries between systems. Thus thus no-index and +one-index forms of entry often make sense for RSA Signature authentication. +.LP +The key part of an entry may start with a token indicating the kind of +key. ``RSA'' signifies RSA private key and ``PSK'' signifies +PreShared Key (case is ignored). For compatability with previous +forms of this file, PSK is the default. +.LP +A preshared secret is most conveniently represented as a sequence of +characters, delimited by the double-quote +character (\fB"\fP). The sequence cannot contain a newline or +double-quote. Strictly speaking, the secret is actually the sequence +of bytes that is used in the file to represent the sequence of +characters (excluding the delimiters). +A preshared secret may also be represented, without quotes, in any form supported by +\fIipsec_ttodata\fP(3). +.LP +An RSA private key is a composite of eight generally large numbers. The notation +used is a brace-enclosed list of field name and value pairs (see the example above). +A suitable key, in a suitable format, may be generated by \fIipsec_rsasigkey\fP(8). +The structure is very similar to that used by BIND 8.2.2 or later, but note that +the numbers must have a ``0s'' prefix if they are in base 64. The order of +the fields is fixed. +.LP +The first token an entry must start in +the first column of its line. Subsequent tokens must be +separated by whitespace, +except for a colon token, which only needs to be followed by whitespace. +A newline is taken as whitespace, but every +line of an entry after the first must be indented. +.LP +Whitespace at the end of a line is ignored (except in the 0t +notation for a key). At the start of line or +after whitespace, \fB#\fP and the following text up to the end of the +line is treated as a comment. Within entries, all lines must be +indented (except for lines with no tokens). +Outside entries, no line may be indented (this is to make sure that +the file layout reflects its structure). +.LP +An include directive causes the contents of the named file to be processed +before continuing with the current file. The filename is subject to +``globbing'' as in \fIsh\fP(1), so every file with a matching name +is processed. Includes may be nested to a modest +depth (10, currently). If the filename doesn't start with a \fB/\fP, the +directory containing the current file is prepended to the name. The +include directive is a line that starts with the word \fBinclude\fP, +followed by whitespace, followed by the filename (which must not contain +whitespace). +.SH FILES +/etc/ipsec.secrets +.SH SEE ALSO +The rest of the FreeS/WAN distribution, in particular +\fIipsec.conf\fP(5), +\fIipsec\fP(8), +\fIipsec_newhostkey\fP(8), +\fIipsec_rsasigkey\fP(8), +\fIipsec_showhostkey\fP(8), +\fIipsec_auto\fP(8) \fB\-\-rereadsecrets\fP, +and \fIipsec_pluto\fP(8) \fB\-\-listen\fP,. +.br +BIND 8.2.2 or later, ftp://ftp.isc.org/isc/bind/src/ +.SH HISTORY +Designed for the FreeS/WAN project + +by D. Hugh Redelmeier. +.SH BUGS +If an ID is \fB0.0.0.0\fP, it will match \fB%any\fP; +if it is \fB0::0\fP, it will match \fB%any6\fP. diff --git a/src/pluto/ipsec_doi.c b/src/pluto/ipsec_doi.c new file mode 100644 index 000000000..1c22b299b --- /dev/null +++ b/src/pluto/ipsec_doi.c @@ -0,0 +1,5630 @@ +/* IPsec DOI and Oakley resolution routines + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_doi.c,v 1.39 2006/04/22 21:59:20 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* missing from on old systems */ +#include +#include /* for gettimeofday */ + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "state.h" +#include "id.h" +#include "x509.h" +#include "crl.h" +#include "ca.h" +#include "certs.h" +#include "smartcard.h" +#include "connections.h" +#include "keys.h" +#include "packet.h" +#include "demux.h" /* needs packet.h */ +#include "adns.h" /* needs */ +#include "dnskey.h" /* needs keys.h and adns.h */ +#include "kernel.h" +#include "log.h" +#include "cookie.h" +#include "server.h" +#include "spdb.h" +#include "timer.h" +#include "rnd.h" +#include "ipsec_doi.h" /* needs demux.h and state.h */ +#include "whack.h" +#include "fetch.h" +#include "pkcs7.h" +#include "asn1.h" + +#include "sha1.h" +#include "md5.h" +#include "crypto.h" /* requires sha1.h and md5.h */ +#include "vendor.h" +#include "alg_info.h" +#include "ike_alg.h" +#include "kernel_alg.h" +#include "nat_traversal.h" +#include "virtual.h" + +/* + * are we sending Pluto's Vendor ID? + */ +#ifdef VENDORID +#define SEND_PLUTO_VID 1 +#else /* !VENDORID */ +#define SEND_PLUTO_VID 0 +#endif /* !VENDORID */ + +/* + * are we sending a Cisco Unity VID? + */ +#ifdef CISCO_QUIRKS +#define SEND_CISCO_UNITY_VID 1 +#else /* !CISCO_QUIRKS */ +#define SEND_CISCO_UNITY_VID 0 +#endif /* !CISCO_QUIRKS */ + +/* MAGIC: perform f, a function that returns notification_t + * and return from the ENCLOSING stf_status returning function if it fails. + */ +#define RETURN_STF_FAILURE(f) \ + { int r = (f); if (r != NOTHING_WRONG) return STF_FAIL + r; } + +/* create output HDR as replica of input HDR */ +void +echo_hdr(struct msg_digest *md, bool enc, u_int8_t np) +{ + struct isakmp_hdr r_hdr = md->hdr; /* mostly same as incoming header */ + + r_hdr.isa_flags &= ~ISAKMP_FLAG_COMMIT; /* we won't ever turn on this bit */ + if (enc) + r_hdr.isa_flags |= ISAKMP_FLAG_ENCRYPTION; + /* some day, we may have to set r_hdr.isa_version */ + r_hdr.isa_np = np; + if (!out_struct(&r_hdr, &isakmp_hdr_desc, &md->reply, &md->rbody)) + impossible(); /* surely must have room and be well-formed */ +} + +/* Compute DH shared secret from our local secret and the peer's public value. + * We make the leap that the length should be that of the group + * (see quoted passage at start of ACCEPT_KE). + */ +static void +compute_dh_shared(struct state *st, const chunk_t g +, const struct oakley_group_desc *group) +{ + MP_INT mp_g, mp_shared; + struct timeval tv0, tv1; + unsigned long tv_diff; + + gettimeofday(&tv0, NULL); + passert(st->st_sec_in_use); + n_to_mpz(&mp_g, g.ptr, g.len); + mpz_init(&mp_shared); + mpz_powm(&mp_shared, &mp_g, &st->st_sec, group->modulus); + mpz_clear(&mp_g); + freeanychunk(st->st_shared); /* happens in odd error cases */ + st->st_shared = mpz_to_n(&mp_shared, group->bytes); + mpz_clear(&mp_shared); + gettimeofday(&tv1, NULL); + tv_diff=(tv1.tv_sec - tv0.tv_sec) * 1000000 + (tv1.tv_usec - tv0.tv_usec); + DBG(DBG_CRYPT, + DBG_log("compute_dh_shared(): time elapsed (%s): %ld usec" + , enum_show(&oakley_group_names, st->st_oakley.group->group) + , tv_diff); + ); + /* if took more than 200 msec ... */ + if (tv_diff > 200000) { + loglog(RC_LOG_SERIOUS, "WARNING: compute_dh_shared(): for %s took " + "%ld usec" + , enum_show(&oakley_group_names, st->st_oakley.group->group) + , tv_diff); + } + + DBG_cond_dump_chunk(DBG_CRYPT, "DH shared secret:\n", st->st_shared); +} + +/* if we haven't already done so, compute a local DH secret (st->st_sec) and + * the corresponding public value (g). This is emitted as a KE payload. + */ +static bool +build_and_ship_KE(struct state *st, chunk_t *g +, const struct oakley_group_desc *group, pb_stream *outs, u_int8_t np) +{ + if (!st->st_sec_in_use) + { + u_char tmp[LOCALSECRETSIZE]; + MP_INT mp_g; + + get_rnd_bytes(tmp, LOCALSECRETSIZE); + st->st_sec_in_use = TRUE; + n_to_mpz(&st->st_sec, tmp, LOCALSECRETSIZE); + + mpz_init(&mp_g); + mpz_powm(&mp_g, &groupgenerator, &st->st_sec, group->modulus); + freeanychunk(*g); /* happens in odd error cases */ + *g = mpz_to_n(&mp_g, group->bytes); + mpz_clear(&mp_g); + DBG(DBG_CRYPT, + DBG_dump("Local DH secret:\n", tmp, LOCALSECRETSIZE); + DBG_dump_chunk("Public DH value sent:\n", *g)); + } + return out_generic_chunk(np, &isakmp_keyex_desc, outs, *g, "keyex value"); +} + +/* accept_ke + * + * Check and accept DH public value (Gi or Gr) from peer's message. + * According to RFC2409 "The Internet key exchange (IKE)" 5: + * The Diffie-Hellman public value passed in a KE payload, in either + * a phase 1 or phase 2 exchange, MUST be the length of the negotiated + * Diffie-Hellman group enforced, if necessary, by pre-pending the + * value with zeros. + */ +static notification_t +accept_KE(chunk_t *dest, const char *val_name +, const struct oakley_group_desc *gr +, pb_stream *pbs) +{ + if (pbs_left(pbs) != gr->bytes) + { + loglog(RC_LOG_SERIOUS, "KE has %u byte DH public value; %u required" + , (unsigned) pbs_left(pbs), (unsigned) gr->bytes); + /* XXX Could send notification back */ + return INVALID_KEY_INFORMATION; + } + clonereplacechunk(*dest, pbs->cur, pbs_left(pbs), val_name); + DBG_cond_dump_chunk(DBG_CRYPT, "DH public value received:\n", *dest); + return NOTHING_WRONG; +} + +/* accept_PFS_KE + * + * Check and accept optional Quick Mode KE payload for PFS. + * Extends ACCEPT_PFS to check whether KE is allowed or required. + */ +static notification_t +accept_PFS_KE(struct msg_digest *md, chunk_t *dest +, const char *val_name, const char *msg_name) +{ + struct state *st = md->st; + struct payload_digest *const ke_pd = md->chain[ISAKMP_NEXT_KE]; + + if (ke_pd == NULL) + { + if (st->st_pfs_group != NULL) + { + loglog(RC_LOG_SERIOUS, "missing KE payload in %s message", msg_name); + return INVALID_KEY_INFORMATION; + } + } + else + { + if (st->st_pfs_group == NULL) + { + loglog(RC_LOG_SERIOUS, "%s message KE payload requires a GROUP_DESCRIPTION attribute in SA" + , msg_name); + return INVALID_KEY_INFORMATION; + } + if (ke_pd->next != NULL) + { + loglog(RC_LOG_SERIOUS, "%s message contains several KE payloads; we accept at most one", msg_name); + return INVALID_KEY_INFORMATION; /* ??? */ + } + return accept_KE(dest, val_name, st->st_pfs_group, &ke_pd->pbs); + } + return NOTHING_WRONG; +} + +static bool +build_and_ship_nonce(chunk_t *n, pb_stream *outs, u_int8_t np +, const char *name) +{ + freeanychunk(*n); + setchunk(*n, alloc_bytes(DEFAULT_NONCE_SIZE, name), DEFAULT_NONCE_SIZE); + get_rnd_bytes(n->ptr, DEFAULT_NONCE_SIZE); + return out_generic_chunk(np, &isakmp_nonce_desc, outs, *n, name); +} + +static bool +collect_rw_ca_candidates(struct msg_digest *md, generalName_t **top) +{ + struct connection *d = find_host_connection(&md->iface->addr + , pluto_port, (ip_address*)NULL, md->sender_port, LEMPTY); + + for (; d != NULL; d = d->hp_next) + { + /* must be a road warrior connection */ + if (d->kind == CK_TEMPLATE && !(d->policy & POLICY_OPPO) + && d->spd.that.ca.ptr != NULL) + { + generalName_t *gn; + bool new_entry = TRUE; + + for (gn = *top; gn != NULL; gn = gn->next) + { + if (same_dn(gn->name, d->spd.that.ca)) + { + new_entry = FALSE; + break; + } + } + if (new_entry) + { + gn = alloc_thing(generalName_t, "generalName"); + gn->kind = GN_DIRECTORY_NAME; + gn->name = d->spd.that.ca; + gn->next = *top; + *top = gn; + } + } + } + return *top != NULL; +} + +static bool +build_and_ship_CR(u_int8_t type, chunk_t ca, pb_stream *outs, u_int8_t np) +{ + pb_stream cr_pbs; + struct isakmp_cr cr_hd; + cr_hd.isacr_np = np; + cr_hd.isacr_type = type; + + /* build CR header */ + if (!out_struct(&cr_hd, &isakmp_ipsec_cert_req_desc, outs, &cr_pbs)) + return FALSE; + + if (ca.ptr != NULL) + { + /* build CR body containing the distinguished name of the CA */ + if (!out_chunk(ca, &cr_pbs, "CA")) + return FALSE; + } + close_output_pbs(&cr_pbs); + return TRUE; +} + +/* Send a notification to the peer. We could decide + * whether to send the notification, based on the type and the + * destination, if we care to. + */ +static void +send_notification(struct state *sndst, u_int16_t type, struct state *encst, + msgid_t msgid, u_char *icookie, u_char *rcookie, + u_char *spi, size_t spisize, u_char protoid) +{ + u_char buffer[1024]; + pb_stream pbs, r_hdr_pbs; + u_char *r_hashval = NULL; /* where in reply to jam hash value */ + u_char *r_hash_start = NULL; /* start of what is to be hashed */ + + passert((sndst) && (sndst->st_connection)); + + plog("sending %snotification %s to %s:%u" + , encst ? "encrypted " : "" + , enum_name(¬ification_names, type) + , ip_str(&sndst->st_connection->spd.that.host_addr) + , (unsigned)sndst->st_connection->spd.that.host_port); + + memset(buffer, 0, sizeof(buffer)); + init_pbs(&pbs, buffer, sizeof(buffer), "ISAKMP notify"); + + /* HDR* */ + { + struct isakmp_hdr hdr; + + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = encst ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_N; + hdr.isa_xchg = ISAKMP_XCHG_INFO; + hdr.isa_msgid = msgid; + hdr.isa_flags = encst ? ISAKMP_FLAG_ENCRYPTION : 0; + if (icookie) + memcpy(hdr.isa_icookie, icookie, COOKIE_SIZE); + if (rcookie) + memcpy(hdr.isa_rcookie, rcookie, COOKIE_SIZE); + if (!out_struct(&hdr, &isakmp_hdr_desc, &pbs, &r_hdr_pbs)) + impossible(); + } + + /* HASH -- value to be filled later */ + if (encst) + { + pb_stream hash_pbs; + if (!out_generic(ISAKMP_NEXT_N, &isakmp_hash_desc, &r_hdr_pbs, + &hash_pbs)) + impossible(); + r_hashval = hash_pbs.cur; /* remember where to plant value */ + if (!out_zero( + encst->st_oakley.hasher->hash_digest_size, &hash_pbs, "HASH")) + impossible(); + close_output_pbs(&hash_pbs); + r_hash_start = r_hdr_pbs.cur; /* hash from after HASH */ + } + + /* Notification Payload */ + { + pb_stream not_pbs; + struct isakmp_notification isan; + + isan.isan_doi = ISAKMP_DOI_IPSEC; + isan.isan_np = ISAKMP_NEXT_NONE; + isan.isan_type = type; + isan.isan_spisize = spisize; + isan.isan_protoid = protoid; + + if (!out_struct(&isan, &isakmp_notification_desc, &r_hdr_pbs, ¬_pbs) + || !out_raw(spi, spisize, ¬_pbs, "spi")) + impossible(); + close_output_pbs(¬_pbs); + } + + /* calculate hash value and patch into Hash Payload */ + if (encst) + { + struct hmac_ctx ctx; + hmac_init_chunk(&ctx, encst->st_oakley.hasher, encst->st_skeyid_a); + hmac_update(&ctx, (u_char *) &msgid, sizeof(msgid_t)); + hmac_update(&ctx, r_hash_start, r_hdr_pbs.cur-r_hash_start); + hmac_final(r_hashval, &ctx); + + DBG(DBG_CRYPT, + DBG_log("HASH computed:"); + DBG_dump("", r_hashval, ctx.hmac_digest_size); + ) + } + + /* Encrypt message (preserve st_iv and st_new_iv) */ + if (encst) + { + u_char old_iv[MAX_DIGEST_LEN]; + u_char new_iv[MAX_DIGEST_LEN]; + + u_int old_iv_len = encst->st_iv_len; + u_int new_iv_len = encst->st_new_iv_len; + + if (old_iv_len > MAX_DIGEST_LEN || new_iv_len > MAX_DIGEST_LEN) + impossible(); + + memcpy(old_iv, encst->st_iv, old_iv_len); + memcpy(new_iv, encst->st_new_iv, new_iv_len); + + if (!IS_ISAKMP_SA_ESTABLISHED(encst->st_state)) + { + memcpy(encst->st_ph1_iv, encst->st_new_iv, encst->st_new_iv_len); + encst->st_ph1_iv_len = encst->st_new_iv_len; + } + init_phase2_iv(encst, &msgid); + if (!encrypt_message(&r_hdr_pbs, encst)) + impossible(); + + /* restore preserved st_iv and st_new_iv */ + memcpy(encst->st_iv, old_iv, old_iv_len); + memcpy(encst->st_new_iv, new_iv, new_iv_len); + encst->st_iv_len = old_iv_len; + encst->st_new_iv_len = new_iv_len; + } + else + { + close_output_pbs(&r_hdr_pbs); + } + + /* Send packet (preserve st_tpacket) */ + { + chunk_t saved_tpacket = sndst->st_tpacket; + + setchunk(sndst->st_tpacket, pbs.start, pbs_offset(&pbs)); + send_packet(sndst, "ISAKMP notify"); + sndst->st_tpacket = saved_tpacket; + } +} + +void +send_notification_from_state(struct state *st, enum state_kind state, + u_int16_t type) +{ + struct state *p1st; + + passert(st); + + if (state == STATE_UNDEFINED) + state = st->st_state; + + if (IS_QUICK(state)) + { + p1st = find_phase1_state(st->st_connection, ISAKMP_SA_ESTABLISHED_STATES); + if ((p1st == NULL) || (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state))) + { + loglog(RC_LOG_SERIOUS, + "no Phase1 state for Quick mode notification"); + return; + } + send_notification(st, type, p1st, generate_msgid(p1st), + st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP); + } + else if (IS_ISAKMP_ENCRYPTED(state) && st->st_enc_key.ptr != NULL) + { + send_notification(st, type, st, generate_msgid(st), + st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP); + } + else + { + /* no ISAKMP SA established - don't encrypt notification */ + send_notification(st, type, NULL, 0, + st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP); + } +} + +void +send_notification_from_md(struct msg_digest *md, u_int16_t type) +{ + /** + * Create a dummy state to be able to use send_packet in + * send_notification + * + * we need to set: + * st_connection->that.host_addr + * st_connection->that.host_port + * st_connection->interface + */ + struct state st; + struct connection cnx; + + passert(md); + + memset(&st, 0, sizeof(st)); + memset(&cnx, 0, sizeof(cnx)); + st.st_connection = &cnx; + cnx.spd.that.host_addr = md->sender; + cnx.spd.that.host_port = md->sender_port; + cnx.interface = md->iface; + + send_notification(&st, type, NULL, 0, + md->hdr.isa_icookie, md->hdr.isa_rcookie, NULL, 0, PROTO_ISAKMP); +} + +/* Send a Delete Notification to announce deletion of ISAKMP SA or + * inbound IPSEC SAs. Does nothing if no such SAs are being deleted. + * Delete Notifications cannot announce deletion of outbound IPSEC/ISAKMP SAs. + */ +void +send_delete(struct state *st) +{ + pb_stream reply_pbs; + pb_stream r_hdr_pbs; + msgid_t msgid; + u_char buffer[8192]; + struct state *p1st; + ip_said said[EM_MAXRELSPIS]; + ip_said *ns = said; + u_char + *r_hashval, /* where in reply to jam hash value */ + *r_hash_start; /* start of what is to be hashed */ + bool isakmp_sa = FALSE; + + if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) + { + p1st = find_phase1_state(st->st_connection, ISAKMP_SA_ESTABLISHED_STATES); + if (p1st == NULL) + { + DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete")); + return; + } + + if (st->st_ah.present) + { + ns->spi = st->st_ah.our_spi; + ns->dst = st->st_connection->spd.this.host_addr; + ns->proto = PROTO_IPSEC_AH; + ns++; + } + if (st->st_esp.present) + { + ns->spi = st->st_esp.our_spi; + ns->dst = st->st_connection->spd.this.host_addr; + ns->proto = PROTO_IPSEC_ESP; + ns++; + } + + passert(ns != said); /* there must be some SAs to delete */ + } + else if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + p1st = st; + isakmp_sa = TRUE; + } + else + { + return; /* nothing to do */ + } + + msgid = generate_msgid(p1st); + + zero(buffer); + init_pbs(&reply_pbs, buffer, sizeof(buffer), "delete msg"); + + /* HDR* */ + { + struct isakmp_hdr hdr; + + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_HASH; + hdr.isa_xchg = ISAKMP_XCHG_INFO; + hdr.isa_msgid = msgid; + hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; + memcpy(hdr.isa_icookie, p1st->st_icookie, COOKIE_SIZE); + memcpy(hdr.isa_rcookie, p1st->st_rcookie, COOKIE_SIZE); + if (!out_struct(&hdr, &isakmp_hdr_desc, &reply_pbs, &r_hdr_pbs)) + impossible(); + } + + /* HASH -- value to be filled later */ + { + pb_stream hash_pbs; + + if (!out_generic(ISAKMP_NEXT_D, &isakmp_hash_desc, &r_hdr_pbs, &hash_pbs)) + impossible(); + r_hashval = hash_pbs.cur; /* remember where to plant value */ + if (!out_zero(p1st->st_oakley.hasher->hash_digest_size, &hash_pbs, "HASH(1)")) + impossible(); + close_output_pbs(&hash_pbs); + r_hash_start = r_hdr_pbs.cur; /* hash from after HASH(1) */ + } + + /* Delete Payloads */ + if (isakmp_sa) + { + pb_stream del_pbs; + struct isakmp_delete isad; + u_char isakmp_spi[2*COOKIE_SIZE]; + + isad.isad_doi = ISAKMP_DOI_IPSEC; + isad.isad_np = ISAKMP_NEXT_NONE; + isad.isad_spisize = (2 * COOKIE_SIZE); + isad.isad_protoid = PROTO_ISAKMP; + isad.isad_nospi = 1; + + memcpy(isakmp_spi, st->st_icookie, COOKIE_SIZE); + memcpy(isakmp_spi+COOKIE_SIZE, st->st_rcookie, COOKIE_SIZE); + + if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs) + || !out_raw(&isakmp_spi, (2*COOKIE_SIZE), &del_pbs, "delete payload")) + impossible(); + close_output_pbs(&del_pbs); + } + else + { + while (ns != said) + { + + pb_stream del_pbs; + struct isakmp_delete isad; + + ns--; + isad.isad_doi = ISAKMP_DOI_IPSEC; + isad.isad_np = ns == said? ISAKMP_NEXT_NONE : ISAKMP_NEXT_D; + isad.isad_spisize = sizeof(ipsec_spi_t); + isad.isad_protoid = ns->proto; + + isad.isad_nospi = 1; + if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs) + || !out_raw(&ns->spi, sizeof(ipsec_spi_t), &del_pbs, "delete payload")) + impossible(); + close_output_pbs(&del_pbs); + } + } + + /* calculate hash value and patch into Hash Payload */ + { + struct hmac_ctx ctx; + hmac_init_chunk(&ctx, p1st->st_oakley.hasher, p1st->st_skeyid_a); + hmac_update(&ctx, (u_char *) &msgid, sizeof(msgid_t)); + hmac_update(&ctx, r_hash_start, r_hdr_pbs.cur-r_hash_start); + hmac_final(r_hashval, &ctx); + + DBG(DBG_CRYPT, + DBG_log("HASH(1) computed:"); + DBG_dump("", r_hashval, ctx.hmac_digest_size); + ) + } + + /* Do a dance to avoid needing a new state object. + * We use the Phase 1 State. This is the one with right + * IV, for one thing. + * The tricky bits are: + * - we need to preserve (save/restore) st_iv (but not st_iv_new) + * - we need to preserve (save/restore) st_tpacket. + */ + { + u_char old_iv[MAX_DIGEST_LEN]; + chunk_t saved_tpacket = p1st->st_tpacket; + + memcpy(old_iv, p1st->st_iv, p1st->st_iv_len); + init_phase2_iv(p1st, &msgid); + + if (!encrypt_message(&r_hdr_pbs, p1st)) + impossible(); + + setchunk(p1st->st_tpacket, reply_pbs.start, pbs_offset(&reply_pbs)); + send_packet(p1st, "delete notify"); + p1st->st_tpacket = saved_tpacket; + + /* get back old IV for this state */ + memcpy(p1st->st_iv, old_iv, p1st->st_iv_len); + } +} + +void +accept_delete(struct state *st, struct msg_digest *md, struct payload_digest *p) +{ + struct isakmp_delete *d = &(p->payload.delete); + size_t sizespi; + int i; + + if (!md->encrypted) + { + loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: not encrypted"); + return; + } + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + /* can't happen (if msg is encrypt), but just to be sure */ + loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: " + "ISAKMP SA not established"); + return; + } + + if (d->isad_nospi == 0) + { + loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: no SPI"); + return; + } + + switch (d->isad_protoid) + { + case PROTO_ISAKMP: + sizespi = 2 * COOKIE_SIZE; + break; + case PROTO_IPSEC_AH: + case PROTO_IPSEC_ESP: + sizespi = sizeof(ipsec_spi_t); + break; + case PROTO_IPCOMP: + /* nothing interesting to delete */ + return; + default: + loglog(RC_LOG_SERIOUS + , "ignoring Delete SA payload: unknown Protocol ID (%s)" + , enum_show(&protocol_names, d->isad_protoid)); + return; + } + + if (d->isad_spisize != sizespi) + { + loglog(RC_LOG_SERIOUS + , "ignoring Delete SA payload: bad SPI size (%d) for %s" + , d->isad_spisize, enum_show(&protocol_names, d->isad_protoid)); + return; + } + + if (pbs_left(&p->pbs) != d->isad_nospi * sizespi) + { + loglog(RC_LOG_SERIOUS + , "ignoring Delete SA payload: invalid payload size"); + return; + } + + for (i = 0; i < d->isad_nospi; i++) + { + u_char *spi = p->pbs.cur + (i * sizespi); + + if (d->isad_protoid == PROTO_ISAKMP) + { + /** + * ISAKMP + */ + struct state *dst = find_state(spi /*iCookie*/ + , spi+COOKIE_SIZE /*rCookie*/ + , &st->st_connection->spd.that.host_addr + , MAINMODE_MSGID); + + if (dst == NULL) + { + loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: " + "ISAKMP SA not found (maybe expired)"); + } + else if (!same_peer_ids(st->st_connection, dst->st_connection, NULL)) + { + /* we've not authenticated the relevant identities */ + loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: " + "ISAKMP SA used to convey Delete has different IDs from ISAKMP SA it deletes"); + } + else + { + struct connection *oldc; + + oldc = cur_connection; + set_cur_connection(dst->st_connection); + + if (nat_traversal_enabled) + nat_traversal_change_port_lookup(md, dst); + + loglog(RC_LOG_SERIOUS, "received Delete SA payload: " + "deleting ISAKMP State #%lu", dst->st_serialno); + delete_state(dst); + set_cur_connection(oldc); + } + } + else + { + /** + * IPSEC (ESP/AH) + */ + bool bogus; + struct state *dst = find_phase2_state_to_delete(st + , d->isad_protoid + , *(ipsec_spi_t *)spi /* network order */ + , &bogus); + + if (dst == NULL) + { + loglog(RC_LOG_SERIOUS + , "ignoring Delete SA payload: %s SA(0x%08lx) not found (%s)" + , enum_show(&protocol_names, d->isad_protoid) + , (unsigned long)ntohl((unsigned long)*(ipsec_spi_t *)spi) + , bogus ? "our SPI - bogus implementation" : "maybe expired"); + } + else + { + struct connection *rc = dst->st_connection; + struct connection *oldc; + + oldc = cur_connection; + set_cur_connection(rc); + + if (nat_traversal_enabled) + nat_traversal_change_port_lookup(md, dst); + + if (rc->newest_ipsec_sa == dst->st_serialno + && (rc->policy & POLICY_UP)) + { + /* Last IPSec SA for a permanent connection that we + * have initiated. Replace it in a few seconds. + * + * Useful if the other peer is rebooting. + */ +#define DELETE_SA_DELAY EVENT_RETRANSMIT_DELAY_0 + if (dst->st_event != NULL + && dst->st_event->ev_type == EVENT_SA_REPLACE + && dst->st_event->ev_time <= DELETE_SA_DELAY + now()) + { + /* Patch from Angus Lees to ignore retransmited + * Delete SA. + */ + loglog(RC_LOG_SERIOUS, "received Delete SA payload: " + "already replacing IPSEC State #%lu in %d seconds" + , dst->st_serialno, (int)(dst->st_event->ev_time - now())); + } + else + { + loglog(RC_LOG_SERIOUS, "received Delete SA payload: " + "replace IPSEC State #%lu in %d seconds" + , dst->st_serialno, DELETE_SA_DELAY); + dst->st_margin = DELETE_SA_DELAY; + delete_event(dst); + event_schedule(EVENT_SA_REPLACE, DELETE_SA_DELAY, dst); + } + } + else + { + loglog(RC_LOG_SERIOUS, "received Delete SA(0x%08lx) payload: " + "deleting IPSEC State #%lu" + , (unsigned long)ntohl((unsigned long)*(ipsec_spi_t *)spi) + , dst->st_serialno); + delete_state(dst); + } + + /* reset connection */ + set_cur_connection(oldc); + } + } + } +} + +/* The whole message must be a multiple of 4 octets. + * I'm not sure where this is spelled out, but look at + * rfc2408 3.6 Transform Payload. + * Note: it talks about 4 BYTE boundaries! + */ +void +close_message(pb_stream *pbs) +{ + size_t padding = pad_up(pbs_offset(pbs), 4); + + if (padding != 0) + (void) out_zero(padding, pbs, "message padding"); + close_output_pbs(pbs); +} + +/* Initiate an Oakley Main Mode exchange. + * --> HDR;SA + * Note: this is not called from demux.c + */ +static stf_status +main_outI1(int whack_sock, struct connection *c, struct state *predecessor + , lset_t policy, unsigned long try) +{ + struct state *st = new_state(); + pb_stream reply; /* not actually a reply, but you know what I mean */ + pb_stream rbody; + + int vids_to_send = 0; + + /* set up new state */ + st->st_connection = c; + set_cur_state(st); /* we must reset before exit */ + st->st_policy = policy & ~POLICY_IPSEC_MASK; + st->st_whack_sock = whack_sock; + st->st_try = try; + st->st_state = STATE_MAIN_I1; + + /* determine how many Vendor ID payloads we will be sending */ + if (SEND_PLUTO_VID) + vids_to_send++; + if (SEND_CISCO_UNITY_VID) + vids_to_send++; + if (c->spd.this.cert.type == CERT_PGP) + vids_to_send++; + /* always send XAUTH Vendor ID */ + vids_to_send++; + /* always send DPD Vendor ID */ + vids_to_send++; + if (nat_traversal_enabled) + vids_to_send++; + + get_cookie(TRUE, st->st_icookie, COOKIE_SIZE, &c->spd.that.host_addr); + + insert_state(st); /* needs cookies, connection, and msgid (0) */ + + if (HAS_IPSEC_POLICY(policy)) + add_pending(dup_any(whack_sock), st, c, policy, 1 + , predecessor == NULL? SOS_NOBODY : predecessor->st_serialno); + + if (predecessor == NULL) + plog("initiating Main Mode"); + else + plog("initiating Main Mode to replace #%lu", predecessor->st_serialno); + + /* set up reply */ + init_pbs(&reply, reply_buffer, sizeof(reply_buffer), "reply packet"); + + /* HDR out */ + { + struct isakmp_hdr hdr; + + zero(&hdr); /* default to 0 */ + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_SA; + hdr.isa_xchg = ISAKMP_XCHG_IDPROT; + memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); + /* R-cookie, flags and MessageID are left zero */ + + if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* SA out */ + { + u_char *sa_start = rbody.cur; + lset_t auth_policy = policy & POLICY_ID_AUTH_MASK; + + if (!out_sa(&rbody, &oakley_sadb, st, TRUE + , vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + + /* save initiator SA for later HASH */ + passert(st->st_p1isa.ptr == NULL); /* no leak! (MUST be first time) */ + clonetochunk(st->st_p1isa, sa_start, rbody.cur - sa_start + , "sa in main_outI1"); + } + + /* if enabled send Pluto Vendor ID */ + if (SEND_PLUTO_VID) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody, VID_STRONGSWAN)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* if enabled send Cisco Unity Vendor ID */ + if (SEND_CISCO_UNITY_VID) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody, VID_CISCO_UNITY)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + /* if we have an OpenPGP certificate we assume an + * OpenPGP peer and have to send the Vendor ID + */ + if (c->spd.this.cert.type == CERT_PGP) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody, VID_OPENPGP)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* Announce our ability to do eXtended AUTHentication to the peer */ + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody, VID_MISC_XAUTH)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + + /* Announce our ability to do Dead Peer Detection to the peer */ + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody, VID_MISC_DPD)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + if (nat_traversal_enabled) + { + /* Add supported NAT-Traversal VID */ + if (!nat_traversal_add_vid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + close_message(&rbody); + close_output_pbs(&reply); + + clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply) + , "reply packet for main_outI1"); + + /* Transmit */ + + send_packet(st, "main_outI1"); + + /* Set up a retransmission event, half a minute henceforth */ + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); + + if (predecessor != NULL) + { + update_pending(predecessor, st); + whack_log(RC_NEW_STATE + STATE_MAIN_I1 + , "%s: initiate, replacing #%lu" + , enum_name(&state_names, st->st_state) + , predecessor->st_serialno); + } + else + { + whack_log(RC_NEW_STATE + STATE_MAIN_I1 + , "%s: initiate", enum_name(&state_names, st->st_state)); + } + reset_cur_state(); + return STF_OK; +} + +void +ipsecdoi_initiate(int whack_sock +, struct connection *c +, lset_t policy +, unsigned long try +, so_serial_t replacing) +{ + /* If there's already an ISAKMP SA established, use that and + * go directly to Quick Mode. We are even willing to use one + * that is still being negotiated, but only if we are the Initiator + * (thus we can be sure that the IDs are not going to change; + * other issues around intent might matter). + * Note: there is no way to initiate with a Road Warrior. + */ + struct state *st = find_phase1_state(c + , ISAKMP_SA_ESTABLISHED_STATES | PHASE1_INITIATOR_STATES); + + if (st == NULL) + { + (void) main_outI1(whack_sock, c, NULL, policy, try); + } + else if (HAS_IPSEC_POLICY(policy)) + { + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + /* leave our Phase 2 negotiation pending */ + add_pending(whack_sock, st, c, policy, try, replacing); + } + else + { + /* ??? we assume that peer_nexthop_sin isn't important: + * we already have it from when we negotiated the ISAKMP SA! + * It isn't clear what to do with the error return. + */ + (void) quick_outI1(whack_sock, st, c, policy, try, replacing); + } + } + else + { + close_any(whack_sock); + } +} + +/* Replace SA with a fresh one that is similar + * + * Shares some logic with ipsecdoi_initiate, but not the same! + * - we must not reuse the ISAKMP SA if we are trying to replace it! + * - if trying to replace IPSEC SA, use ipsecdoi_initiate to build + * ISAKMP SA if needed. + * - duplicate whack fd, if live. + * Does not delete the old state -- someone else will do that. + */ +void +ipsecdoi_replace(struct state *st, unsigned long try) +{ + int whack_sock = dup_any(st->st_whack_sock); + lset_t policy = st->st_policy; + + if (IS_PHASE1(st->st_state)) + { + passert(!HAS_IPSEC_POLICY(policy)); + (void) main_outI1(whack_sock, st->st_connection, st, policy, try); + } + else + { + /* Add features of actual old state to policy. This ensures + * that rekeying doesn't downgrade security. I admit that + * this doesn't capture everything. + */ + if (st->st_pfs_group != NULL) + policy |= POLICY_PFS; + if (st->st_ah.present) + { + policy |= POLICY_AUTHENTICATE; + if (st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) + policy |= POLICY_TUNNEL; + } + if (st->st_esp.present && st->st_esp.attrs.transid != ESP_NULL) + { + policy |= POLICY_ENCRYPT; + if (st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) + policy |= POLICY_TUNNEL; + } + if (st->st_ipcomp.present) + { + policy |= POLICY_COMPRESS; + if (st->st_ipcomp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) + policy |= POLICY_TUNNEL; + } + passert(HAS_IPSEC_POLICY(policy)); + ipsecdoi_initiate(whack_sock, st->st_connection, policy, try + , st->st_serialno); + } +} + +/* SKEYID for preshared keys. + * See draft-ietf-ipsec-ike-01.txt 4.1 + */ +static bool +skeyid_preshared(struct state *st) +{ + const chunk_t *pss = get_preshared_secret(st->st_connection); + + if (pss == NULL) + { + loglog(RC_LOG_SERIOUS, "preshared secret disappeared!"); + return FALSE; + } + else + { + struct hmac_ctx ctx; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, *pss); + hmac_update_chunk(&ctx, st->st_ni); + hmac_update_chunk(&ctx, st->st_nr); + hmac_final_chunk(st->st_skeyid, "st_skeyid in skeyid_preshared()", &ctx); + return TRUE; + } +} + +static bool +skeyid_digisig(struct state *st) +{ + struct hmac_ctx ctx; + chunk_t nir; + + /* We need to hmac_init with the concatenation of Ni_b and Nr_b, + * so we have to build a temporary concatentation. + */ + nir.len = st->st_ni.len + st->st_nr.len; + nir.ptr = alloc_bytes(nir.len, "Ni + Nr in skeyid_digisig"); + memcpy(nir.ptr, st->st_ni.ptr, st->st_ni.len); + memcpy(nir.ptr+st->st_ni.len, st->st_nr.ptr, st->st_nr.len); + hmac_init_chunk(&ctx, st->st_oakley.hasher, nir); + pfree(nir.ptr); + + hmac_update_chunk(&ctx, st->st_shared); + hmac_final_chunk(st->st_skeyid, "st_skeyid in skeyid_digisig()", &ctx); + return TRUE; +} + +/* Generate the SKEYID_* and new IV + * See draft-ietf-ipsec-ike-01.txt 4.1 + */ +static bool +generate_skeyids_iv(struct state *st) +{ + /* Generate the SKEYID */ + switch (st->st_oakley.auth) + { + case OAKLEY_PRESHARED_KEY: + case XAUTHInitPreShared: + case XAUTHRespPreShared: + if (!skeyid_preshared(st)) + return FALSE; + break; + + case OAKLEY_RSA_SIG: + case XAUTHInitRSA: + case XAUTHRespRSA: + if (!skeyid_digisig(st)) + return FALSE; + break; + + case OAKLEY_DSS_SIG: + /* XXX */ + + case OAKLEY_RSA_ENC: + case OAKLEY_RSA_ENC_REV: + case OAKLEY_ELGAMAL_ENC: + case OAKLEY_ELGAMAL_ENC_REV: + /* XXX */ + + default: + bad_case(st->st_oakley.auth); + } + + /* generate SKEYID_* from SKEYID */ + { + struct hmac_ctx ctx; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid); + + /* SKEYID_D */ + hmac_update_chunk(&ctx, st->st_shared); + hmac_update(&ctx, st->st_icookie, COOKIE_SIZE); + hmac_update(&ctx, st->st_rcookie, COOKIE_SIZE); + hmac_update(&ctx, "\0", 1); + hmac_final_chunk(st->st_skeyid_d, "st_skeyid_d in generate_skeyids_iv()", &ctx); + + /* SKEYID_A */ + hmac_reinit(&ctx); + hmac_update_chunk(&ctx, st->st_skeyid_d); + hmac_update_chunk(&ctx, st->st_shared); + hmac_update(&ctx, st->st_icookie, COOKIE_SIZE); + hmac_update(&ctx, st->st_rcookie, COOKIE_SIZE); + hmac_update(&ctx, "\1", 1); + hmac_final_chunk(st->st_skeyid_a, "st_skeyid_a in generate_skeyids_iv()", &ctx); + + /* SKEYID_E */ + hmac_reinit(&ctx); + hmac_update_chunk(&ctx, st->st_skeyid_a); + hmac_update_chunk(&ctx, st->st_shared); + hmac_update(&ctx, st->st_icookie, COOKIE_SIZE); + hmac_update(&ctx, st->st_rcookie, COOKIE_SIZE); + hmac_update(&ctx, "\2", 1); + hmac_final_chunk(st->st_skeyid_e, "st_skeyid_e in generate_skeyids_iv()", &ctx); + } + + /* generate IV */ + { + union hash_ctx hash_ctx; + const struct hash_desc *h = st->st_oakley.hasher; + + st->st_new_iv_len = h->hash_digest_size; + passert(st->st_new_iv_len <= sizeof(st->st_new_iv)); + + DBG(DBG_CRYPT, + DBG_dump_chunk("DH_i:", st->st_gi); + DBG_dump_chunk("DH_r:", st->st_gr); + ); + h->hash_init(&hash_ctx); + h->hash_update(&hash_ctx, st->st_gi.ptr, st->st_gi.len); + h->hash_update(&hash_ctx, st->st_gr.ptr, st->st_gr.len); + h->hash_final(st->st_new_iv, &hash_ctx); + } + + /* Oakley Keying Material + * Derived from Skeyid_e: if it is not big enough, generate more + * using the PRF. + * See RFC 2409 "IKE" Appendix B + */ + { + /* const size_t keysize = st->st_oakley.encrypter->keydeflen/BITS_PER_BYTE; */ + const size_t keysize = st->st_oakley.enckeylen/BITS_PER_BYTE; + u_char keytemp[MAX_OAKLEY_KEY_LEN + MAX_DIGEST_LEN]; + u_char *k = st->st_skeyid_e.ptr; + + if (keysize > st->st_skeyid_e.len) + { + struct hmac_ctx ctx; + size_t i = 0; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_e); + hmac_update(&ctx, "\0", 1); + for (;;) + { + hmac_final(&keytemp[i], &ctx); + i += ctx.hmac_digest_size; + if (i >= keysize) + break; + hmac_reinit(&ctx); + hmac_update(&ctx, &keytemp[i - ctx.hmac_digest_size], ctx.hmac_digest_size); + } + k = keytemp; + } + clonereplacechunk(st->st_enc_key, k, keysize, "st_enc_key"); + } + + DBG(DBG_CRYPT, + DBG_dump_chunk("Skeyid: ", st->st_skeyid); + DBG_dump_chunk("Skeyid_d:", st->st_skeyid_d); + DBG_dump_chunk("Skeyid_a:", st->st_skeyid_a); + DBG_dump_chunk("Skeyid_e:", st->st_skeyid_e); + DBG_dump_chunk("enc key:", st->st_enc_key); + DBG_dump("IV:", st->st_new_iv, st->st_new_iv_len)); + return TRUE; +} + +/* Generate HASH_I or HASH_R for ISAKMP Phase I. + * This will *not* generate other hash payloads (eg. Phase II or Quick Mode, + * New Group Mode, or ISAKMP Informational Exchanges). + * If the hashi argument is TRUE, generate HASH_I; if FALSE generate HASH_R. + * If hashus argument is TRUE, we're generating a hash for our end. + * See RFC2409 IKE 5. + * + * Generating the SIG_I and SIG_R for DSS is an odd perversion of this: + * Most of the logic is the same, but SHA-1 is used in place of HMAC-whatever. + * The extensive common logic is embodied in main_mode_hash_body(). + * See draft-ietf-ipsec-ike-01.txt 4.1 and 6.1.1.2 + */ + +typedef void (*hash_update_t)(union hash_ctx *, const u_char *, size_t) ; +static void +main_mode_hash_body(struct state *st +, bool hashi /* Initiator? */ +, const pb_stream *idpl /* ID payload, as PBS */ +, union hash_ctx *ctx +, void (*hash_update_void)(void *, const u_char *input, size_t)) +{ +#define HASH_UPDATE_T (union hash_ctx *, const u_char *input, unsigned int len) + hash_update_t hash_update=(hash_update_t) hash_update_void; +#if 0 /* if desperate to debug hashing */ +# define hash_update(ctx, input, len) { \ + DBG_dump("hash input", input, len); \ + (hash_update)(ctx, input, len); \ + } +#endif + +# define hash_update_chunk(ctx, ch) hash_update((ctx), (ch).ptr, (ch).len) + + if (hashi) + { + hash_update_chunk(ctx, st->st_gi); + hash_update_chunk(ctx, st->st_gr); + hash_update(ctx, st->st_icookie, COOKIE_SIZE); + hash_update(ctx, st->st_rcookie, COOKIE_SIZE); + } + else + { + hash_update_chunk(ctx, st->st_gr); + hash_update_chunk(ctx, st->st_gi); + hash_update(ctx, st->st_rcookie, COOKIE_SIZE); + hash_update(ctx, st->st_icookie, COOKIE_SIZE); + } + + DBG(DBG_CRYPT, DBG_log("hashing %lu bytes of SA" + , (unsigned long) (st->st_p1isa.len - sizeof(struct isakmp_generic)))); + + /* SA_b */ + hash_update(ctx, st->st_p1isa.ptr + sizeof(struct isakmp_generic) + , st->st_p1isa.len - sizeof(struct isakmp_generic)); + + /* Hash identification payload, without generic payload header. + * We used to reconstruct ID Payload for this purpose, but now + * we use the bytes as they appear on the wire to avoid + * "spelling problems". + */ + hash_update(ctx + , idpl->start + sizeof(struct isakmp_generic) + , pbs_offset(idpl) - sizeof(struct isakmp_generic)); + +# undef hash_update_chunk +# undef hash_update +} + +static size_t /* length of hash */ +main_mode_hash(struct state *st +, u_char *hash_val /* resulting bytes */ +, bool hashi /* Initiator? */ +, const pb_stream *idpl) /* ID payload, as PBS; cur must be at end */ +{ + struct hmac_ctx ctx; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid); + main_mode_hash_body(st, hashi, idpl, &ctx.hash_ctx, ctx.h->hash_update); + hmac_final(hash_val, &ctx); + return ctx.hmac_digest_size; +} + +#if 0 /* only needed for DSS */ +static void +main_mode_sha1(struct state *st +, u_char *hash_val /* resulting bytes */ +, size_t *hash_len /* length of hash */ +, bool hashi /* Initiator? */ +, const pb_stream *idpl) /* ID payload, as PBS */ +{ + union hash_ctx ctx; + + SHA1Init(&ctx.ctx_sha1); + SHA1Update(&ctx.ctx_sha1, st->st_skeyid.ptr, st->st_skeyid.len); + *hash_len = SHA1_DIGEST_SIZE; + main_mode_hash_body(st, hashi, idpl, &ctx + , (void (*)(union hash_ctx *, const u_char *, unsigned int))&SHA1Update); + SHA1Final(hash_val, &ctx.ctx_sha1); +} +#endif + +/* Create an RSA signature of a hash. + * Poorly specified in draft-ietf-ipsec-ike-01.txt 6.1.1.2. + * Use PKCS#1 version 1.5 encryption of hash (called + * RSAES-PKCS1-V1_5) in PKCS#2. + */ +static size_t +RSA_sign_hash(struct connection *c +, u_char sig_val[RSA_MAX_OCTETS] +, const u_char *hash_val, size_t hash_len) +{ + size_t sz = 0; + smartcard_t *sc = c->spd.this.sc; + + if (sc == NULL) /* no smartcard */ + { + const struct RSA_private_key *k = get_RSA_private_key(c); + + if (k == NULL) + return 0; /* failure: no key to use */ + + sz = k->pub.k; + passert(RSA_MIN_OCTETS <= sz && 4 + hash_len < sz && sz <= RSA_MAX_OCTETS); + sign_hash(k, hash_val, hash_len, sig_val, sz); + } + else if (sc->valid) /* if valid pin then sign hash on the smartcard */ + { + lock_certs_and_keys("RSA_sign_hash"); + if (!scx_establish_context(sc) || !scx_login(sc)) + { + scx_release_context(sc); + unlock_certs_and_keys("RSA_sign_hash"); + return 0; + } + + sz = scx_get_keylength(sc); + if (sz == 0) + { + plog("failed to get keylength from smartcard"); + scx_release_context(sc); + unlock_certs_and_keys("RSA_sign_hash"); + return 0; + } + + DBG(DBG_CONTROL | DBG_CRYPT, + DBG_log("signing hash with RSA key from smartcard (slot: %d, id: %s)" + , (int)sc->slot, sc->id) + ) + sz = scx_sign_hash(sc, hash_val, hash_len, sig_val, sz) ? sz : 0; + if (!pkcs11_keep_state) + scx_release_context(sc); + unlock_certs_and_keys("RSA_sign_hash"); + } + return sz; +} + +/* Check a Main Mode RSA Signature against computed hash using RSA public key k. + * + * As a side effect, on success, the public key is copied into the + * state object to record the authenticator. + * + * Can fail because wrong public key is used or because hash disagrees. + * We distinguish because diagnostics should also. + * + * The result is NULL if the Signature checked out. + * Otherwise, the first character of the result indicates + * how far along failure occurred. A greater character signifies + * greater progress. + * + * Classes: + * 0 reserved for caller + * 1 SIG length doesn't match key length -- wrong key + * 2-8 malformed ECB after decryption -- probably wrong key + * 9 decrypted hash != computed hash -- probably correct key + * + * Although the math should be the same for generating and checking signatures, + * it is not: the knowledge of the private key allows more efficient (i.e. + * different) computation for encryption. + */ +static err_t +try_RSA_signature(const u_char hash_val[MAX_DIGEST_LEN], size_t hash_len +, const pb_stream *sig_pbs, pubkey_t *kr +, struct state *st) +{ + const u_char *sig_val = sig_pbs->cur; + size_t sig_len = pbs_left(sig_pbs); + u_char s[RSA_MAX_OCTETS]; /* for decrypted sig_val */ + u_char *hash_in_s = &s[sig_len - hash_len]; + const struct RSA_public_key *k = &kr->u.rsa; + + /* decrypt the signature -- reversing RSA_sign_hash */ + if (sig_len != k->k) + { + /* XXX notification: INVALID_KEY_INFORMATION */ + return "1" "SIG length does not match public key length"; + } + + /* actual exponentiation; see PKCS#1 v2.0 5.1 */ + { + chunk_t temp_s; + mpz_t c; + + n_to_mpz(c, sig_val, sig_len); + mpz_powm(c, c, &k->e, &k->n); + + temp_s = mpz_to_n(c, sig_len); /* back to octets */ + memcpy(s, temp_s.ptr, sig_len); + pfree(temp_s.ptr); + mpz_clear(c); + } + + /* sanity check on signature: see if it matches + * PKCS#1 v1.5 8.1 encryption-block formatting + */ + { + err_t ugh = NULL; + + if (s[0] != 0x00) + ugh = "2" "no leading 00"; + else if (hash_in_s[-1] != 0x00) + ugh = "3" "00 separator not present"; + else if (s[1] == 0x01) + { + const u_char *p; + + for (p = &s[2]; p != hash_in_s - 1; p++) + { + if (*p != 0xFF) + { + ugh = "4" "invalid Padding String"; + break; + } + } + } + else if (s[1] == 0x02) + { + const u_char *p; + + for (p = &s[2]; p != hash_in_s - 1; p++) + { + if (*p == 0x00) + { + ugh = "5" "invalid Padding String"; + break; + } + } + } + else + ugh = "6" "Block Type not 01 or 02"; + + if (ugh != NULL) + { + /* note: it might be a good idea to make sure that + * an observer cannot tell what kind of failure happened. + * I don't know what this means in practice. + */ + /* We probably selected the wrong public key for peer: + * SIG Payload decrypted into malformed ECB + */ + /* XXX notification: INVALID_KEY_INFORMATION */ + return ugh; + } + } + + /* We have the decoded hash: see if it matches. */ + if (memcmp(hash_val, hash_in_s, hash_len) != 0) + { + /* good: header, hash, signature, and other payloads well-formed + * good: we could find an RSA Sig key for the peer. + * bad: hash doesn't match + * Guess: sides disagree about key to be used. + */ + DBG_cond_dump(DBG_CRYPT, "decrypted SIG", s, sig_len); + DBG_cond_dump(DBG_CRYPT, "computed HASH", hash_val, hash_len); + /* XXX notification: INVALID_HASH_INFORMATION */ + return "9" "authentication failure: received SIG does not match computed HASH, but message is well-formed"; + } + + /* Success: copy successful key into state. + * There might be an old one if we previously aborted this + * state transition. + */ + unreference_key(&st->st_peer_pubkey); + st->st_peer_pubkey = reference_key(kr); + + return NULL; /* happy happy */ +} + +/* Check signature against all RSA public keys we can find. + * If we need keys from DNS KEY records, and they haven't been fetched, + * return STF_SUSPEND to ask for asynch DNS lookup. + * + * Note: parameter keys_from_dns contains results of DNS lookup for key + * or is NULL indicating lookup not yet tried. + * + * take_a_crack is a helper function. Mostly forensic. + * If only we had coroutines. + */ +struct tac_state { + /* RSA_check_signature's args that take_a_crack needs */ + struct state *st; + const u_char *hash_val; + size_t hash_len; + const pb_stream *sig_pbs; + + /* state carried between calls */ + err_t best_ugh; /* most successful failure */ + int tried_cnt; /* number of keys tried */ + char tried[50]; /* keyids of tried public keys */ + char *tn; /* roof of tried[] */ +}; + +static bool +take_a_crack(struct tac_state *s +, pubkey_t *kr +, const char *story USED_BY_DEBUG) +{ + err_t ugh = try_RSA_signature(s->hash_val, s->hash_len, s->sig_pbs + , kr, s->st); + const struct RSA_public_key *k = &kr->u.rsa; + + s->tried_cnt++; + if (ugh == NULL) + { + DBG(DBG_CRYPT | DBG_CONTROL + , DBG_log("an RSA Sig check passed with *%s [%s]" + , k->keyid, story)); + return TRUE; + } + else + { + DBG(DBG_CRYPT + , DBG_log("an RSA Sig check failure %s with *%s [%s]" + , ugh + 1, k->keyid, story)); + if (s->best_ugh == NULL || s->best_ugh[0] < ugh[0]) + s->best_ugh = ugh; + if (ugh[0] > '0' + && s->tn - s->tried + KEYID_BUF + 2 < (ptrdiff_t)sizeof(s->tried)) + { + strcpy(s->tn, " *"); + strcpy(s->tn + 2, k->keyid); + s->tn += strlen(s->tn); + } + return FALSE; + } +} + +static stf_status +RSA_check_signature(const struct id* peer +, struct state *st +, const u_char hash_val[MAX_DIGEST_LEN] +, size_t hash_len +, const pb_stream *sig_pbs +#ifdef USE_KEYRR +, const pubkey_list_t *keys_from_dns +#endif /* USE_KEYRR */ +, const struct gw_info *gateways_from_dns +) +{ + const struct connection *c = st->st_connection; + struct tac_state s; + err_t dns_ugh = NULL; + + s.st = st; + s.hash_val = hash_val; + s.hash_len = hash_len; + s.sig_pbs = sig_pbs; + + s.best_ugh = NULL; + s.tried_cnt = 0; + s.tn = s.tried; + + /* try all gateway records hung off c */ + if (c->policy & POLICY_OPPO) + { + struct gw_info *gw; + + for (gw = c->gw_info; gw != NULL; gw = gw->next) + { + /* only consider entries that have a key and are for our peer */ + if (gw->gw_key_present + && same_id(&gw->gw_id, &c->spd.that.id) + && take_a_crack(&s, gw->key, "key saved from DNS TXT")) + return STF_OK; + } + } + + /* try all appropriate Public keys */ + { + pubkey_list_t *p, **pp; + + pp = &pubkeys; + + for (p = pubkeys; p != NULL; p = *pp) + { + pubkey_t *key = p->key; + + if (key->alg == PUBKEY_ALG_RSA && same_id(peer, &key->id)) + { + time_t now = time(NULL); + + /* check if found public key has expired */ + if (key->until_time != UNDEFINED_TIME && key->until_time < now) + { + loglog(RC_LOG_SERIOUS, + "cached RSA public key has expired and has been deleted"); + *pp = free_public_keyentry(p); + continue; /* continue with next public key */ + } + + if (take_a_crack(&s, key, "preloaded key")) + return STF_OK; + } + pp = &p->next; + } + } + + /* if no key was found (evidenced by best_ugh == NULL) + * and that side of connection is key_from_DNS_on_demand + * then go search DNS for keys for peer. + */ + if (s.best_ugh == NULL && c->spd.that.key_from_DNS_on_demand) + { + if (gateways_from_dns != NULL) + { + /* TXT keys */ + const struct gw_info *gwp; + + for (gwp = gateways_from_dns; gwp != NULL; gwp = gwp->next) + if (gwp->gw_key_present + && take_a_crack(&s, gwp->key, "key from DNS TXT")) + return STF_OK; + } +#ifdef USE_KEYRR + else if (keys_from_dns != NULL) + { + /* KEY keys */ + const pubkey_list_t *kr; + + for (kr = keys_from_dns; kr != NULL; kr = kr->next) + if (kr->key->alg == PUBKEY_ALG_RSA + && take_a_crack(&s, kr->key, "key from DNS KEY")) + return STF_OK; + } +#endif /* USE_KEYRR */ + else + { + /* nothing yet: ask for asynch DNS lookup */ + return STF_SUSPEND; + } + } + + /* no acceptable key was found: diagnose */ + { + char id_buf[BUF_LEN]; /* arbitrary limit on length of ID reported */ + + (void) idtoa(peer, id_buf, sizeof(id_buf)); + + if (s.best_ugh == NULL) + { + if (dns_ugh == NULL) + loglog(RC_LOG_SERIOUS, "no RSA public key known for '%s'" + , id_buf); + else + loglog(RC_LOG_SERIOUS, "no RSA public key known for '%s'" + "; DNS search for KEY failed (%s)" + , id_buf, dns_ugh); + + /* ??? is this the best code there is? */ + return STF_FAIL + INVALID_KEY_INFORMATION; + } + + if (s.best_ugh[0] == '9') + { + loglog(RC_LOG_SERIOUS, "%s", s.best_ugh + 1); + /* XXX Could send notification back */ + return STF_FAIL + INVALID_HASH_INFORMATION; + } + else + { + if (s.tried_cnt == 1) + { + loglog(RC_LOG_SERIOUS + , "Signature check (on %s) failed (wrong key?); tried%s" + , id_buf, s.tried); + DBG(DBG_CONTROL, + DBG_log("public key for %s failed:" + " decrypted SIG payload into a malformed ECB (%s)" + , id_buf, s.best_ugh + 1)); + } + else + { + loglog(RC_LOG_SERIOUS + , "Signature check (on %s) failed:" + " tried%s keys but none worked." + , id_buf, s.tried); + DBG(DBG_CONTROL, + DBG_log("all %d public keys for %s failed:" + " best decrypted SIG payload into a malformed ECB (%s)" + , s.tried_cnt, id_buf, s.best_ugh + 1)); + } + return STF_FAIL + INVALID_KEY_INFORMATION; + } + } +} + +static notification_t +accept_nonce(struct msg_digest *md, chunk_t *dest, const char *name) +{ + pb_stream *nonce_pbs = &md->chain[ISAKMP_NEXT_NONCE]->pbs; + size_t len = pbs_left(nonce_pbs); + + if (len < MINIMUM_NONCE_SIZE || MAXIMUM_NONCE_SIZE < len) + { + loglog(RC_LOG_SERIOUS, "%s length not between %d and %d" + , name , MINIMUM_NONCE_SIZE, MAXIMUM_NONCE_SIZE); + return PAYLOAD_MALFORMED; /* ??? */ + } + clonereplacechunk(*dest, nonce_pbs->cur, len, "nonce"); + return NOTHING_WRONG; +} + +/* encrypt message, sans fixed part of header + * IV is fetched from st->st_new_iv and stored into st->st_iv. + * The theory is that there will be no "backing out", so we commit to IV. + * We also close the pbs. + */ +bool +encrypt_message(pb_stream *pbs, struct state *st) +{ + const struct encrypt_desc *e = st->st_oakley.encrypter; + u_int8_t *enc_start = pbs->start + sizeof(struct isakmp_hdr); + size_t enc_len = pbs_offset(pbs) - sizeof(struct isakmp_hdr); + + DBG_cond_dump(DBG_CRYPT | DBG_RAW, "encrypting:\n", enc_start, enc_len); + + /* Pad up to multiple of encryption blocksize. + * See the description associated with the definition of + * struct isakmp_hdr in packet.h. + */ + { + size_t padding = pad_up(enc_len, e->enc_blocksize); + + if (padding != 0) + { + if (!out_zero(padding, pbs, "encryption padding")) + return FALSE; + enc_len += padding; + } + } + + DBG(DBG_CRYPT, DBG_log("encrypting using %s", enum_show(&oakley_enc_names, st->st_oakley.encrypt))); + + /* e->crypt(TRUE, enc_start, enc_len, st); */ + crypto_cbc_encrypt(e, TRUE, enc_start, enc_len, st); + + update_iv(st); + DBG_cond_dump(DBG_CRYPT, "next IV:", st->st_iv, st->st_iv_len); + close_message(pbs); + return TRUE; +} + +/* Compute HASH(1), HASH(2) of Quick Mode. + * HASH(1) is part of Quick I1 message. + * HASH(2) is part of Quick R1 message. + * Used by: quick_outI1, quick_inI1_outR1 (twice), quick_inR1_outI2 + * (see RFC 2409 "IKE" 5.5, pg. 18 or draft-ietf-ipsec-ike-01.txt 6.2 pg 25) + */ +static size_t +quick_mode_hash12(u_char *dest, const u_char *start, const u_char *roof +, const struct state *st, const msgid_t *msgid, bool hash2) +{ + struct hmac_ctx ctx; + +#if 0 /* if desperate to debug hashing */ +# define hmac_update(ctx, ptr, len) { \ + DBG_dump("hash input", (ptr), (len)); \ + (hmac_update)((ctx), (ptr), (len)); \ + } + DBG_dump("hash key", st->st_skeyid_a.ptr, st->st_skeyid_a.len); +#endif + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); + hmac_update(&ctx, (const void *) msgid, sizeof(msgid_t)); + if (hash2) + hmac_update_chunk(&ctx, st->st_ni); /* include Ni_b in the hash */ + hmac_update(&ctx, start, roof-start); + hmac_final(dest, &ctx); + + DBG(DBG_CRYPT, + DBG_log("HASH(%d) computed:", hash2 + 1); + DBG_dump("", dest, ctx.hmac_digest_size)); + return ctx.hmac_digest_size; +# undef hmac_update +} + +/* Compute HASH(3) in Quick Mode (part of Quick I2 message). + * Used by: quick_inR1_outI2, quick_inI2 + * See RFC2409 "The Internet Key Exchange (IKE)" 5.5. + * NOTE: this hash (unlike HASH(1) and HASH(2)) ONLY covers the + * Message ID and Nonces. This is a mistake. + */ +static size_t +quick_mode_hash3(u_char *dest, struct state *st) +{ + struct hmac_ctx ctx; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); + hmac_update(&ctx, "\0", 1); + hmac_update(&ctx, (u_char *) &st->st_msgid, sizeof(st->st_msgid)); + hmac_update_chunk(&ctx, st->st_ni); + hmac_update_chunk(&ctx, st->st_nr); + hmac_final(dest, &ctx); + DBG_cond_dump(DBG_CRYPT, "HASH(3) computed:", dest, ctx.hmac_digest_size); + return ctx.hmac_digest_size; +} + +/* Compute Phase 2 IV. + * Uses Phase 1 IV from st_iv; puts result in st_new_iv. + */ +void +init_phase2_iv(struct state *st, const msgid_t *msgid) +{ + const struct hash_desc *h = st->st_oakley.hasher; + union hash_ctx ctx; + + DBG_cond_dump(DBG_CRYPT, "last Phase 1 IV:" + , st->st_ph1_iv, st->st_ph1_iv_len); + + st->st_new_iv_len = h->hash_digest_size; + passert(st->st_new_iv_len <= sizeof(st->st_new_iv)); + + h->hash_init(&ctx); + h->hash_update(&ctx, st->st_ph1_iv, st->st_ph1_iv_len); + passert(*msgid != 0); + h->hash_update(&ctx, (const u_char *)msgid, sizeof(*msgid)); + h->hash_final(st->st_new_iv, &ctx); + + DBG_cond_dump(DBG_CRYPT, "computed Phase 2 IV:" + , st->st_new_iv, st->st_new_iv_len); +} + +/* Initiate quick mode. + * --> HDR*, HASH(1), SA, Nr [, KE ] [, IDci, IDcr ] + * (see RFC 2409 "IKE" 5.5) + * Note: this is not called from demux.c + */ + +static bool +emit_subnet_id(ip_subnet *net +, u_int8_t np, u_int8_t protoid, u_int16_t port, pb_stream *outs) +{ + struct isakmp_ipsec_id id; + pb_stream id_pbs; + ip_address ta; + const unsigned char *tbp; + size_t tal; + + id.isaiid_np = np; + id.isaiid_idtype = subnetishost(net) + ? aftoinfo(subnettypeof(net))->id_addr + : aftoinfo(subnettypeof(net))->id_subnet; + id.isaiid_protoid = protoid; + id.isaiid_port = port; + + if (!out_struct(&id, &isakmp_ipsec_identification_desc, outs, &id_pbs)) + return FALSE; + + networkof(net, &ta); + tal = addrbytesptr(&ta, &tbp); + if (!out_raw(tbp, tal, &id_pbs, "client network")) + return FALSE; + + if (!subnetishost(net)) + { + maskof(net, &ta); + tal = addrbytesptr(&ta, &tbp); + if (!out_raw(tbp, tal, &id_pbs, "client mask")) + return FALSE; + } + + close_output_pbs(&id_pbs); + return TRUE; +} + +stf_status +quick_outI1(int whack_sock +, struct state *isakmp_sa +, struct connection *c +, lset_t policy +, unsigned long try +, so_serial_t replacing) +{ + struct state *st = duplicate_state(isakmp_sa); + pb_stream reply; /* not really a reply */ + pb_stream rbody; + u_char /* set by START_HASH_PAYLOAD: */ + *r_hashval, /* where in reply to jam hash value */ + *r_hash_start; /* start of what is to be hashed */ + bool has_client = c->spd.this.has_client || c->spd.that.has_client || + c->spd.this.protocol || c->spd.that.protocol || + c->spd.this.port || c->spd.that.port; + + bool send_natoa = FALSE; + u_int8_t np = ISAKMP_NEXT_NONE; + + st->st_whack_sock = whack_sock; + st->st_connection = c; + set_cur_state(st); /* we must reset before exit */ + st->st_policy = policy; + st->st_try = try; + + st->st_myuserprotoid = c->spd.this.protocol; + st->st_peeruserprotoid = c->spd.that.protocol; + st->st_myuserport = c->spd.this.port; + st->st_peeruserport = c->spd.that.port; + + st->st_msgid = generate_msgid(isakmp_sa); + st->st_state = STATE_QUICK_I1; + + insert_state(st); /* needs cookies, connection, and msgid */ + + if (replacing == SOS_NOBODY) + plog("initiating Quick Mode %s {using isakmp#%lu}" + , prettypolicy(policy) + , isakmp_sa->st_serialno); + else + plog("initiating Quick Mode %s to replace #%lu {using isakmp#%lu}" + , prettypolicy(policy) + , replacing + , isakmp_sa->st_serialno); + + if (isakmp_sa->nat_traversal & NAT_T_DETECTED) + { + /* Duplicate nat_traversal status in new state */ + st->nat_traversal = isakmp_sa->nat_traversal; + + if (isakmp_sa->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME)) + has_client = TRUE; + + nat_traversal_change_port_lookup(NULL, st); + } + else + st->nat_traversal = 0; + + /* are we going to send a NAT-OA payload? */ + if ((st->nat_traversal & NAT_T_WITH_NATOA) + && !(st->st_policy & POLICY_TUNNEL) + && (st->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME))) + { + send_natoa = TRUE; + np = (st->nat_traversal & NAT_T_WITH_RFC_VALUES) ? + ISAKMP_NEXT_NATOA_RFC : ISAKMP_NEXT_NATOA_DRAFTS; + } + + /* set up reply */ + init_pbs(&reply, reply_buffer, sizeof(reply_buffer), "reply packet"); + + /* HDR* out */ + { + struct isakmp_hdr hdr; + + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_HASH; + hdr.isa_xchg = ISAKMP_XCHG_QUICK; + hdr.isa_msgid = st->st_msgid; + hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; + memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); + memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); + if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* HASH(1) -- create and note space to be filled later */ + START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_SA); + + /* SA out */ + + /* + * See if pfs_group has been specified for this conn, + * if not, fallback to old use-same-as-P1 behaviour + */ +#ifndef NO_IKE_ALG + if (st->st_connection) + st->st_pfs_group = ike_alg_pfsgroup(st->st_connection, policy); + if (!st->st_pfs_group) +#endif + /* If PFS specified, use the same group as during Phase 1: + * since no negotiation is possible, we pick one that is + * very likely supported. + */ + st->st_pfs_group = policy & POLICY_PFS? isakmp_sa->st_oakley.group : NULL; + + /* Emit SA payload based on a subset of the policy bits. + * POLICY_COMPRESS is considered iff we can do IPcomp. + */ + { + lset_t pm = POLICY_ENCRYPT | POLICY_AUTHENTICATE; + + if (can_do_IPcomp) + pm |= POLICY_COMPRESS; + + if (!out_sa(&rbody + , &ipsec_sadb[(st->st_policy & pm) >> POLICY_IPSEC_SHIFT] + , st, FALSE, ISAKMP_NEXT_NONCE)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* Ni out */ + if (!build_and_ship_nonce(&st->st_ni, &rbody + , policy & POLICY_PFS? ISAKMP_NEXT_KE : has_client? ISAKMP_NEXT_ID : np + , "Ni")) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + + /* [ KE ] out (for PFS) */ + + if (st->st_pfs_group != NULL) + { + if (!build_and_ship_KE(st, &st->st_gi, st->st_pfs_group + , &rbody, has_client? ISAKMP_NEXT_ID : np)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* [ IDci, IDcr ] out */ + if (has_client) + { + /* IDci (we are initiator), then IDcr (peer is responder) */ + if (!emit_subnet_id(&c->spd.this.client + , ISAKMP_NEXT_ID, st->st_myuserprotoid, st->st_myuserport, &rbody) + || !emit_subnet_id(&c->spd.that.client + , np, st->st_peeruserprotoid, st->st_peeruserport, &rbody)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* Send NAT-OA if our address is NATed */ + if (send_natoa) + { + if (!nat_traversal_add_natoa(ISAKMP_NEXT_NONE, &rbody, st)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + } + + /* finish computing HASH(1), inserting it in output */ + (void) quick_mode_hash12(r_hashval, r_hash_start, rbody.cur + , st, &st->st_msgid, FALSE); + + /* encrypt message, except for fixed part of header */ + + init_phase2_iv(isakmp_sa, &st->st_msgid); + st->st_new_iv_len = isakmp_sa->st_new_iv_len; + memcpy(st->st_new_iv, isakmp_sa->st_new_iv, st->st_new_iv_len); + + if (!encrypt_message(&rbody, st)) + { + reset_cur_state(); + return STF_INTERNAL_ERROR; + } + + /* save packet, now that we know its size */ + clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply) + , "reply packet from quick_outI1"); + + /* send the packet */ + + send_packet(st, "quick_outI1"); + + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); + + if (replacing == SOS_NOBODY) + whack_log(RC_NEW_STATE + STATE_QUICK_I1 + , "%s: initiate" + , enum_name(&state_names, st->st_state)); + else + whack_log(RC_NEW_STATE + STATE_QUICK_I1 + , "%s: initiate to replace #%lu" + , enum_name(&state_names, st->st_state) + , replacing); + reset_cur_state(); + return STF_OK; +} + + +/* + * Decode the CERT payload of Phase 1. + */ +static void +decode_cert(struct msg_digest *md) +{ + struct payload_digest *p; + + for (p = md->chain[ISAKMP_NEXT_CERT]; p != NULL; p = p->next) + { + struct isakmp_cert *const cert = &p->payload.cert; + chunk_t blob; + time_t valid_until; + blob.ptr = p->pbs.cur; + blob.len = pbs_left(&p->pbs); + if (cert->isacert_type == CERT_X509_SIGNATURE) + { + x509cert_t cert = empty_x509cert; + if (parse_x509cert(blob, 0, &cert)) + { + if (verify_x509cert(&cert, strict_crl_policy, &valid_until)) + { + DBG(DBG_PARSING, + DBG_log("Public key validated") + ) + add_x509_public_key(&cert, valid_until, DAL_SIGNED); + } + else + { + plog("X.509 certificate rejected"); + } + free_generalNames(cert.subjectAltName, FALSE); + free_generalNames(cert.crlDistributionPoints, FALSE); + } + else + plog("Syntax error in X.509 certificate"); + } + else if (cert->isacert_type == CERT_PKCS7_WRAPPED_X509) + { + x509cert_t *cert = NULL; + + if (pkcs7_parse_signedData(blob, NULL, &cert, NULL, NULL)) + store_x509certs(&cert, strict_crl_policy); + else + plog("Syntax error in PKCS#7 wrapped X.509 certificates"); + } + else + { + loglog(RC_LOG_SERIOUS, "ignoring %s certificate payload", + enum_show(&cert_type_names, cert->isacert_type)); + DBG_cond_dump_chunk(DBG_PARSING, "CERT:\n", blob); + } + } +} + +/* + * Decode the CR payload of Phase 1. + */ +static void +decode_cr(struct msg_digest *md, struct connection *c) +{ + struct payload_digest *p; + + for (p = md->chain[ISAKMP_NEXT_CR]; p != NULL; p = p->next) + { + struct isakmp_cr *const cr = &p->payload.cr; + chunk_t ca_name; + + ca_name.len = pbs_left(&p->pbs); + ca_name.ptr = (ca_name.len > 0)? p->pbs.cur : NULL; + + DBG_cond_dump_chunk(DBG_PARSING, "CR", ca_name); + + if (cr->isacr_type == CERT_X509_SIGNATURE) + { + char buf[BUF_LEN]; + + if (ca_name.len > 0) + { + generalName_t *gn; + + if (!is_asn1(ca_name)) + continue; + + gn = alloc_thing(generalName_t, "generalName"); + clonetochunk(ca_name, ca_name.ptr,ca_name.len, "ca name"); + gn->kind = GN_DIRECTORY_NAME; + gn->name = ca_name; + gn->next = c->requested_ca; + c->requested_ca = gn; + } + c->got_certrequest = TRUE; + + DBG(DBG_PARSING | DBG_CONTROL, + dntoa_or_null(buf, BUF_LEN, ca_name, "%any"); + DBG_log("requested CA: '%s'", buf); + ) + } + else + loglog(RC_LOG_SERIOUS, "ignoring %s certificate request payload", + enum_show(&cert_type_names, cr->isacr_type)); + } +} + +/* Decode the ID payload of Phase 1 (main_inI3_outR3 and main_inR3) + * Note: we may change connections as a result. + * We must be called before SIG or HASH are decoded since we + * may change the peer's RSA key or ID. + */ +static bool +decode_peer_id(struct msg_digest *md, struct id *peer) +{ + struct state *const st = md->st; + struct payload_digest *const id_pld = md->chain[ISAKMP_NEXT_ID]; + const pb_stream *const id_pbs = &id_pld->pbs; + struct isakmp_id *const id = &id_pld->payload.id; + + /* I think that RFC2407 (IPSEC DOI) 4.6.2 is confused. + * It talks about the protocol ID and Port fields of the ID + * Payload, but they don't exist as such in Phase 1. + * We use more appropriate names. + * isaid_doi_specific_a is in place of Protocol ID. + * isaid_doi_specific_b is in place of Port. + * Besides, there is no good reason for allowing these to be + * other than 0 in Phase 1. + */ + if ((st->nat_traversal & NAT_T_WITH_PORT_FLOATING) + && id->isaid_doi_specific_a == IPPROTO_UDP + && (id->isaid_doi_specific_b == 0 || id->isaid_doi_specific_b == NAT_T_IKE_FLOAT_PORT)) + { + DBG_log("protocol/port in Phase 1 ID Payload is %d/%d. " + "accepted with port_floating NAT-T", + id->isaid_doi_specific_a, id->isaid_doi_specific_b); + } + else if (!(id->isaid_doi_specific_a == 0 && id->isaid_doi_specific_b == 0) + && !(id->isaid_doi_specific_a == IPPROTO_UDP && id->isaid_doi_specific_b == IKE_UDP_PORT)) + { + loglog(RC_LOG_SERIOUS, "protocol/port in Phase 1 ID Payload must be 0/0 or %d/%d" + " but are %d/%d" + , IPPROTO_UDP, IKE_UDP_PORT + , id->isaid_doi_specific_a, id->isaid_doi_specific_b); + return FALSE; + } + + peer->kind = id->isaid_idtype; + + switch (peer->kind) + { + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + /* failure mode for initaddr is probably inappropriate address length */ + { + err_t ugh = initaddr(id_pbs->cur, pbs_left(id_pbs) + , peer->kind == ID_IPV4_ADDR? AF_INET : AF_INET6 + , &peer->ip_addr); + + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS, "improper %s identification payload: %s" + , enum_show(&ident_names, peer->kind), ugh); + /* XXX Could send notification back */ + return FALSE; + } + } + break; + + case ID_USER_FQDN: + if (memchr(id_pbs->cur, '@', pbs_left(id_pbs)) == NULL) + { + loglog(RC_LOG_SERIOUS, "peer's ID_USER_FQDN contains no @"); + return FALSE; + } + /* FALLTHROUGH */ + case ID_FQDN: + if (memchr(id_pbs->cur, '\0', pbs_left(id_pbs)) != NULL) + { + loglog(RC_LOG_SERIOUS, "Phase 1 ID Payload of type %s contains a NUL" + , enum_show(&ident_names, peer->kind)); + return FALSE; + } + + /* ??? ought to do some more sanity check, but what? */ + + setchunk(peer->name, id_pbs->cur, pbs_left(id_pbs)); + break; + + case ID_KEY_ID: + setchunk(peer->name, id_pbs->cur, pbs_left(id_pbs)); + DBG(DBG_PARSING, + DBG_dump_chunk("KEY ID:", peer->name)); + break; + + case ID_DER_ASN1_DN: + setchunk(peer->name, id_pbs->cur, pbs_left(id_pbs)); + DBG(DBG_PARSING, + DBG_dump_chunk("DER ASN1 DN:", peer->name)); + break; + + default: + /* XXX Could send notification back */ + loglog(RC_LOG_SERIOUS, "Unacceptable identity type (%s) in Phase 1 ID Payload" + , enum_show(&ident_names, peer->kind)); + return FALSE; + } + + { + char buf[BUF_LEN]; + + idtoa(peer, buf, sizeof(buf)); + plog("Peer ID is %s: '%s'", + enum_show(&ident_names, id->isaid_idtype), buf); + } + + /* check for certificates */ + decode_cert(md); + return TRUE; +} + +/* Now that we've decoded the ID payload, let's see if we + * need to switch connections. + * We must not switch horses if we initiated: + * - if the initiation was explicit, we'd be ignoring user's intent + * - if opportunistic, we'll lose our HOLD info + */ +static bool +switch_connection(struct msg_digest *md, struct id *peer, bool initiator) +{ + struct state *const st = md->st; + struct connection *c = st->st_connection; + + chunk_t peer_ca = (st->st_peer_pubkey != NULL) + ? st->st_peer_pubkey->issuer : empty_chunk; + + DBG(DBG_CONTROL, + char buf[BUF_LEN]; + + dntoa_or_null(buf, BUF_LEN, peer_ca, "%none"); + DBG_log("peer CA: '%s'", buf); + ) + + if (initiator) + { + int pathlen; + + if (!same_id(&c->spd.that.id, peer)) + { + char expect[BUF_LEN] + , found[BUF_LEN]; + + idtoa(&c->spd.that.id, expect, sizeof(expect)); + idtoa(peer, found, sizeof(found)); + loglog(RC_LOG_SERIOUS + , "we require peer to have ID '%s', but peer declares '%s'" + , expect, found); + return FALSE; + } + + DBG(DBG_CONTROL, + char buf[BUF_LEN]; + + dntoa_or_null(buf, BUF_LEN, c->spd.this.ca, "%none"); + DBG_log("required CA: '%s'", buf); + ) + + if (!trusted_ca(peer_ca, c->spd.that.ca, &pathlen)) + { + loglog(RC_LOG_SERIOUS + , "we don't accept the peer's CA"); + return FALSE; + } + } + else + { + struct connection *r; + + /* check for certificate requests */ + decode_cr(md, c); + + r = refine_host_connection(st, peer, peer_ca); + + /* delete the collected certificate requests */ + free_generalNames(c->requested_ca, TRUE); + c->requested_ca = NULL; + + if (r == NULL) + { + char buf[BUF_LEN]; + + idtoa(peer, buf, sizeof(buf)); + loglog(RC_LOG_SERIOUS, "no suitable connection for peer '%s'", buf); + return FALSE; + } + + DBG(DBG_CONTROL, + char buf[BUF_LEN]; + + dntoa_or_null(buf, BUF_LEN, r->spd.this.ca, "%none"); + DBG_log("offered CA: '%s'", buf); + ) + + if (r != c) + { + /* apparently, r is an improvement on c -- replace */ + + DBG(DBG_CONTROL + , DBG_log("switched from \"%s\" to \"%s\"", c->name, r->name)); + if (r->kind == CK_TEMPLATE) + { + /* instantiate it, filling in peer's ID */ + r = rw_instantiate(r, &c->spd.that.host_addr + , c->spd.that.host_port, NULL, peer); + } + + /* copy certificate request info */ + r->got_certrequest = c->got_certrequest; + + st->st_connection = r; /* kill reference to c */ + set_cur_connection(r); + connection_discard(c); + } + else if (c->spd.that.has_id_wildcards) + { + free_id_content(&c->spd.that.id); + c->spd.that.id = *peer; + c->spd.that.has_id_wildcards = FALSE; + unshare_id_content(&c->spd.that.id); + } + } + return TRUE; +} + +/* Decode the variable part of an ID packet (during Quick Mode). + * This is designed for packets that identify clients, not peers. + * Rejects 0.0.0.0/32 or IPv6 equivalent because + * (1) it is wrong and (2) we use this value for inband signalling. + */ +static bool +decode_net_id(struct isakmp_ipsec_id *id +, pb_stream *id_pbs +, ip_subnet *net +, const char *which) +{ + const struct af_info *afi = NULL; + + /* Note: the following may be a pointer into static memory + * that may be recycled, but only if the type is not known. + * That case is disposed of very early -- in the first switch. + */ + const char *idtypename = enum_show(&ident_names, id->isaiid_idtype); + + switch (id->isaiid_idtype) + { + case ID_IPV4_ADDR: + case ID_IPV4_ADDR_SUBNET: + case ID_IPV4_ADDR_RANGE: + afi = &af_inet4_info; + break; + case ID_IPV6_ADDR: + case ID_IPV6_ADDR_SUBNET: + case ID_IPV6_ADDR_RANGE: + afi = &af_inet6_info; + break; + case ID_FQDN: + return TRUE; + default: + /* XXX support more */ + loglog(RC_LOG_SERIOUS, "unsupported ID type %s" + , idtypename); + /* XXX Could send notification back */ + return FALSE; + } + + switch (id->isaiid_idtype) + { + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + { + ip_address temp_address; + err_t ugh; + + ugh = initaddr(id_pbs->cur, pbs_left(id_pbs), afi->af, &temp_address); + + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS, "%s ID payload %s has wrong length in Quick I1 (%s)" + , which, idtypename, ugh); + /* XXX Could send notification back */ + return FALSE; + } + if (isanyaddr(&temp_address)) + { + loglog(RC_LOG_SERIOUS, "%s ID payload %s is invalid (%s) in Quick I1" + , which, idtypename, ip_str(&temp_address)); + /* XXX Could send notification back */ + return FALSE; + } + happy(addrtosubnet(&temp_address, net)); + DBG(DBG_PARSING | DBG_CONTROL + , DBG_log("%s is %s", which, ip_str(&temp_address))); + break; + } + + case ID_IPV4_ADDR_SUBNET: + case ID_IPV6_ADDR_SUBNET: + { + ip_address temp_address, temp_mask; + err_t ugh; + + if (pbs_left(id_pbs) != 2 * afi->ia_sz) + { + loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1" + , which, idtypename); + /* XXX Could send notification back */ + return FALSE; + } + ugh = initaddr(id_pbs->cur + , afi->ia_sz, afi->af, &temp_address); + if (ugh == NULL) + ugh = initaddr(id_pbs->cur + afi->ia_sz + , afi->ia_sz, afi->af, &temp_mask); + if (ugh == NULL) + ugh = initsubnet(&temp_address, masktocount(&temp_mask) + , '0', net); + if (ugh == NULL && subnetisnone(net)) + ugh = "contains only anyaddr"; + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS, "%s ID payload %s bad subnet in Quick I1 (%s)" + , which, idtypename, ugh); + /* XXX Could send notification back */ + return FALSE; + } + DBG(DBG_PARSING | DBG_CONTROL, + { + char temp_buff[SUBNETTOT_BUF]; + + subnettot(net, 0, temp_buff, sizeof(temp_buff)); + DBG_log("%s is subnet %s", which, temp_buff); + }); + break; + } + + case ID_IPV4_ADDR_RANGE: + case ID_IPV6_ADDR_RANGE: + { + ip_address temp_address_from, temp_address_to; + err_t ugh; + + if (pbs_left(id_pbs) != 2 * afi->ia_sz) + { + loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1" + , which, idtypename); + /* XXX Could send notification back */ + return FALSE; + } + ugh = initaddr(id_pbs->cur, afi->ia_sz, afi->af, &temp_address_from); + if (ugh == NULL) + ugh = initaddr(id_pbs->cur + afi->ia_sz + , afi->ia_sz, afi->af, &temp_address_to); + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1" + , which, idtypename, ugh); + /* XXX Could send notification back */ + return FALSE; + } + + ugh = rangetosubnet(&temp_address_from, &temp_address_to, net); + if (ugh == NULL && subnetisnone(net)) + ugh = "contains only anyaddr"; + if (ugh != NULL) + { + char temp_buff1[ADDRTOT_BUF], temp_buff2[ADDRTOT_BUF]; + + addrtot(&temp_address_from, 0, temp_buff1, sizeof(temp_buff1)); + addrtot(&temp_address_to, 0, temp_buff2, sizeof(temp_buff2)); + loglog(RC_LOG_SERIOUS, "%s ID payload in Quick I1, %s" + " %s - %s unacceptable: %s" + , which, idtypename, temp_buff1, temp_buff2, ugh); + return FALSE; + } + DBG(DBG_PARSING | DBG_CONTROL, + { + char temp_buff[SUBNETTOT_BUF]; + + subnettot(net, 0, temp_buff, sizeof(temp_buff)); + DBG_log("%s is subnet %s (received as range)" + , which, temp_buff); + }); + break; + } + } + + /* set the port selector */ + setportof(htons(id->isaiid_port), &net->addr); + + DBG(DBG_PARSING | DBG_CONTROL, + DBG_log("%s protocol/port is %d/%d", which, id->isaiid_protoid, id->isaiid_port) + ) + + return TRUE; +} + +/* like decode, but checks that what is received matches what was sent */ +static bool + +check_net_id(struct isakmp_ipsec_id *id +, pb_stream *id_pbs +, u_int8_t *protoid +, u_int16_t *port +, ip_subnet *net +, const char *which) +{ + ip_subnet net_temp; + + if (!decode_net_id(id, id_pbs, &net_temp, which)) + return FALSE; + + if (!samesubnet(net, &net_temp) + || *protoid != id->isaiid_protoid || *port != id->isaiid_port) + { + loglog(RC_LOG_SERIOUS, "%s ID returned doesn't match my proposal", which); + return FALSE; + } + return TRUE; +} + +/* + * look for the existence of a non-expiring preloaded public key + */ +static bool +has_preloaded_public_key(struct state *st) +{ + struct connection *c = st->st_connection; + + /* do not consider rw connections since + * the peer's identity must be known + */ + if (c->kind == CK_PERMANENT) + { + pubkey_list_t *p; + + /* look for a matching RSA public key */ + for (p = pubkeys; p != NULL; p = p->next) + { + pubkey_t *key = p->key; + + if (key->alg == PUBKEY_ALG_RSA && + same_id(&c->spd.that.id, &key->id) && + key->until_time == UNDEFINED_TIME) + { + /* found a preloaded public key */ + return TRUE; + } + } + } + return FALSE; +} + +/* + * Produce the new key material of Quick Mode. + * RFC 2409 "IKE" section 5.5 + * specifies how this is to be done. + */ +static void +compute_proto_keymat(struct state *st +, u_int8_t protoid +, struct ipsec_proto_info *pi) +{ + size_t needed_len; /* bytes of keying material needed */ + + /* Add up the requirements for keying material + * (It probably doesn't matter if we produce too much!) + */ + switch (protoid) + { + case PROTO_IPSEC_ESP: + switch (pi->attrs.transid) + { + case ESP_NULL: + needed_len = 0; + break; + case ESP_DES: + needed_len = DES_CBC_BLOCK_SIZE; + break; + case ESP_3DES: + needed_len = DES_CBC_BLOCK_SIZE * 3; + break; + default: +#ifndef NO_KERNEL_ALG + if((needed_len=kernel_alg_esp_enc_keylen(pi->attrs.transid))>0) { + /* XXX: check key_len "coupling with kernel.c's */ + if (pi->attrs.key_len) { + needed_len=pi->attrs.key_len/8; + DBG(DBG_PARSING, DBG_log("compute_proto_keymat:" + "key_len=%d from peer", + (int)needed_len)); + } + break; + } +#endif + bad_case(pi->attrs.transid); + } + +#ifndef NO_KERNEL_ALG + DBG(DBG_PARSING, DBG_log("compute_proto_keymat:" + "needed_len (after ESP enc)=%d", + (int)needed_len)); + if (kernel_alg_esp_auth_ok(pi->attrs.auth, NULL)) { + needed_len += kernel_alg_esp_auth_keylen(pi->attrs.auth); + } else +#endif + switch (pi->attrs.auth) + { + case AUTH_ALGORITHM_NONE: + break; + case AUTH_ALGORITHM_HMAC_MD5: + needed_len += HMAC_MD5_KEY_LEN; + break; + case AUTH_ALGORITHM_HMAC_SHA1: + needed_len += HMAC_SHA1_KEY_LEN; + break; + case AUTH_ALGORITHM_DES_MAC: + default: + bad_case(pi->attrs.auth); + } + DBG(DBG_PARSING, DBG_log("compute_proto_keymat:" + "needed_len (after ESP auth)=%d", + (int)needed_len)); + break; + + case PROTO_IPSEC_AH: + switch (pi->attrs.transid) + { + case AH_MD5: + needed_len = HMAC_MD5_KEY_LEN; + break; + case AH_SHA: + needed_len = HMAC_SHA1_KEY_LEN; + break; + default: + bad_case(pi->attrs.transid); + } + break; + + default: + bad_case(protoid); + } + + pi->keymat_len = needed_len; + + /* Allocate space for the keying material. + * Although only needed_len bytes are desired, we + * must round up to a multiple of ctx.hmac_digest_size + * so that our buffer isn't overrun. + */ + { + struct hmac_ctx ctx_me, ctx_peer; + size_t needed_space; /* space needed for keying material (rounded up) */ + size_t i; + + hmac_init_chunk(&ctx_me, st->st_oakley.hasher, st->st_skeyid_d); + ctx_peer = ctx_me; /* duplicate initial conditions */ + + needed_space = needed_len + pad_up(needed_len, ctx_me.hmac_digest_size); + replace(pi->our_keymat, alloc_bytes(needed_space, "keymat in compute_keymat()")); + replace(pi->peer_keymat, alloc_bytes(needed_space, "peer_keymat in quick_inI1_outR1()")); + + for (i = 0;; ) + { + if (st->st_shared.ptr != NULL) + { + /* PFS: include the g^xy */ + hmac_update_chunk(&ctx_me, st->st_shared); + hmac_update_chunk(&ctx_peer, st->st_shared); + } + hmac_update(&ctx_me, &protoid, sizeof(protoid)); + hmac_update(&ctx_peer, &protoid, sizeof(protoid)); + + hmac_update(&ctx_me, (u_char *)&pi->our_spi, sizeof(pi->our_spi)); + hmac_update(&ctx_peer, (u_char *)&pi->attrs.spi, sizeof(pi->attrs.spi)); + + hmac_update_chunk(&ctx_me, st->st_ni); + hmac_update_chunk(&ctx_peer, st->st_ni); + + hmac_update_chunk(&ctx_me, st->st_nr); + hmac_update_chunk(&ctx_peer, st->st_nr); + + hmac_final(pi->our_keymat + i, &ctx_me); + hmac_final(pi->peer_keymat + i, &ctx_peer); + + i += ctx_me.hmac_digest_size; + if (i >= needed_space) + break; + + /* more keying material needed: prepare to go around again */ + + hmac_reinit(&ctx_me); + hmac_reinit(&ctx_peer); + + hmac_update(&ctx_me, pi->our_keymat + i - ctx_me.hmac_digest_size + , ctx_me.hmac_digest_size); + hmac_update(&ctx_peer, pi->peer_keymat + i - ctx_peer.hmac_digest_size + , ctx_peer.hmac_digest_size); + } + } + + DBG(DBG_CRYPT, + DBG_dump("KEYMAT computed:\n", pi->our_keymat, pi->keymat_len); + DBG_dump("Peer KEYMAT computed:\n", pi->peer_keymat, pi->keymat_len)); +} + +static void +compute_keymats(struct state *st) +{ + if (st->st_ah.present) + compute_proto_keymat(st, PROTO_IPSEC_AH, &st->st_ah); + if (st->st_esp.present) + compute_proto_keymat(st, PROTO_IPSEC_ESP, &st->st_esp); +} + +/* State Transition Functions. + * + * The definition of state_microcode_table in demux.c is a good + * overview of these routines. + * + * - Called from process_packet; result handled by complete_state_transition + * - struct state_microcode member "processor" points to these + * - these routine definitionss are in state order + * - these routines must be restartable from any point of error return: + * beware of memory allocated before any error. + * - output HDR is usually emitted by process_packet (if state_microcode + * member first_out_payload isn't ISAKMP_NEXT_NONE). + * + * The transition functions' functions include: + * - process and judge payloads + * - update st_iv (result of decryption is in st_new_iv) + * - build reply packet + */ + +/* Handle a Main Mode Oakley first packet (responder side). + * HDR;SA --> HDR;SA + */ +stf_status +main_inI1_outR1(struct msg_digest *md) +{ + struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA]; + struct state *st; + struct connection *c; + struct isakmp_proposal proposal; + pb_stream proposal_pbs; + pb_stream r_sa_pbs; + u_int32_t ipsecdoisit; + lset_t policy = LEMPTY; + int vids_to_send = 0; + + /* We preparse the peer's proposal in order to determine + * the requested authentication policy (RSA or PSK) + */ + RETURN_STF_FAILURE(preparse_isakmp_sa_body(&sa_pd->payload.sa + , &sa_pd->pbs, &ipsecdoisit, &proposal_pbs, &proposal)); + + backup_pbs(&proposal_pbs); + RETURN_STF_FAILURE(parse_isakmp_policy(&proposal_pbs + , proposal.isap_notrans, &policy)); + restore_pbs(&proposal_pbs); + + /* We are only considering candidate connections that match + * the requested authentication policy (RSA or PSK) + */ + c = find_host_connection(&md->iface->addr, pluto_port + , &md->sender, md->sender_port, policy); + + if (c == NULL && md->iface->ike_float) + { + c = find_host_connection(&md->iface->addr, NAT_T_IKE_FLOAT_PORT + , &md->sender, md->sender_port, policy); + } + + if (c == NULL) + { + /* See if a wildcarded connection can be found. + * We cannot pick the right connection, so we're making a guess. + * All Road Warrior connections are fair game: + * we pick the first we come across (if any). + * If we don't find any, we pick the first opportunistic + * with the smallest subnet that includes the peer. + * There is, of course, no necessary relationship between + * an Initiator's address and that of its client, + * but Food Groups kind of assumes one. + */ + { + struct connection *d; + + d = find_host_connection(&md->iface->addr + , pluto_port, (ip_address*)NULL, md->sender_port, policy); + + for (; d != NULL; d = d->hp_next) + { + if (d->kind == CK_GROUP) + { + /* ignore */ + } + else + { + if (d->kind == CK_TEMPLATE && !(d->policy & POLICY_OPPO)) + { + /* must be Road Warrior: we have a winner */ + c = d; + break; + } + + /* Opportunistic or Shunt: pick tightest match */ + if (addrinsubnet(&md->sender, &d->spd.that.client) + && (c == NULL || !subnetinsubnet(&c->spd.that.client, &d->spd.that.client))) + c = d; + } + } + } + + if (c == NULL) + { + loglog(RC_LOG_SERIOUS, "initial Main Mode message received on %s:%u" + " but no connection has been authorized%s%s" + , ip_str(&md->iface->addr), ntohs(portof(&md->iface->addr)) + , (policy != LEMPTY) ? " with policy=" : "" + , (policy != LEMPTY) ? bitnamesof(sa_policy_bit_names, policy) : ""); + /* XXX notification is in order! */ + return STF_IGNORE; + } + else if (c->kind != CK_TEMPLATE) + { + loglog(RC_LOG_SERIOUS, "initial Main Mode message received on %s:%u" + " but \"%s\" forbids connection" + , ip_str(&md->iface->addr), pluto_port, c->name); + /* XXX notification is in order! */ + return STF_IGNORE; + } + else + { + /* Create a temporary connection that is a copy of this one. + * His ID isn't declared yet. + */ + c = rw_instantiate(c, &md->sender, md->sender_port, NULL, NULL); + } + } + else if (c->kind == CK_TEMPLATE) + { + /* Create an instance + * This is a rare case: wildcard peer ID but static peer IP address + */ + c = rw_instantiate(c, &md->sender, md->sender_port, NULL, &c->spd.that.id); + } + + /* Set up state */ + md->st = st = new_state(); + st->st_connection = c; + set_cur_state(st); /* (caller will reset cur_state) */ + st->st_try = 0; /* not our job to try again from start */ + st->st_policy = c->policy & ~POLICY_IPSEC_MASK; /* only as accurate as connection */ + + memcpy(st->st_icookie, md->hdr.isa_icookie, COOKIE_SIZE); + get_cookie(FALSE, st->st_rcookie, COOKIE_SIZE, &md->sender); + + insert_state(st); /* needs cookies, connection, and msgid (0) */ + + st->st_doi = ISAKMP_DOI_IPSEC; + st->st_situation = SIT_IDENTITY_ONLY; /* We only support this */ + + if ((c->kind == CK_INSTANCE) && (c->spd.that.host_port != pluto_port)) + { + plog("responding to Main Mode from unknown peer %s:%u" + , ip_str(&c->spd.that.host_addr), c->spd.that.host_port); + } + else if (c->kind == CK_INSTANCE) + { + plog("responding to Main Mode from unknown peer %s" + , ip_str(&c->spd.that.host_addr)); + } + else + { + plog("responding to Main Mode"); + } + + /* parse_isakmp_sa also spits out a winning SA into our reply, + * so we have to build our md->reply and emit HDR before calling it. + */ + + /* determine how many Vendor ID payloads we will be sending */ + if (SEND_PLUTO_VID) + vids_to_send++; + if (SEND_CISCO_UNITY_VID) + vids_to_send++; + if (md->openpgp) + vids_to_send++; + /* always send XAUTH Vendor ID */ + vids_to_send++; + /* always send DPD Vendor ID */ + vids_to_send++; + if (md->nat_traversal_vid && nat_traversal_enabled) + vids_to_send++; + + /* HDR out. + * We can't leave this to comm_handle() because we must + * fill in the cookie. + */ + { + struct isakmp_hdr r_hdr = md->hdr; + + r_hdr.isa_flags &= ~ISAKMP_FLAG_COMMIT; /* we won't ever turn on this bit */ + memcpy(r_hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); + r_hdr.isa_np = ISAKMP_NEXT_SA; + if (!out_struct(&r_hdr, &isakmp_hdr_desc, &md->reply, &md->rbody)) + return STF_INTERNAL_ERROR; + } + + /* start of SA out */ + { + struct isakmp_sa r_sa = sa_pd->payload.sa; + + r_sa.isasa_np = vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE; + + if (!out_struct(&r_sa, &isakmp_sa_desc, &md->rbody, &r_sa_pbs)) + return STF_INTERNAL_ERROR; + } + + /* SA body in and out */ + RETURN_STF_FAILURE(parse_isakmp_sa_body(ipsecdoisit, &proposal_pbs + ,&proposal, &r_sa_pbs, st, FALSE)); + + /* if enabled send Pluto Vendor ID */ + if (SEND_PLUTO_VID) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, VID_STRONGSWAN)) + { + return STF_INTERNAL_ERROR; + } + } + + /* if enabled send Cisco Unity Vendor ID */ + if (SEND_CISCO_UNITY_VID) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, VID_CISCO_UNITY)) + { + return STF_INTERNAL_ERROR; + } + } + + /* + * if the peer sent an OpenPGP Vendor ID we offer the same capability + */ + if (md->openpgp) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, VID_OPENPGP)) + { + return STF_INTERNAL_ERROR; + } + } + + /* Announce our ability to do eXtended AUTHentication to the peer */ + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, VID_MISC_XAUTH)) + { + return STF_INTERNAL_ERROR; + } + + /* Announce our ability to do Dead Peer Detection to the peer */ + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, VID_MISC_DPD)) + { + return STF_INTERNAL_ERROR; + } + + if (md->nat_traversal_vid && nat_traversal_enabled) + { + /* reply if NAT-Traversal draft is supported */ + st->nat_traversal = nat_traversal_vid_to_method(md->nat_traversal_vid); + + if (st->nat_traversal + && !out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, md->nat_traversal_vid)) + { + return STF_INTERNAL_ERROR; + } + } + + close_message(&md->rbody); + + /* save initiator SA for HASH */ + clonereplacechunk(st->st_p1isa, sa_pd->pbs.start, pbs_room(&sa_pd->pbs), "sa in main_inI1_outR1()"); + + return STF_OK; +} + +/* STATE_MAIN_I1: HDR, SA --> auth dependent + * PSK_AUTH, DS_AUTH: --> HDR, KE, Ni + * + * The following are not yet implemented: + * PKE_AUTH: --> HDR, KE, [ HASH(1), ] PubKey_r, PubKey_r + * RPKE_AUTH: --> HDR, [ HASH(1), ] Pubkey_r, Ke_i, + * Ke_i [,<Ke_i] + * + * We must verify that the proposal received matches one we sent. + */ +stf_status +main_inR1_outI2(struct msg_digest *md) +{ + struct state *const st = md->st; + + u_int8_t np = ISAKMP_NEXT_NONE; + + /* verify echoed SA */ + { + u_int32_t ipsecdoisit; + pb_stream proposal_pbs; + struct isakmp_proposal proposal; + struct payload_digest *const sapd = md->chain[ISAKMP_NEXT_SA]; + + RETURN_STF_FAILURE(preparse_isakmp_sa_body(&sapd->payload.sa + ,&sapd->pbs, &ipsecdoisit, &proposal_pbs, &proposal)); + if (proposal.isap_notrans != 1) + { + loglog(RC_LOG_SERIOUS, "a single Transform is required in a selecting Oakley Proposal; found %u" + , (unsigned)proposal.isap_notrans); + RETURN_STF_FAILURE(BAD_PROPOSAL_SYNTAX); + } + RETURN_STF_FAILURE(parse_isakmp_sa_body(ipsecdoisit + , &proposal_pbs, &proposal, NULL, st, TRUE)); + } + + if (nat_traversal_enabled && md->nat_traversal_vid) + { + st->nat_traversal = nat_traversal_vid_to_method(md->nat_traversal_vid); + plog("enabling possible NAT-traversal with method %s" + , bitnamesof(natt_type_bitnames, st->nat_traversal)); + } + if (st->nat_traversal & NAT_T_WITH_NATD) + { + np = (st->nat_traversal & NAT_T_WITH_RFC_VALUES) ? + ISAKMP_NEXT_NATD_RFC : ISAKMP_NEXT_NATD_DRAFTS; + } + + /**************** build output packet HDR;KE;Ni ****************/ + + /* HDR out. + * We can't leave this to comm_handle() because the isa_np + * depends on the type of Auth (eventually). + */ + echo_hdr(md, FALSE, ISAKMP_NEXT_KE); + + /* KE out */ + if (!build_and_ship_KE(st, &st->st_gi, st->st_oakley.group + , &md->rbody, ISAKMP_NEXT_NONCE)) + return STF_INTERNAL_ERROR; + +#ifdef DEBUG + /* Ni out */ + if (!build_and_ship_nonce(&st->st_ni, &md->rbody + , (cur_debugging & IMPAIR_BUST_MI2)? ISAKMP_NEXT_VID : np, "Ni")) + return STF_INTERNAL_ERROR; + + if (cur_debugging & IMPAIR_BUST_MI2) + { + /* generate a pointless large VID payload to push message over MTU */ + pb_stream vid_pbs; + + if (!out_generic(np, &isakmp_vendor_id_desc, &md->rbody, &vid_pbs)) + return STF_INTERNAL_ERROR; + if (!out_zero(1500 /*MTU?*/, &vid_pbs, "Filler VID")) + return STF_INTERNAL_ERROR; + close_output_pbs(&vid_pbs); + } +#else + /* Ni out */ + if (!build_and_ship_nonce(&st->st_ni, &md->rbody, np, "Ni")) + return STF_INTERNAL_ERROR; +#endif + + if (st->nat_traversal & NAT_T_WITH_NATD) + { + if (!nat_traversal_add_natd(ISAKMP_NEXT_NONE, &md->rbody, md)) + return STF_INTERNAL_ERROR; + } + + /* finish message */ + close_message(&md->rbody); + + /* Reinsert the state, using the responder cookie we just received */ + unhash_state(st); + memcpy(st->st_rcookie, md->hdr.isa_rcookie, COOKIE_SIZE); + insert_state(st); /* needs cookies, connection, and msgid (0) */ + + return STF_OK; +} + +/* STATE_MAIN_R1: + * PSK_AUTH, DS_AUTH: HDR, KE, Ni --> HDR, KE, Nr + * + * The following are not yet implemented: + * PKE_AUTH: HDR, KE, [ HASH(1), ] PubKey_r, PubKey_r + * --> HDR, KE, PubKey_i, PubKey_i + * RPKE_AUTH: + * HDR, [ HASH(1), ] Pubkey_r, Ke_i, Ke_i [,<Ke_i] + * --> HDR, PubKey_i, Ke_r, Ke_r + */ +stf_status +main_inI2_outR2(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + + /* send CR if auth is RSA and no preloaded RSA public key exists*/ + bool RSA_auth = st->st_oakley.auth == OAKLEY_RSA_SIG + || st->st_oakley.auth == XAUTHInitRSA + || st->st_oakley.auth == XAUTHRespRSA; + bool send_cr = !no_cr_send && RSA_auth && !has_preloaded_public_key(st); + + u_int8_t np = ISAKMP_NEXT_NONE; + + /* KE in */ + RETURN_STF_FAILURE(accept_KE(&st->st_gi, "Gi", st->st_oakley.group, keyex_pbs)); + + /* Ni in */ + RETURN_STF_FAILURE(accept_nonce(md, &st->st_ni, "Ni")); + + if (st->nat_traversal & NAT_T_WITH_NATD) + { + nat_traversal_natd_lookup(md); + + np = (st->nat_traversal & NAT_T_WITH_RFC_VALUES) ? + ISAKMP_NEXT_NATD_RFC : ISAKMP_NEXT_NATD_DRAFTS; + } + if (st->nat_traversal) + { + nat_traversal_show_result(st->nat_traversal, md->sender_port); + } + if (st->nat_traversal & NAT_T_WITH_KA) + { + nat_traversal_new_ka_event(); + } + + /* decode certificate requests */ + st->st_connection->got_certrequest = FALSE; + decode_cr(md, st->st_connection); + + /**************** build output packet HDR;KE;Nr ****************/ + + /* HDR out done */ + + /* KE out */ + if (!build_and_ship_KE(st, &st->st_gr, st->st_oakley.group + , &md->rbody, ISAKMP_NEXT_NONCE)) + return STF_INTERNAL_ERROR; + +#ifdef DEBUG + /* Nr out */ + if (!build_and_ship_nonce(&st->st_nr, &md->rbody + , (cur_debugging & IMPAIR_BUST_MR2)? ISAKMP_NEXT_VID + : (send_cr? ISAKMP_NEXT_CR : np), "Nr")) + return STF_INTERNAL_ERROR; + + if (cur_debugging & IMPAIR_BUST_MR2) + { + /* generate a pointless large VID payload to push message over MTU */ + pb_stream vid_pbs; + + if (!out_generic((send_cr)? ISAKMP_NEXT_CR : np, + &isakmp_vendor_id_desc, &md->rbody, &vid_pbs)) + return STF_INTERNAL_ERROR; + if (!out_zero(1500 /*MTU?*/, &vid_pbs, "Filler VID")) + return STF_INTERNAL_ERROR; + close_output_pbs(&vid_pbs); + } +#else + /* Nr out */ + if (!build_and_ship_nonce(&st->st_nr, &md->rbody, + (send_cr)? ISAKMP_NEXT_CR : np, "Nr")) + return STF_INTERNAL_ERROR; +#endif + + /* CR out */ + if (send_cr) + { + if (st->st_connection->kind == CK_PERMANENT) + { + if (!build_and_ship_CR(CERT_X509_SIGNATURE + , st->st_connection->spd.that.ca + , &md->rbody, np)) + return STF_INTERNAL_ERROR; + } + else + { + generalName_t *ca = NULL; + + if (collect_rw_ca_candidates(md, &ca)) + { + generalName_t *gn; + + for (gn = ca; gn != NULL; gn = gn->next) + { + if (!build_and_ship_CR(CERT_X509_SIGNATURE, gn->name + , &md->rbody + , gn->next == NULL ? np : ISAKMP_NEXT_CR)) + return STF_INTERNAL_ERROR; + } + free_generalNames(ca, FALSE); + } + else + { + if (!build_and_ship_CR(CERT_X509_SIGNATURE, empty_chunk + , &md->rbody, np)) + return STF_INTERNAL_ERROR; + } + } + } + + if (st->nat_traversal & NAT_T_WITH_NATD) + { + if (!nat_traversal_add_natd(ISAKMP_NEXT_NONE, &md->rbody, md)) + return STF_INTERNAL_ERROR; + } + + /* finish message */ + close_message(&md->rbody); + + /* next message will be encrypted, but not this one. + * We could defer this calculation. + */ + compute_dh_shared(st, st->st_gi, st->st_oakley.group); + if (!generate_skeyids_iv(st)) + return STF_FAIL + AUTHENTICATION_FAILED; + update_iv(st); + + return STF_OK; +} + +/* STATE_MAIN_I2: + * SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I + * SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I + * + * The following are not yet implemented. + * SMF_PKE_AUTH: HDR, KE, PubKey_i, PubKey_i + * --> HDR*, HASH_I + * SMF_RPKE_AUTH: HDR, PubKey_i, Ke_r, Ke_r + * --> HDR*, HASH_I + */ +stf_status +main_inR2_outI3(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *const keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + pb_stream id_pbs; /* ID Payload; also used for hash calculation */ + + certpolicy_t cert_policy = st->st_connection->spd.this.sendcert; + cert_t mycert = st->st_connection->spd.this.cert; + bool requested, send_cert, send_cr; + + bool RSA_auth = st->st_oakley.auth == OAKLEY_RSA_SIG + || st->st_oakley.auth == XAUTHInitRSA + || st->st_oakley.auth == XAUTHRespRSA; + + int auth_payload = RSA_auth ? ISAKMP_NEXT_SIG : ISAKMP_NEXT_HASH; + + /* KE in */ + RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr", st->st_oakley.group, keyex_pbs)); + + /* Nr in */ + RETURN_STF_FAILURE(accept_nonce(md, &st->st_nr, "Nr")); + + /* decode certificate requests */ + st->st_connection->got_certrequest = FALSE; + decode_cr(md, st->st_connection); + + /* free collected certificate requests since as initiator + * we don't heed them anyway + */ + free_generalNames(st->st_connection->requested_ca, TRUE); + st->st_connection->requested_ca = NULL; + + /* send certificate if auth is RSA, we have one and we want + * or are requested to send it + */ + requested = cert_policy == CERT_SEND_IF_ASKED + && st->st_connection->got_certrequest; + send_cert = RSA_auth && mycert.type != CERT_NONE + && (cert_policy == CERT_ALWAYS_SEND || requested); + + /* send certificate request if we don't have a preloaded RSA public key */ + send_cr = !no_cr_send && send_cert && !has_preloaded_public_key(st); + + /* done parsing; initialize crypto */ + compute_dh_shared(st, st->st_gr, st->st_oakley.group); + if (!generate_skeyids_iv(st)) + return STF_FAIL + AUTHENTICATION_FAILED; + + if (st->nat_traversal & NAT_T_WITH_NATD) + { + nat_traversal_natd_lookup(md); + } + if (st->nat_traversal) + { + nat_traversal_show_result(st->nat_traversal, md->sender_port); + } + if (st->nat_traversal & NAT_T_WITH_KA) + { + nat_traversal_new_ka_event(); + } + + /*************** build output packet HDR*;IDii;HASH/SIG_I ***************/ + /* ??? NOTE: this is almost the same as main_inI3_outR3's code */ + + /* HDR* out done */ + + /* IDii out */ + { + struct isakmp_ipsec_id id_hd; + chunk_t id_b; + + build_id_payload(&id_hd, &id_b, &st->st_connection->spd.this); + id_hd.isaiid_np = (send_cert)? ISAKMP_NEXT_CERT : auth_payload; + if (!out_struct(&id_hd, &isakmp_ipsec_identification_desc, &md->rbody, &id_pbs) + || !out_chunk(id_b, &id_pbs, "my identity")) + return STF_INTERNAL_ERROR; + close_output_pbs(&id_pbs); + } + + /* CERT out */ + if (RSA_auth) + { + DBG(DBG_CONTROL, + DBG_log("our certificate policy is %s" + , enum_name(&cert_policy_names, cert_policy)) + ) + if (mycert.type != CERT_NONE) + { + const char *request_text = ""; + + if (cert_policy == CERT_SEND_IF_ASKED) + request_text = (send_cert)? "upon request":"without request"; + plog("we have a cert %s sending it %s" + , send_cert? "and are":"but are not", request_text); + } + else + { + plog("we don't have a cert"); + } + } + if (send_cert) + { + pb_stream cert_pbs; + + struct isakmp_cert cert_hd; + cert_hd.isacert_np = (send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_SIG; + cert_hd.isacert_type = mycert.type; + + if (!out_struct(&cert_hd, &isakmp_ipsec_certificate_desc, &md->rbody, &cert_pbs)) + return STF_INTERNAL_ERROR; + if (!out_chunk(get_mycert(mycert), &cert_pbs, "CERT")) + return STF_INTERNAL_ERROR; + close_output_pbs(&cert_pbs); + } + + /* CR out */ + if (send_cr) + { + if (!build_and_ship_CR(mycert.type, st->st_connection->spd.that.ca + , &md->rbody, ISAKMP_NEXT_SIG)) + return STF_INTERNAL_ERROR; + } + + /* HASH_I or SIG_I out */ + { + u_char hash_val[MAX_DIGEST_LEN]; + size_t hash_len = main_mode_hash(st, hash_val, TRUE, &id_pbs); + + if (auth_payload == ISAKMP_NEXT_HASH) + { + /* HASH_I out */ + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_hash_desc, &md->rbody + , hash_val, hash_len, "HASH_I")) + return STF_INTERNAL_ERROR; + } + else + { + /* SIG_I out */ + u_char sig_val[RSA_MAX_OCTETS]; + size_t sig_len = RSA_sign_hash(st->st_connection + , sig_val, hash_val, hash_len); + + if (sig_len == 0) + { + loglog(RC_LOG_SERIOUS, "unable to locate my private key for RSA Signature"); + return STF_FAIL + AUTHENTICATION_FAILED; + } + + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc + , &md->rbody, sig_val, sig_len, "SIG_I")) + return STF_INTERNAL_ERROR; + } + } + + /* encrypt message, except for fixed part of header */ + + /* st_new_iv was computed by generate_skeyids_iv */ + if (!encrypt_message(&md->rbody, st)) + return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ + + return STF_OK; +} + +/* Shared logic for asynchronous lookup of DNS KEY records. + * Used for STATE_MAIN_R2 and STATE_MAIN_I3. + */ + +enum key_oppo_step { + kos_null, + kos_his_txt +#ifdef USE_KEYRR + , kos_his_key +#endif +}; + +struct key_continuation { + struct adns_continuation ac; /* common prefix */ + struct msg_digest *md; + enum key_oppo_step step; + bool failure_ok; + err_t last_ugh; +}; + +typedef stf_status (key_tail_fn)(struct msg_digest *md + , struct key_continuation *kc); +static void +report_key_dns_failure(struct id *id, err_t ugh) +{ + char id_buf[BUF_LEN]; /* arbitrary limit on length of ID reported */ + + (void) idtoa(id, id_buf, sizeof(id_buf)); + loglog(RC_LOG_SERIOUS, "no RSA public key known for '%s'" + "; DNS search for KEY failed (%s)", id_buf, ugh); +} + + +/* Processs the Main Mode ID Payload and the Authenticator + * (Hash or Signature Payload). + * If a DNS query is still needed to get the other host's public key, + * the query is initiated and STF_SUSPEND is returned. + * Note: parameter kc is a continuation containing the results from + * the previous DNS query, or NULL indicating no query has been issued. + */ +static stf_status +main_id_and_auth(struct msg_digest *md + , bool initiator /* are we the Initiator? */ + , cont_fn_t cont_fn /* continuation function */ + , const struct key_continuation *kc /* current state, can be NULL */ +) +{ + struct state *st = md->st; + u_char hash_val[MAX_DIGEST_LEN]; + size_t hash_len; + struct id peer; + stf_status r = STF_OK; + + /* ID Payload in */ + if (!decode_peer_id(md, &peer)) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* Hash the ID Payload. + * main_mode_hash requires idpl->cur to be at end of payload + * so we temporarily set if so. + */ + { + pb_stream *idpl = &md->chain[ISAKMP_NEXT_ID]->pbs; + u_int8_t *old_cur = idpl->cur; + + idpl->cur = idpl->roof; + hash_len = main_mode_hash(st, hash_val, !initiator, idpl); + idpl->cur = old_cur; + } + + switch (st->st_oakley.auth) + { + case OAKLEY_PRESHARED_KEY: + case XAUTHInitPreShared: + case XAUTHRespPreShared: + { + pb_stream *const hash_pbs = &md->chain[ISAKMP_NEXT_HASH]->pbs; + + if (pbs_left(hash_pbs) != hash_len + || memcmp(hash_pbs->cur, hash_val, hash_len) != 0) + { + DBG_cond_dump(DBG_CRYPT, "received HASH:" + , hash_pbs->cur, pbs_left(hash_pbs)); + loglog(RC_LOG_SERIOUS, "received Hash Payload does not match computed value"); + /* XXX Could send notification back */ + r = STF_FAIL + INVALID_HASH_INFORMATION; + } + } + break; + + case OAKLEY_RSA_SIG: + case XAUTHInitRSA: + case XAUTHRespRSA: + r = RSA_check_signature(&peer, st, hash_val, hash_len + , &md->chain[ISAKMP_NEXT_SIG]->pbs +#ifdef USE_KEYRR + , kc == NULL? NULL : kc->ac.keys_from_dns +#endif /* USE_KEYRR */ + , kc == NULL? NULL : kc->ac.gateways_from_dns + ); + + if (r == STF_SUSPEND) + { + /* initiate/resume asynchronous DNS lookup for key */ + struct key_continuation *nkc + = alloc_thing(struct key_continuation, "key continuation"); + enum key_oppo_step step_done = kc == NULL? kos_null : kc->step; + err_t ugh; + + /* Record that state is used by a suspended md */ + passert(st->st_suspended_md == NULL); + st->st_suspended_md = md; + + nkc->failure_ok = FALSE; + nkc->md = md; + + switch (step_done) + { + case kos_null: + /* first try: look for the TXT records */ + nkc->step = kos_his_txt; +#ifdef USE_KEYRR + nkc->failure_ok = TRUE; +#endif + ugh = start_adns_query(&peer + , &peer /* SG itself */ + , T_TXT + , cont_fn + , &nkc->ac); + break; + +#ifdef USE_KEYRR + case kos_his_txt: + /* second try: look for the KEY records */ + nkc->step = kos_his_key; + ugh = start_adns_query(&peer + , NULL /* no sgw for KEY */ + , T_KEY + , cont_fn + , &nkc->ac); + break; +#endif /* USE_KEYRR */ + + default: + bad_case(step_done); + } + + if (ugh != NULL) + { + report_key_dns_failure(&peer, ugh); + st->st_suspended_md = NULL; + r = STF_FAIL + INVALID_KEY_INFORMATION; + } + } + break; + + default: + bad_case(st->st_oakley.auth); + } + if (r != STF_OK) + return r; + + DBG(DBG_CRYPT, DBG_log("authentication succeeded")); + + /* + * With the peer ID known, let's see if we need to switch connections. + */ + if (!switch_connection(md, &peer, initiator)) + return STF_FAIL + INVALID_ID_INFORMATION; + + return r; +} + +/* This continuation is called as part of either + * the main_inI3_outR3 state or main_inR3 state. + * + * The "tail" function is the corresponding tail + * function main_inI3_outR3_tail | main_inR3_tail, + * either directly when the state is started, or via + * adns continuation. + * + * Basically, we go around in a circle: + * main_in?3* -> key_continue + * ^ \ + * / V + * adns main_in?3*_tail + * ^ | + * \ V + * main_id_and_auth + * + * until such time as main_id_and_auth is able + * to find authentication, or we run out of things + * to try. + */ +static void +key_continue(struct adns_continuation *cr +, err_t ugh +, key_tail_fn *tail) +{ + struct key_continuation *kc = (void *)cr; + struct state *st = kc->md->st; + + passert(cur_state == NULL); + + /* if st == NULL, our state has been deleted -- just clean up */ + if (st != NULL) + { + stf_status r; + + passert(st->st_suspended_md == kc->md); + st->st_suspended_md = NULL; /* no longer connected or suspended */ + cur_state = st; + + if (!kc->failure_ok && ugh != NULL) + { + report_key_dns_failure(&st->st_connection->spd.that.id, ugh); + r = STF_FAIL + INVALID_KEY_INFORMATION; + } + else + { + +#ifdef USE_KEYRR + passert(kc->step == kos_his_txt || kc->step == kos_his_key); +#else + passert(kc->step == kos_his_txt); +#endif + kc->last_ugh = ugh; /* record previous error in case we need it */ + r = (*tail)(kc->md, kc); + } + complete_state_transition(&kc->md, r); + } + if (kc->md != NULL) + release_md(kc->md); + cur_state = NULL; +} + +/* STATE_MAIN_R2: + * PSK_AUTH: HDR*, IDi1, HASH_I --> HDR*, IDr1, HASH_R + * DS_AUTH: HDR*, IDi1, [ CERT, ] SIG_I --> HDR*, IDr1, [ CERT, ] SIG_R + * PKE_AUTH, RPKE_AUTH: HDR*, HASH_I --> HDR*, HASH_R + * + * Broken into parts to allow asynchronous DNS lookup. + * + * - main_inI3_outR3 to start + * - main_inI3_outR3_tail to finish or suspend for DNS lookup + * - main_inI3_outR3_continue to start main_inI3_outR3_tail again + */ +static key_tail_fn main_inI3_outR3_tail; /* forward */ + +stf_status +main_inI3_outR3(struct msg_digest *md) +{ + return main_inI3_outR3_tail(md, NULL); +} + +static void +main_inI3_outR3_continue(struct adns_continuation *cr, err_t ugh) +{ + key_continue(cr, ugh, main_inI3_outR3_tail); +} + +static stf_status +main_inI3_outR3_tail(struct msg_digest *md +, struct key_continuation *kc) +{ + struct state *const st = md->st; + u_int8_t auth_payload; + pb_stream r_id_pbs; /* ID Payload; also used for hash calculation */ + certpolicy_t cert_policy; + cert_t mycert; + bool RSA_auth; + bool send_cert; + bool requested; + + /* ID and HASH_I or SIG_I in + * Note: this may switch the connection being used! + */ + { + stf_status r = main_id_and_auth(md, FALSE + , main_inI3_outR3_continue + , kc); + + if (r != STF_OK) + return r; + } + + /* send certificate if auth is RSA, we have one and we want + * or are requested to send it + */ + cert_policy = st->st_connection->spd.this.sendcert; + mycert = st->st_connection->spd.this.cert; + requested = cert_policy == CERT_SEND_IF_ASKED + && st->st_connection->got_certrequest; + RSA_auth = st->st_oakley.auth == OAKLEY_RSA_SIG + || st->st_oakley.auth == XAUTHInitRSA + || st->st_oakley.auth == XAUTHRespRSA; + send_cert = RSA_auth + && mycert.type != CERT_NONE + && (cert_policy == CERT_ALWAYS_SEND || requested); + + /*************** build output packet HDR*;IDir;HASH/SIG_R ***************/ + /* proccess_packet() would automatically generate the HDR* + * payload if smc->first_out_payload is not ISAKMP_NEXT_NONE. + * We don't do this because we wish there to be no partially + * built output packet if we need to suspend for asynch DNS. + */ + /* ??? NOTE: this is almost the same as main_inR2_outI3's code */ + + /* HDR* out + * If auth were PKE_AUTH or RPKE_AUTH, ISAKMP_NEXT_HASH would + * be first payload. + */ + echo_hdr(md, TRUE, ISAKMP_NEXT_ID); + + auth_payload = RSA_auth ? ISAKMP_NEXT_SIG : ISAKMP_NEXT_HASH; + + /* IDir out */ + { + /* id_hd should be struct isakmp_id, but struct isakmp_ipsec_id + * allows build_id_payload() to work for both phases. + */ + struct isakmp_ipsec_id id_hd; + chunk_t id_b; + + build_id_payload(&id_hd, &id_b, &st->st_connection->spd.this); + id_hd.isaiid_np = (send_cert)? ISAKMP_NEXT_CERT : auth_payload; + if (!out_struct(&id_hd, &isakmp_ipsec_identification_desc, &md->rbody, &r_id_pbs) + || !out_chunk(id_b, &r_id_pbs, "my identity")) + return STF_INTERNAL_ERROR; + close_output_pbs(&r_id_pbs); + } + + /* CERT out */ + if (RSA_auth) + { + DBG(DBG_CONTROL, + DBG_log("our certificate policy is %s" + , enum_name(&cert_policy_names, cert_policy)) + ) + if (mycert.type != CERT_NONE) + { + const char *request_text = ""; + + if (cert_policy == CERT_SEND_IF_ASKED) + request_text = (send_cert)? "upon request":"without request"; + plog("we have a cert %s sending it %s" + , send_cert? "and are":"but are not", request_text); + } + else + { + plog("we don't have a cert"); + } + } + if (send_cert) + { + pb_stream cert_pbs; + + struct isakmp_cert cert_hd; + cert_hd.isacert_np = ISAKMP_NEXT_SIG; + cert_hd.isacert_type = mycert.type; + + if (!out_struct(&cert_hd, &isakmp_ipsec_certificate_desc, &md->rbody, &cert_pbs)) + return STF_INTERNAL_ERROR; + if (!out_chunk(get_mycert(mycert), &cert_pbs, "CERT")) + return STF_INTERNAL_ERROR; + close_output_pbs(&cert_pbs); + } + + /* HASH_R or SIG_R out */ + { + u_char hash_val[MAX_DIGEST_LEN]; + size_t hash_len = main_mode_hash(st, hash_val, FALSE, &r_id_pbs); + + if (auth_payload == ISAKMP_NEXT_HASH) + { + /* HASH_R out */ + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_hash_desc, &md->rbody + , hash_val, hash_len, "HASH_R")) + return STF_INTERNAL_ERROR; + } + else + { + /* SIG_R out */ + u_char sig_val[RSA_MAX_OCTETS]; + size_t sig_len = RSA_sign_hash(st->st_connection + , sig_val, hash_val, hash_len); + + if (sig_len == 0) + { + loglog(RC_LOG_SERIOUS, "unable to locate my private key for RSA Signature"); + return STF_FAIL + AUTHENTICATION_FAILED; + } + + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc + , &md->rbody, sig_val, sig_len, "SIG_R")) + return STF_INTERNAL_ERROR; + } + } + + /* encrypt message, sans fixed part of header */ + + if (!encrypt_message(&md->rbody, st)) + return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ + + /* Last block of Phase 1 (R3), kept for Phase 2 IV generation */ + DBG_cond_dump(DBG_CRYPT, "last encrypted block of Phase 1:" + , st->st_new_iv, st->st_new_iv_len); + + ISAKMP_SA_established(st->st_connection, st->st_serialno); + + /* Save Phase 1 IV */ + st->st_ph1_iv_len = st->st_new_iv_len; + set_ph1_iv(st, st->st_new_iv); + + return STF_OK; +} + +/* STATE_MAIN_I3: + * Handle HDR*;IDir;HASH/SIG_R from responder. + * + * Broken into parts to allow asynchronous DNS for KEY records. + * + * - main_inR3 to start + * - main_inR3_tail to finish or suspend for DNS lookup + * - main_inR3_continue to start main_inR3_tail again + */ + +static key_tail_fn main_inR3_tail; /* forward */ + +stf_status +main_inR3(struct msg_digest *md) +{ + return main_inR3_tail(md, NULL); +} + +static void +main_inR3_continue(struct adns_continuation *cr, err_t ugh) +{ + key_continue(cr, ugh, main_inR3_tail); +} + +static stf_status +main_inR3_tail(struct msg_digest *md +, struct key_continuation *kc) +{ + struct state *const st = md->st; + + /* ID and HASH_R or SIG_R in + * Note: this may switch the connection being used! + */ + { + stf_status r = main_id_and_auth(md, TRUE, main_inR3_continue, kc); + + if (r != STF_OK) + return r; + } + + /**************** done input ****************/ + + ISAKMP_SA_established(st->st_connection, st->st_serialno); + + /* Save Phase 1 IV */ + st->st_ph1_iv_len = st->st_new_iv_len; + set_ph1_iv(st, st->st_new_iv); + + + update_iv(st); /* finalize our Phase 1 IV */ + + return STF_OK; +} + +/* Handle first message of Phase 2 -- Quick Mode. + * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] --> + * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] + * (see RFC 2409 "IKE" 5.5) + * Installs inbound IPsec SAs. + * Although this seems early, we know enough to do so, and + * this way we know that it is soon enough to catch all + * packets that other side could send using this IPsec SA. + * + * Broken into parts to allow asynchronous DNS for TXT records: + * + * - quick_inI1_outR1 starts the ball rolling. + * It checks and parses enough to learn the Phase 2 IDs + * + * - quick_inI1_outR1_tail does the rest of the job + * unless DNS must be consulted. In that case, + * it starts a DNS query, salts away what is needed + * to continue, and suspends. Calls + * + quick_inI1_outR1_start_query + * + quick_inI1_outR1_process_answer + * + * - quick_inI1_outR1_continue will restart quick_inI1_outR1_tail + * when DNS comes back with an answer. + * + * A big chunk of quick_inI1_outR1_tail is executed twice. + * This is necessary because the set of connections + * might change while we are awaiting DNS. + * When first called, gateways_from_dns == NULL. If DNS is + * consulted asynchronously, gateways_from_dns != NULL the second time. + * Remember that our state object might disappear too! + * + * + * If the connection is opportunistic, we must verify delegation. + * + * 1. Check that we are authorized to be SG for + * our client. We look for the TXT record that + * delegates us. We also check that the public + * key (if present) matches the private key we used. + * Eventually, we should probably require DNSsec + * authentication for our side. + * + * 2. If our client TXT record did not include a + * public key, check the KEY record indicated + * by the identity in the TXT record. + * + * 3. If the peer's client is the peer itself, we + * consider it authenticated. Otherwise, we check + * the TXT record for the client to see that + * the identity of the SG matches the peer and + * that some public key (if present in the TXT) + * matches. We need not check the public key if + * it isn't in the TXT record. + * + * Since p isn't yet instantiated, we need to look + * in c for description of peer. + * + * We cannot afford to block waiting for a DNS query. + * The code here is structured as two halves: + * - process the result of just completed + * DNS query (if any) + * - if another query is needed, initiate the next + * DNS query and suspend + */ + +enum verify_oppo_step { + vos_fail, + vos_start, + vos_our_client, + vos_our_txt, +#ifdef USE_KEYRR + vos_our_key, +#endif /* USE_KEYRR */ + vos_his_client, + vos_done +}; + +static const char *const verify_step_name[] = { + "vos_fail", + "vos_start", + "vos_our_client", + "vos_our_txt", +#ifdef USE_KEYRR + "vos_our_key", +#endif /* USE_KEYRR */ + "vos_his_client", + "vos_done" +}; + +/* hold anything we can handle of a Phase 2 ID */ +struct p2id { + ip_subnet net; + u_int8_t proto; + u_int16_t port; +}; + +struct verify_oppo_bundle { + enum verify_oppo_step step; + bool failure_ok; /* if true, quick_inI1_outR1_continue will try + * other things on DNS failure */ + struct msg_digest *md; + struct p2id my, his; + unsigned int new_iv_len; /* p1st's might change */ + u_char new_iv[MAX_DIGEST_LEN]; + /* int whackfd; */ /* not needed because we are Responder */ +}; + +struct verify_oppo_continuation { + struct adns_continuation ac; /* common prefix */ + struct verify_oppo_bundle b; +}; + +static stf_status quick_inI1_outR1_tail(struct verify_oppo_bundle *b + , struct adns_continuation *ac); + +stf_status +quick_inI1_outR1(struct msg_digest *md) +{ + const struct state *const p1st = md->st; + struct connection *c = p1st->st_connection; + struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID]; + struct verify_oppo_bundle b; + + /* HASH(1) in */ + CHECK_QUICK_HASH(md + , quick_mode_hash12(hash_val, hash_pbs->roof, md->message_pbs.roof + , p1st, &md->hdr.isa_msgid, FALSE) + , "HASH(1)", "Quick I1"); + + /* [ IDci, IDcr ] in + * We do this now (probably out of physical order) because + * we wish to select the correct connection before we consult + * it for policy. + */ + + if (id_pd != NULL) + { + /* ??? we are assuming IPSEC_DOI */ + + /* IDci (initiator is peer) */ + + if (!decode_net_id(&id_pd->payload.ipsec_id, &id_pd->pbs + , &b.his.net, "peer client")) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* Hack for MS 818043 NAT-T Update */ + + if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN) + happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net)); + + /* End Hack for MS 818043 NAT-T Update */ + + b.his.proto = id_pd->payload.ipsec_id.isaiid_protoid; + b.his.port = id_pd->payload.ipsec_id.isaiid_port; + b.his.net.addr.u.v4.sin_port = htons(b.his.port); + + /* IDcr (we are responder) */ + + if (!decode_net_id(&id_pd->next->payload.ipsec_id, &id_pd->next->pbs + , &b.my.net, "our client")) + return STF_FAIL + INVALID_ID_INFORMATION; + + b.my.proto = id_pd->next->payload.ipsec_id.isaiid_protoid; + b.my.port = id_pd->next->payload.ipsec_id.isaiid_port; + b.my.net.addr.u.v4.sin_port = htons(b.my.port); + } + else + { + /* implicit IDci and IDcr: peer and self */ + if (!sameaddrtype(&c->spd.this.host_addr, &c->spd.that.host_addr)) + return STF_FAIL; + + happy(addrtosubnet(&c->spd.this.host_addr, &b.my.net)); + happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net)); + b.his.proto = b.my.proto = 0; + b.his.port = b.my.port = 0; + } + b.step = vos_start; + b.md = md; + b.new_iv_len = p1st->st_new_iv_len; + memcpy(b.new_iv, p1st->st_new_iv, p1st->st_new_iv_len); + return quick_inI1_outR1_tail(&b, NULL); +} + +static void +report_verify_failure(struct verify_oppo_bundle *b, err_t ugh) +{ + struct state *st = b->md->st; + char fgwb[ADDRTOT_BUF] + , cb[ADDRTOT_BUF]; + ip_address client; + err_t which; + + switch (b->step) + { + case vos_our_client: + case vos_our_txt: +#ifdef USE_KEYRR + case vos_our_key: +#endif /* USE_KEYRR */ + which = "our"; + networkof(&b->my.net, &client); + break; + + case vos_his_client: + which = "his"; + networkof(&b->his.net, &client); + break; + + case vos_start: + case vos_done: + case vos_fail: + default: + bad_case(b->step); + } + + addrtot(&st->st_connection->spd.that.host_addr, 0, fgwb, sizeof(fgwb)); + addrtot(&client, 0, cb, sizeof(cb)); + loglog(RC_OPPOFAILURE + , "gateway %s wants connection with %s as %s client, but DNS fails to confirm delegation: %s" + , fgwb, cb, which, ugh); +} + +static void +quick_inI1_outR1_continue(struct adns_continuation *cr, err_t ugh) +{ + stf_status r; + struct verify_oppo_continuation *vc = (void *)cr; + struct verify_oppo_bundle *b = &vc->b; + struct state *st = b->md->st; + + passert(cur_state == NULL); + /* if st == NULL, our state has been deleted -- just clean up */ + if (st != NULL) + { + passert(st->st_suspended_md == b->md); + st->st_suspended_md = NULL; /* no longer connected or suspended */ + cur_state = st; + if (!b->failure_ok && ugh != NULL) + { + report_verify_failure(b, ugh); + r = STF_FAIL + INVALID_ID_INFORMATION; + } + else + { + r = quick_inI1_outR1_tail(b, cr); + } + complete_state_transition(&b->md, r); + } + if (b->md != NULL) + release_md(b->md); + cur_state = NULL; +} + +static stf_status +quick_inI1_outR1_start_query(struct verify_oppo_bundle *b +, enum verify_oppo_step next_step) +{ + struct msg_digest *md = b->md; + struct state *p1st = md->st; + struct connection *c = p1st->st_connection; + struct verify_oppo_continuation *vc + = alloc_thing(struct verify_oppo_continuation, "verify continuation"); + struct id id /* subject of query */ + , *our_id /* needed for myid playing */ + , our_id_space; /* ephemeral: no need for unshare_id_content */ + ip_address client; + err_t ugh; + + /* Record that state is used by a suspended md */ + b->step = next_step; /* not just vc->b.step */ + vc->b = *b; + passert(p1st->st_suspended_md == NULL); + p1st->st_suspended_md = b->md; + + DBG(DBG_CONTROL, + { + char ours[SUBNETTOT_BUF]; + char his[SUBNETTOT_BUF]; + + subnettot(&c->spd.this.client, 0, ours, sizeof(ours)); + subnettot(&c->spd.that.client, 0, his, sizeof(his)); + + DBG_log("responding with DNS query - from %s to %s new state: %s" + , ours, his, verify_step_name[b->step]); + }); + + /* Resolve %myid in a cheesy way. + * We have to do the resolution because start_adns_query + * et al have insufficient information to do so. + * If %myid is already known, we'll use that value + * (XXX this may be a mistake: it could be stale). + * If %myid is unknown, we should check to see if + * there are credentials for the IP address or the FQDN. + * Instead, we'll just assume the IP address since we are + * acting as the responder and only the IP address would + * have gotten it to us. + * We don't even try to do this for the other side: + * %myid makes no sense for the other side (but it is syntactically + * legal). + */ + our_id = resolve_myid(&c->spd.this.id); + if (our_id->kind == ID_NONE) + { + iptoid(&c->spd.this.host_addr, &our_id_space); + our_id = &our_id_space; + } + + switch (next_step) + { + case vos_our_client: + networkof(&b->my.net, &client); + iptoid(&client, &id); + vc->b.failure_ok = b->failure_ok = FALSE; + ugh = start_adns_query(&id + , our_id + , T_TXT + , quick_inI1_outR1_continue + , &vc->ac); + break; + + case vos_our_txt: + vc->b.failure_ok = b->failure_ok = TRUE; + ugh = start_adns_query(our_id + , our_id /* self as SG */ + , T_TXT + , quick_inI1_outR1_continue + , &vc->ac); + break; + +#ifdef USE_KEYRR + case vos_our_key: + vc->b.failure_ok = b->failure_ok = FALSE; + ugh = start_adns_query(our_id + , NULL + , T_KEY + , quick_inI1_outR1_continue + , &vc->ac); + break; +#endif + + case vos_his_client: + networkof(&b->his.net, &client); + iptoid(&client, &id); + vc->b.failure_ok = b->failure_ok = FALSE; + ugh = start_adns_query(&id + , &c->spd.that.id + , T_TXT + , quick_inI1_outR1_continue + , &vc->ac); + break; + + default: + bad_case(next_step); + } + + if (ugh != NULL) + { + /* note: we'd like to use vc->b but vc has been freed + * so we have to use b. This is why we plunked next_state + * into b, not just vc->b. + */ + report_verify_failure(b, ugh); + p1st->st_suspended_md = NULL; + return STF_FAIL + INVALID_ID_INFORMATION; + } + else + { + return STF_SUSPEND; + } +} + +static enum verify_oppo_step +quick_inI1_outR1_process_answer(struct verify_oppo_bundle *b +, struct adns_continuation *ac +, struct state *p1st) +{ + struct connection *c = p1st->st_connection; + enum verify_oppo_step next_step; + err_t ugh = NULL; + + DBG(DBG_CONTROL, + { + char ours[SUBNETTOT_BUF]; + char his[SUBNETTOT_BUF]; + + subnettot(&c->spd.this.client, 0, ours, sizeof(ours)); + subnettot(&c->spd.that.client, 0, his, sizeof(his)); + DBG_log("responding on demand from %s to %s state: %s" + , ours, his, verify_step_name[b->step]); + }); + + /* process just completed DNS query (if any) */ + switch (b->step) + { + case vos_start: + /* no query to digest */ + next_step = vos_our_client; + break; + + case vos_our_client: + next_step = vos_his_client; + { + const struct RSA_private_key *pri = get_RSA_private_key(c); + struct gw_info *gwp; + + if (pri == NULL) + { + ugh = "we don't know our own key"; + break; + } + ugh = "our client does not delegate us as its Security Gateway"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + ugh = "our client delegates us as its Security Gateway but with the wrong public key"; + /* If there is no key in the TXT record, + * we count it as a win, but we will have + * to separately fetch and check the KEY record. + * If there is a key from the TXT record, + * we count it as a win if we match the key. + */ + if (!gwp->gw_key_present) + { + next_step = vos_our_txt; + ugh = NULL; /* good! */ + break; + } + else if (same_RSA_public_key(&pri->pub, &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + break; + + case vos_our_txt: + next_step = vos_his_client; + { + const struct RSA_private_key *pri = get_RSA_private_key(c); + + if (pri == NULL) + { + ugh = "we don't know our own key"; + break; + } + { + struct gw_info *gwp; + + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { +#ifdef USE_KEYRR + /* not an error yet, because we have to check KEY RR as well */ + ugh = NULL; +#else + ugh = "our client delegation depends on our " RRNAME " record, but it has the wrong public key"; +#endif + if (gwp->gw_key_present + && same_RSA_public_key(&pri->pub, &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } +#ifdef USE_KEYRR + next_step = vos_our_key; +#endif + } + } + } + break; + +#ifdef USE_KEYRR + case vos_our_key: + next_step = vos_his_client; + { + const struct RSA_private_key *pri = get_RSA_private_key(c); + + if (pri == NULL) + { + ugh = "we don't know our own key"; + break; + } + { + pubkey_list_t *kp; + + ugh = "our client delegation depends on our missing " RRNAME " record"; + for (kp = ac->keys_from_dns; kp != NULL; kp = kp->next) + { + ugh = "our client delegation depends on our " RRNAME " record, but it has the wrong public key"; + if (same_RSA_public_key(&pri->pub, &kp->key->u.rsa)) + { + /* do this only once a day */ + if (!logged_txt_warning) + { + loglog(RC_LOG_SERIOUS, "found KEY RR but not TXT RR. See http://www.freeswan.org/err/txt-change.html."); + logged_txt_warning = TRUE; + } + ugh = NULL; /* good! */ + break; + } + } + } + } + break; +#endif /* USE_KEYRR */ + + case vos_his_client: + next_step = vos_done; + { + struct gw_info *gwp; + + /* check that the public key that authenticated + * the ISAKMP SA (p1st) will do for this gateway. + */ + + ugh = "peer's client does not delegate to peer"; + for (gwp = ac->gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + ugh = "peer and its client disagree about public key"; + /* If there is a key from the TXT record, + * we count it as a win if we match the key. + * If there was no key, we claim a match since + * it implies fetching a KEY from the same + * place we must have gotten it. + */ + if (!gwp->gw_key_present + || same_RSA_public_key(&p1st->st_peer_pubkey->u.rsa + , &gwp->key->u.rsa)) + { + ugh = NULL; /* good! */ + break; + } + } + } + break; + + default: + bad_case(b->step); + } + + if (ugh != NULL) + { + report_verify_failure(b, ugh); + next_step = vos_fail; + } + return next_step; +} + +static stf_status +quick_inI1_outR1_tail(struct verify_oppo_bundle *b +, struct adns_continuation *ac) +{ + struct msg_digest *md = b->md; + struct state *const p1st = md->st; + struct connection *c = p1st->st_connection; + struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID]; + ip_subnet *our_net = &b->my.net + , *his_net = &b->his.net; + + u_char /* set by START_HASH_PAYLOAD: */ + *r_hashval, /* where in reply to jam hash value */ + *r_hash_start; /* from where to start hashing */ + + /* Now that we have identities of client subnets, we must look for + * a suitable connection (our current one only matches for hosts). + */ + { + struct connection *p = find_client_connection(c + , our_net, his_net, b->my.proto, b->my.port, b->his.proto, b->his.port); + + if (p == NULL) + { + /* This message occurs in very puzzling circumstances + * so we must add as much information and beauty as we can. + */ + struct end + me = c->spd.this, + he = c->spd.that; + char buf[2*SUBNETTOT_BUF + 2*ADDRTOT_BUF + 2*BUF_LEN + 2*ADDRTOT_BUF + 12]; /* + 12 for separating */ + size_t l; + + me.client = *our_net; + me.has_client = !subnetisaddr(our_net, &me.host_addr); + me.protocol = b->my.proto; + me.port = b->my.port; + + he.client = *his_net; + he.has_client = !subnetisaddr(his_net, &he.host_addr); + he.protocol = b->his.proto; + he.port = b->his.port; + + l = format_end(buf, sizeof(buf), &me, NULL, TRUE, LEMPTY); + l += snprintf(buf + l, sizeof(buf) - l, "..."); + (void)format_end(buf + l, sizeof(buf) - l, &he, NULL, FALSE, LEMPTY); + plog("cannot respond to IPsec SA request" + " because no connection is known for %s" + , buf); + return STF_FAIL + INVALID_ID_INFORMATION; + } + else if (p != c) + { + /* We've got a better connection: it can support the + * specified clients. But it may need instantiation. + */ + if (p->kind == CK_TEMPLATE) + { + /* Yup, it needs instantiation. How much? + * Is it a Road Warrior connection (simple) + * or is it an Opportunistic connection (needing gw validation)? + */ + if (p->policy & POLICY_OPPO) + { + /* Opportunistic case: delegation must be verified. + * Here be dragons. + */ + enum verify_oppo_step next_step; + ip_address our_client, his_client; + + passert(subnetishost(our_net) && subnetishost(his_net)); + networkof(our_net, &our_client); + networkof(his_net, &his_client); + + next_step = quick_inI1_outR1_process_answer(b, ac, p1st); + if (next_step == vos_fail) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* short circuit: if peer's client is self, + * accept that we've verified delegation in Phase 1 + */ + if (next_step == vos_his_client + && sameaddr(&c->spd.that.host_addr, &his_client)) + next_step = vos_done; + + /* the second chunk: initiate the next DNS query (if any) */ + DBG(DBG_CONTROL, + { + char ours[SUBNETTOT_BUF]; + char his[SUBNETTOT_BUF]; + + subnettot(&c->spd.this.client, 0, ours, sizeof(ours)); + subnettot(&c->spd.that.client, 0, his, sizeof(his)); + + DBG_log("responding on demand from %s to %s new state: %s" + , ours, his, verify_step_name[next_step]); + }); + + /* start next DNS query and suspend (if necessary) */ + if (next_step != vos_done) + return quick_inI1_outR1_start_query(b, next_step); + + /* Instantiate inbound Opportunistic connection, + * carrying over authenticated peer ID + * and filling in a few more details. + * We used to include gateways_from_dns, but that + * seems pointless at this stage of negotiation. + * We should record DNS sec use, if any -- belongs in + * state during perhaps. + */ + p = oppo_instantiate(p, &c->spd.that.host_addr, &c->spd.that.id + , NULL, &our_client, &his_client); + } + else + { + /* Plain Road Warrior: + * instantiate, carrying over authenticated peer ID + */ + p = rw_instantiate(p, &c->spd.that.host_addr, md->sender_port + , his_net, &c->spd.that.id); + } + } +#ifdef DEBUG + /* temporarily bump up cur_debugging to get "using..." message + * printed if we'd want it with new connection. + */ + { + lset_t old_cur_debugging = cur_debugging; + + cur_debugging |= p->extra_debugging; + DBG(DBG_CONTROL, DBG_log("using connection \"%s\"", p->name)); + cur_debugging = old_cur_debugging; + } +#endif + c = p; + } + /* fill in the client's true ip address/subnet */ + if (p->spd.that.has_client_wildcard) + { + p->spd.that.client = *his_net; + p->spd.that.has_client_wildcard = FALSE; + } + else if (is_virtual_connection(c)) + { + c->spd.that.client = *his_net; + c->spd.that.virt = NULL; + if (subnetishost(his_net) && addrinsubnet(&c->spd.that.host_addr, his_net)) + c->spd.that.has_client = FALSE; + } + + /* fill in the client's true port */ + if (p->spd.that.has_port_wildcard) + { + int port = htons(b->his.port); + + setportof(port, &p->spd.that.host_addr); + setportof(port, &p->spd.that.client.addr); + + p->spd.that.port = b->his.port; + p->spd.that.has_port_wildcard = FALSE; + } + } + + /* now that we are sure of our connection, create our new state */ + { + struct state *const st = duplicate_state(p1st); + + /* first: fill in missing bits of our new state object + * note: we don't copy over st_peer_pubkey, the public key + * that authenticated the ISAKMP SA. We only need it in this + * routine, so we can "reach back" to p1st to get it. + */ + + if (st->st_connection != c) + { + struct connection *t = st->st_connection; + + st->st_connection = c; + set_cur_connection(c); + connection_discard(t); + } + + st->st_try = 0; /* not our job to try again from start */ + + st->st_msgid = md->hdr.isa_msgid; + + st->st_new_iv_len = b->new_iv_len; + memcpy(st->st_new_iv, b->new_iv, b->new_iv_len); + + set_cur_state(st); /* (caller will reset) */ + md->st = st; /* feed back new state */ + + st->st_peeruserprotoid = b->his.proto; + st->st_peeruserport = b->his.port; + st->st_myuserprotoid = b->my.proto; + st->st_myuserport = b->my.port; + + insert_state(st); /* needs cookies, connection, and msgid */ + + /* copy the connection's + * IPSEC policy into our state. The ISAKMP policy is water under + * the bridge, I think. It will reflect the ISAKMP SA that we + * are using. + */ + st->st_policy = (p1st->st_policy & POLICY_ISAKMP_MASK) + | (c->policy & ~POLICY_ISAKMP_MASK); + + if (p1st->nat_traversal & NAT_T_DETECTED) + { + st->nat_traversal = p1st->nat_traversal; + nat_traversal_change_port_lookup(md, md->st); + } + else + { + st->nat_traversal = 0; + } + if ((st->nat_traversal & NAT_T_DETECTED) + && (st->nat_traversal & NAT_T_WITH_NATOA)) + { + nat_traversal_natoa_lookup(md); + } + + /* Start the output packet. + * + * proccess_packet() would automatically generate the HDR* + * payload if smc->first_out_payload is not ISAKMP_NEXT_NONE. + * We don't do this because we wish there to be no partially + * built output packet if we need to suspend for asynch DNS. + * + * We build the reply packet as we parse the message since + * the parse_ipsec_sa_body emits the reply SA + */ + + /* HDR* out */ + echo_hdr(md, TRUE, ISAKMP_NEXT_HASH); + + /* HASH(2) out -- first pass */ + START_HASH_PAYLOAD(md->rbody, ISAKMP_NEXT_SA); + + /* process SA (in and out) */ + { + struct payload_digest *const sapd = md->chain[ISAKMP_NEXT_SA]; + pb_stream r_sa_pbs; + struct isakmp_sa sa = sapd->payload.sa; + + /* sa header is unchanged -- except for np */ + sa.isasa_np = ISAKMP_NEXT_NONCE; + if (!out_struct(&sa, &isakmp_sa_desc, &md->rbody, &r_sa_pbs)) + return STF_INTERNAL_ERROR; + + /* parse and accept body */ + st->st_pfs_group = &unset_group; + RETURN_STF_FAILURE(parse_ipsec_sa_body(&sapd->pbs + , &sapd->payload.sa, &r_sa_pbs, FALSE, st)); + } + + passert(st->st_pfs_group != &unset_group); + + if ((st->st_policy & POLICY_PFS) && st->st_pfs_group == NULL) + { + loglog(RC_LOG_SERIOUS, "we require PFS but Quick I1 SA specifies no GROUP_DESCRIPTION"); + return STF_FAIL + NO_PROPOSAL_CHOSEN; /* ??? */ + } + + /* Ni in */ + RETURN_STF_FAILURE(accept_nonce(md, &st->st_ni, "Ni")); + + /* [ KE ] in (for PFS) */ + RETURN_STF_FAILURE(accept_PFS_KE(md, &st->st_gi, "Gi", "Quick Mode I1")); + + plog("responding to Quick Mode"); + + /**** finish reply packet: Nr [, KE ] [, IDci, IDcr ] ****/ + + /* Nr out */ + if (!build_and_ship_nonce(&st->st_nr, &md->rbody + , st->st_pfs_group != NULL? ISAKMP_NEXT_KE : id_pd != NULL? ISAKMP_NEXT_ID : ISAKMP_NEXT_NONE + , "Nr")) + return STF_INTERNAL_ERROR; + + /* [ KE ] out (for PFS) */ + + if (st->st_pfs_group != NULL) + { + if (!build_and_ship_KE(st, &st->st_gr, st->st_pfs_group + , &md->rbody, id_pd != NULL? ISAKMP_NEXT_ID : ISAKMP_NEXT_NONE)) + return STF_INTERNAL_ERROR; + + /* MPZ-Operations might be done after sending the packet... */ + compute_dh_shared(st, st->st_gi, st->st_pfs_group); + } + + /* [ IDci, IDcr ] out */ + if (id_pd != NULL) + { + struct isakmp_ipsec_id *p = (void *)md->rbody.cur; /* UGH! */ + + if (!out_raw(id_pd->pbs.start, pbs_room(&id_pd->pbs), &md->rbody, "IDci")) + return STF_INTERNAL_ERROR; + p->isaiid_np = ISAKMP_NEXT_ID; + + p = (void *)md->rbody.cur; /* UGH! */ + + if (!out_raw(id_pd->next->pbs.start, pbs_room(&id_pd->next->pbs), &md->rbody, "IDcr")) + return STF_INTERNAL_ERROR; + p->isaiid_np = ISAKMP_NEXT_NONE; + } + + if ((st->nat_traversal & NAT_T_WITH_NATOA) + && (st->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME)) + && (st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TRANSPORT)) + { + /** Send NAT-OA if our address is NATed and if we use Transport Mode */ + if (!nat_traversal_add_natoa(ISAKMP_NEXT_NONE, &md->rbody, md->st)) + { + return STF_INTERNAL_ERROR; + } + } + if ((st->nat_traversal & NAT_T_DETECTED) + && (st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TRANSPORT) + && (c->spd.that.has_client)) + { + /** Remove client **/ + addrtosubnet(&c->spd.that.host_addr, &c->spd.that.client); + c->spd.that.has_client = FALSE; + } + + /* Compute reply HASH(2) and insert in output */ + (void)quick_mode_hash12(r_hashval, r_hash_start, md->rbody.cur + , st, &st->st_msgid, TRUE); + + /* Derive new keying material */ + compute_keymats(st); + + /* Tell the kernel to establish the new inbound SA + * (unless the commit bit is set -- which we don't support). + * We do this before any state updating so that + * failure won't look like success. + */ + if (!install_inbound_ipsec_sa(st)) + return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ + + /* encrypt message, except for fixed part of header */ + + if (!encrypt_message(&md->rbody, st)) + return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ + + return STF_OK; + } +} + +/* + * Initialize RFC 3706 Dead Peer Detection + */ +static void +dpd_init(struct state *st) +{ + struct state *p1st = find_state(st->st_icookie, st->st_rcookie + , &st->st_connection->spd.that.host_addr, 0); + + if (p1st == NULL) + loglog(RC_LOG_SERIOUS, "could not find phase 1 state for DPD"); + else if (p1st->st_dpd) + { + plog("Dead Peer Detection (RFC 3706) enabled"); + /* randomize the first DPD event */ + + event_schedule(EVENT_DPD + , (0.5 + rand()/(RAND_MAX + 1.E0)) * st->st_connection->dpd_delay + , st); + } +} + +/* Handle (the single) message from Responder in Quick Mode. + * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] --> + * HDR*, HASH(3) + * (see RFC 2409 "IKE" 5.5) + * Installs inbound and outbound IPsec SAs, routing, etc. + */ +stf_status +quick_inR1_outI2(struct msg_digest *md) +{ + struct state *const st = md->st; + const struct connection *c = st->st_connection; + + /* HASH(2) in */ + CHECK_QUICK_HASH(md + , quick_mode_hash12(hash_val, hash_pbs->roof, md->message_pbs.roof + , st, &st->st_msgid, TRUE) + , "HASH(2)", "Quick R1"); + + /* SA in */ + { + struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA]; + + RETURN_STF_FAILURE(parse_ipsec_sa_body(&sa_pd->pbs + , &sa_pd->payload.sa, NULL, TRUE, st)); + } + + /* Nr in */ + RETURN_STF_FAILURE(accept_nonce(md, &st->st_nr, "Nr")); + + /* [ KE ] in (for PFS) */ + RETURN_STF_FAILURE(accept_PFS_KE(md, &st->st_gr, "Gr", "Quick Mode R1")); + + if (st->st_pfs_group != NULL) + compute_dh_shared(st, st->st_gr, st->st_pfs_group); + + /* [ IDci, IDcr ] in; these must match what we sent */ + + { + struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID]; + + if (id_pd != NULL) + { + /* ??? we are assuming IPSEC_DOI */ + + /* IDci (we are initiator) */ + + if (!check_net_id(&id_pd->payload.ipsec_id, &id_pd->pbs + , &st->st_myuserprotoid, &st->st_myuserport + , &st->st_connection->spd.this.client + , "our client")) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* IDcr (responder is peer) */ + + if (!check_net_id(&id_pd->next->payload.ipsec_id, &id_pd->next->pbs + , &st->st_peeruserprotoid, &st->st_peeruserport + , &st->st_connection->spd.that.client + , "peer client")) + return STF_FAIL + INVALID_ID_INFORMATION; + } + else + { + /* no IDci, IDcr: we must check that the defaults match our proposal */ + if (!subnetisaddr(&c->spd.this.client, &c->spd.this.host_addr) + || !subnetisaddr(&c->spd.that.client, &c->spd.that.host_addr)) + { + loglog(RC_LOG_SERIOUS, "IDci, IDcr payloads missing in message" + " but default does not match proposal"); + return STF_FAIL + INVALID_ID_INFORMATION; + } + } + } + + /* check the peer's group attributes */ + + { + const ietfAttrList_t *peer_list = NULL; + + get_peer_ca_and_groups(st->st_connection, &peer_list); + + if (!group_membership(peer_list, st->st_connection->name + , st->st_connection->spd.that.groups)) + { + char buf[BUF_LEN]; + + format_groups(st->st_connection->spd.that.groups, buf, BUF_LEN); + loglog(RC_LOG_SERIOUS, "peer is not member of one of the groups: %s" + , buf); + return STF_FAIL + INVALID_ID_INFORMATION; + } + } + + if ((st->nat_traversal & NAT_T_DETECTED) + && (st->nat_traversal & NAT_T_WITH_NATOA)) + { + nat_traversal_natoa_lookup(md); + } + + /* ??? We used to copy the accepted proposal into the state, but it was + * never used. From sa_pd->pbs.start, length pbs_room(&sa_pd->pbs). + */ + + /**************** build reply packet HDR*, HASH(3) ****************/ + + /* HDR* out done */ + + /* HASH(3) out -- since this is the only content, no passes needed */ + { + u_char /* set by START_HASH_PAYLOAD: */ + *r_hashval, /* where in reply to jam hash value */ + *r_hash_start; /* start of what is to be hashed */ + + START_HASH_PAYLOAD(md->rbody, ISAKMP_NEXT_NONE); + (void)quick_mode_hash3(r_hashval, st); + } + + /* Derive new keying material */ + compute_keymats(st); + + /* Tell the kernel to establish the inbound, outbound, and routing part + * of the new SA (unless the commit bit is set -- which we don't support). + * We do this before any state updating so that + * failure won't look like success. + */ + if (!install_ipsec_sa(st, TRUE)) + return STF_INTERNAL_ERROR; + + /* encrypt message, except for fixed part of header */ + + if (!encrypt_message(&md->rbody, st)) + return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ + + { + DBG(DBG_CONTROLMORE, DBG_log("inR1_outI2: instance %s[%ld], setting newest_ipsec_sa to #%ld (was #%ld) (spd.eroute=#%ld)" + , st->st_connection->name + , st->st_connection->instance_serial + , st->st_serialno + , st->st_connection->newest_ipsec_sa + , st->st_connection->spd.eroute_owner)); + } + + st->st_connection->newest_ipsec_sa = st->st_serialno; + + /* note (presumed) success */ + if (c->gw_info != NULL) + c->gw_info->key->last_worked_time = now(); + + /* If we want DPD on this connection then initialize it */ + if (st->st_connection->dpd_action != DPD_ACTION_NONE) + dpd_init(st); + + return STF_OK; +} + +/* Handle last message of Quick Mode. + * HDR*, HASH(3) -> done + * (see RFC 2409 "IKE" 5.5) + * Installs outbound IPsec SAs, routing, etc. + */ +stf_status +quick_inI2(struct msg_digest *md) +{ + struct state *const st = md->st; + + /* HASH(3) in */ + CHECK_QUICK_HASH(md, quick_mode_hash3(hash_val, st) + , "HASH(3)", "Quick I2"); + + /* Tell the kernel to establish the outbound and routing part of the new SA + * (the previous state established inbound) + * (unless the commit bit is set -- which we don't support). + * We do this before any state updating so that + * failure won't look like success. + */ + if (!install_ipsec_sa(st, FALSE)) + return STF_INTERNAL_ERROR; + + { + DBG(DBG_CONTROLMORE, DBG_log("inI2: instance %s[%ld], setting newest_ipsec_sa to #%ld (was #%ld) (spd.eroute=#%ld)" + , st->st_connection->name + , st->st_connection->instance_serial + , st->st_serialno + , st->st_connection->newest_ipsec_sa + , st->st_connection->spd.eroute_owner)); + } + + st->st_connection->newest_ipsec_sa = st->st_serialno; + + update_iv(st); /* not actually used, but tidy */ + + /* note (presumed) success */ + { + struct gw_info *gw = st->st_connection->gw_info; + + if (gw != NULL) + gw->key->last_worked_time = now(); + } + + /* If we want DPD on this connection then initialize it */ + if (st->st_connection->dpd_action != DPD_ACTION_NONE) + dpd_init(st); + + return STF_OK; +} + +static stf_status +send_isakmp_notification(struct state *st, u_int16_t type + , const void *data, size_t len) +{ + msgid_t msgid; + pb_stream reply; + pb_stream rbody; + u_char + *r_hashval, /* where in reply to jam hash value */ + *r_hash_start; /* start of what is to be hashed */ + + msgid = generate_msgid(st); + + init_pbs(&reply, reply_buffer, sizeof(reply_buffer), "ISAKMP notify"); + + /* HDR* */ + { + struct isakmp_hdr hdr; + + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_HASH; + hdr.isa_xchg = ISAKMP_XCHG_INFO; + hdr.isa_msgid = msgid; + hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; + memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); + memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); + if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) + impossible(); + } + /* HASH -- create and note space to be filled later */ + START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_N); + + /* NOTIFY */ + { + pb_stream notify_pbs; + struct isakmp_notification isan; + + isan.isan_np = ISAKMP_NEXT_NONE; + isan.isan_doi = ISAKMP_DOI_IPSEC; + isan.isan_protoid = PROTO_ISAKMP; + isan.isan_spisize = COOKIE_SIZE * 2; + isan.isan_type = type; + if (!out_struct(&isan, &isakmp_notification_desc, &rbody, ¬ify_pbs)) + return STF_INTERNAL_ERROR; + if (!out_raw(st->st_icookie, COOKIE_SIZE, ¬ify_pbs, "notify icookie")) + return STF_INTERNAL_ERROR; + if (!out_raw(st->st_rcookie, COOKIE_SIZE, ¬ify_pbs, "notify rcookie")) + return STF_INTERNAL_ERROR; + if (data != NULL && len > 0) + if (!out_raw(data, len, ¬ify_pbs, "notify data")) + return STF_INTERNAL_ERROR; + close_output_pbs(¬ify_pbs); + } + + { + /* finish computing HASH */ + struct hmac_ctx ctx; + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); + hmac_update(&ctx, (const u_char *) &msgid, sizeof(msgid_t)); + hmac_update(&ctx, r_hash_start, rbody.cur-r_hash_start); + hmac_final(r_hashval, &ctx); + + DBG(DBG_CRYPT, + DBG_log("HASH computed:"); + DBG_dump("", r_hashval, ctx.hmac_digest_size)); + } + + /* Encrypt message (preserve st_iv and st_new_iv) */ + { + u_char old_iv[MAX_DIGEST_LEN]; + u_char new_iv[MAX_DIGEST_LEN]; + + u_int old_iv_len = st->st_iv_len; + u_int new_iv_len = st->st_new_iv_len; + + if (old_iv_len > MAX_DIGEST_LEN || new_iv_len > MAX_DIGEST_LEN) + return STF_INTERNAL_ERROR; + + memcpy(old_iv, st->st_iv, old_iv_len); + memcpy(new_iv, st->st_new_iv, new_iv_len); + + init_phase2_iv(st, &msgid); + if (!encrypt_message(&rbody, st)) + return STF_INTERNAL_ERROR; + + /* restore preserved st_iv and st_new_iv */ + memcpy(st->st_iv, old_iv, old_iv_len); + memcpy(st->st_new_iv, new_iv, new_iv_len); + st->st_iv_len = old_iv_len; + st->st_new_iv_len = new_iv_len; + } + + /* Send packet (preserve st_tpacket) */ + { + chunk_t saved_tpacket = st->st_tpacket; + + setchunk(st->st_tpacket, reply.start, pbs_offset(&reply)); + send_packet(st, "ISAKMP notify"); + st->st_tpacket = saved_tpacket; + } + + return STF_IGNORE; +} + +/* + * DPD Out Initiator + */ +void +dpd_outI(struct state *p2st) +{ + struct state *st; + u_int32_t seqno; + time_t tm; + time_t idle_time; + time_t delay = p2st->st_connection->dpd_delay; + time_t timeout = p2st->st_connection->dpd_timeout; + + /* find the newest related Phase 1 state */ + st = find_phase1_state(p2st->st_connection, ISAKMP_SA_ESTABLISHED_STATES); + + if (st == NULL) + { + loglog(RC_LOG_SERIOUS, "DPD: Could not find newest phase 1 state"); + return; + } + + /* If no DPD, then get out of here */ + if (!st->st_dpd) + return; + + /* schedule the next periodic DPD event */ + event_schedule(EVENT_DPD, delay, p2st); + + /* Current time */ + tm = now(); + + /* Make sure we really need to invoke DPD */ + if (!was_eroute_idle(p2st, delay, &idle_time)) + { + DBG(DBG_CONTROL, + DBG_log("recent eroute activity %u seconds ago, " + "no need to send DPD notification" + , (int)idle_time) + ) + st->st_last_dpd = tm; + delete_dpd_event(st); + return; + } + + /* If an R_U_THERE has been sent or received recently, or if a + * companion Phase 2 SA has shown eroute activity, + * then we don't need to invoke DPD. + */ + if (tm < st->st_last_dpd + delay) + { + DBG(DBG_CONTROL, + DBG_log("recent DPD activity %u seconds ago, " + "no need to send DPD notification" + , (int)(tm - st->st_last_dpd)) + ) + return; + } + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + return; + + if (!st->st_dpd_seqno) + { + /* Get a non-zero random value that has room to grow */ + get_rnd_bytes((u_char *)&st->st_dpd_seqno, sizeof(st->st_dpd_seqno)); + st->st_dpd_seqno &= 0x7fff; + st->st_dpd_seqno++; + } + seqno = htonl(st->st_dpd_seqno); + + if (send_isakmp_notification(st, R_U_THERE, &seqno, sizeof(seqno)) != STF_IGNORE) + { + loglog(RC_LOG_SERIOUS, "DPD: Could not send R_U_THERE"); + return; + } + DBG(DBG_CONTROL, + DBG_log("sent DPD notification R_U_THERE with seqno = %u", st->st_dpd_seqno) + ) + st->st_dpd_expectseqno = st->st_dpd_seqno++; + st->st_last_dpd = tm; + /* Only schedule a new timeout if there isn't one currently, + * or if it would be sooner than the current timeout. */ + if (st->st_dpd_event == NULL + || st->st_dpd_event->ev_time > tm + timeout) + { + delete_dpd_event(st); + event_schedule(EVENT_DPD_TIMEOUT, timeout, st); + } +} + +/* + * DPD in Initiator, out Responder + */ +stf_status +dpd_inI_outR(struct state *st, struct isakmp_notification *const n, pb_stream *pbs) +{ + time_t tm = now(); + u_int32_t seqno; + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + loglog(RC_LOG_SERIOUS, "DPD: Received R_U_THERE for unestablished ISKAMP SA"); + return STF_IGNORE; + } + if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) + { + loglog(RC_LOG_SERIOUS, "DPD: R_U_THERE has invalid SPI length (%d)", n->isan_spisize); + return STF_FAIL + PAYLOAD_MALFORMED; + } + + if (memcmp(pbs->cur, st->st_icookie, COOKIE_SIZE) != 0) + { +#ifdef APPLY_CRISCO + /* Ignore it, cisco sends odd icookies */ +#else + loglog(RC_LOG_SERIOUS, "DPD: R_U_THERE has invalid icookie (broken Cisco?)"); + return STF_FAIL + INVALID_COOKIE; +#endif + } + pbs->cur += COOKIE_SIZE; + + if (memcmp(pbs->cur, st->st_rcookie, COOKIE_SIZE) != 0) + { + loglog(RC_LOG_SERIOUS, "DPD: R_U_THERE has invalid rcookie (broken Cisco?)"); + return STF_FAIL + INVALID_COOKIE; + } + pbs->cur += COOKIE_SIZE; + + if (pbs_left(pbs) != sizeof(seqno)) + { + loglog(RC_LOG_SERIOUS, "DPD: R_U_THERE has invalid data length (%d)" + , (int) pbs_left(pbs)); + return STF_FAIL + PAYLOAD_MALFORMED; + } + + seqno = ntohl(*(u_int32_t *)pbs->cur); + DBG(DBG_CONTROL, + DBG_log("received DPD notification R_U_THERE with seqno = %u", seqno) + ) + + if (st->st_dpd_peerseqno && seqno <= st->st_dpd_peerseqno) { + loglog(RC_LOG_SERIOUS, "DPD: Received old or duplicate R_U_THERE"); + return STF_IGNORE; + } + + st->st_dpd_peerseqno = seqno; + delete_dpd_event(st); + + if (send_isakmp_notification(st, R_U_THERE_ACK, pbs->cur, pbs_left(pbs)) != STF_IGNORE) + { + loglog(RC_LOG_SERIOUS, "DPD Info: could not send R_U_THERE_ACK"); + return STF_IGNORE; + } + DBG(DBG_CONTROL, + DBG_log("sent DPD notification R_U_THERE_ACK with seqno = %u", seqno) + ) + + st->st_last_dpd = tm; + return STF_IGNORE; +} + +/* + * DPD out Responder + */ +stf_status +dpd_inR(struct state *st, struct isakmp_notification *const n, pb_stream *pbs) +{ + u_int32_t seqno; + + if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) + { + loglog(RC_LOG_SERIOUS + , "DPD: Received R_U_THERE_ACK for unestablished ISKAMP SA"); + return STF_FAIL; + } + + if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) + { + loglog(RC_LOG_SERIOUS + , "DPD: R_U_THERE_ACK has invalid SPI length (%d)" + , n->isan_spisize); + return STF_FAIL + PAYLOAD_MALFORMED; + } + + if (memcmp(pbs->cur, st->st_icookie, COOKIE_SIZE) != 0) + { +#ifdef APPLY_CRISCO + /* Ignore it, cisco sends odd icookies */ +#else + loglog(RC_LOG_SERIOUS, "DPD: R_U_THERE_ACK has invalid icookie"); + return STF_FAIL + INVALID_COOKIE; +#endif + } + pbs->cur += COOKIE_SIZE; + + if (memcmp(pbs->cur, st->st_rcookie, COOKIE_SIZE) != 0) + { +#ifdef APPLY_CRISCO + /* Ignore it, cisco sends odd icookies */ +#else + loglog(RC_LOG_SERIOUS, "DPD: R_U_THERE_ACK has invalid rcookie"); + return STF_FAIL + INVALID_COOKIE; +#endif + } + pbs->cur += COOKIE_SIZE; + + if (pbs_left(pbs) != sizeof(seqno)) + { + loglog(RC_LOG_SERIOUS + , " DPD: R_U_THERE_ACK has invalid data length (%d)" + , (int) pbs_left(pbs)); + return STF_FAIL + PAYLOAD_MALFORMED; + } + + seqno = ntohl(*(u_int32_t *)pbs->cur); + DBG(DBG_CONTROL, + DBG_log("received DPD notification R_U_THERE_ACK with seqno = %u" + , seqno) + ) + + if (!st->st_dpd_expectseqno && seqno != st->st_dpd_expectseqno) + { + loglog(RC_LOG_SERIOUS + , "DPD: R_U_THERE_ACK has unexpected sequence number"); + return STF_FAIL + PAYLOAD_MALFORMED; + } + + st->st_dpd_expectseqno = 0; + delete_dpd_event(st); + return STF_IGNORE; +} + +/* + * DPD Timeout Function + * + * This function is called when a timeout DPD_EVENT occurs. We set clear/trap + * both the SA and the eroutes, depending on what the connection definition + * tells us (either 'hold' or 'clear') + */ +void +dpd_timeout(struct state *st) +{ + struct state *newest_phase1_st; + struct connection *c = st->st_connection; + int action = st->st_connection->dpd_action; + + passert(action == DPD_ACTION_HOLD + || action == DPD_ACTION_CLEAR + || DPD_ACTION_RESTART); + + /* is there a newer phase1_state? */ + newest_phase1_st = find_phase1_state(c, ISAKMP_SA_ESTABLISHED_STATES); + if (newest_phase1_st != NULL && newest_phase1_st != st) + { + plog("DPD: Phase1 state #%ld has been superseded by #%ld" + " - timeout ignored" + , st->st_serialno, newest_phase1_st->st_serialno); + return; + } + + loglog(RC_LOG_SERIOUS, "DPD: No response from peer - declaring peer dead"); + + /* delete the state, which is probably in phase 2 */ + set_cur_connection(c); + plog("DPD: Terminating all SAs using this connection"); + delete_states_by_connection(c, TRUE); + reset_cur_connection(); + + switch (action) + { + case DPD_ACTION_HOLD: + /* dpdaction=hold - Wipe the SA's but %trap the eroute so we don't + * leak traffic. Also, being in %trap means new packets will + * force an initiation of the conn again. + */ + loglog(RC_LOG_SERIOUS, "DPD: Putting connection into %%trap"); + break; + case DPD_ACTION_CLEAR: + /* dpdaction=clear - Wipe the SA & eroute - everything */ + loglog(RC_LOG_SERIOUS, "DPD: Clearing connection"); + unroute_connection(c); + break; + case DPD_ACTION_RESTART: + /* dpdaction=restart - Restart connection, + * except if roadwarrior connection + */ + loglog(RC_LOG_SERIOUS, "DPD: Restarting connection"); + unroute_connection(c); + initiate_connection(c->name, NULL_FD); + break; + default: + loglog(RC_LOG_SERIOUS, "DPD: unknown action"); + } +} + diff --git a/src/pluto/ipsec_doi.h b/src/pluto/ipsec_doi.h new file mode 100644 index 000000000..80b12c31d --- /dev/null +++ b/src/pluto/ipsec_doi.h @@ -0,0 +1,104 @@ +/* IPsec DOI and Oakley resolution routines + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: ipsec_doi.h,v 1.3 2005/01/06 22:10:44 as Exp $ + */ + +extern void echo_hdr(struct msg_digest *md, bool enc, u_int8_t np); + +extern void ipsecdoi_initiate(int whack_sock, struct connection *c + , lset_t policy, unsigned long try, so_serial_t replacing); + +extern void ipsecdoi_replace(struct state *st, unsigned long try); + +extern void init_phase2_iv(struct state *st, const msgid_t *msgid); + +extern stf_status quick_outI1(int whack_sock + , struct state *isakmp_sa + , struct connection *c + , lset_t policy + , unsigned long try + , so_serial_t replacing); + +extern state_transition_fn + main_inI1_outR1, + main_inR1_outI2, + main_inI2_outR2, + main_inR2_outI3, + main_inI3_outR3, + main_inR3, + quick_inI1_outR1, + quick_inR1_outI2, + quick_inI2; + +extern void send_delete(struct state *st); +extern void accept_delete(struct state *st, struct msg_digest *md + , struct payload_digest *p); +extern void close_message(pb_stream *pbs); +extern bool encrypt_message(pb_stream *pbs, struct state *st); + + +extern void send_notification_from_state(struct state *st, + enum state_kind state, u_int16_t type); +extern void send_notification_from_md(struct msg_digest *md, u_int16_t type); + +extern const char *init_pluto_vendorid(void); + +extern void dpd_outI(struct state *st); +extern stf_status dpd_inI_outR(struct state *st + , struct isakmp_notification *const n, pb_stream *n_pbs); +extern stf_status dpd_inR(struct state *st + , struct isakmp_notification *const n, pb_stream *n_pbs); +extern void dpd_timeout(struct state *st); + +/* START_HASH_PAYLOAD + * + * Emit a to-be-filled-in hash payload, noting the field start (r_hashval) + * and the start of the part of the message to be hashed (r_hash_start). + * This macro is magic. + * - it can cause the caller to return + * - it references variables local to the caller (r_hashval, r_hash_start, st) + */ +#define START_HASH_PAYLOAD(rbody, np) { \ + pb_stream hash_pbs; \ + if (!out_generic(np, &isakmp_hash_desc, &(rbody), &hash_pbs)) \ + return STF_INTERNAL_ERROR; \ + r_hashval = hash_pbs.cur; /* remember where to plant value */ \ + if (!out_zero(st->st_oakley.hasher->hash_digest_size, &hash_pbs, "HASH")) \ + return STF_INTERNAL_ERROR; \ + close_output_pbs(&hash_pbs); \ + r_hash_start = (rbody).cur; /* hash from after HASH payload */ \ +} + +/* CHECK_QUICK_HASH + * + * This macro is magic -- it cannot be expressed as a function. + * - it causes the caller to return! + * - it declares local variables and expects the "do_hash" argument + * expression to reference them (hash_val, hash_pbs) + */ +#define CHECK_QUICK_HASH(md, do_hash, hash_name, msg_name) { \ + pb_stream *const hash_pbs = &md->chain[ISAKMP_NEXT_HASH]->pbs; \ + u_char hash_val[MAX_DIGEST_LEN]; \ + size_t hash_len = do_hash; \ + if (pbs_left(hash_pbs) != hash_len \ + || memcmp(hash_pbs->cur, hash_val, hash_len) != 0) \ + { \ + DBG_cond_dump(DBG_CRYPT, "received " hash_name ":", hash_pbs->cur, pbs_left(hash_pbs)); \ + loglog(RC_LOG_SERIOUS, "received " hash_name " does not match computed value in " msg_name); \ + /* XXX Could send notification back */ \ + return STF_FAIL + INVALID_HASH_INFORMATION; \ + } \ + } + + diff --git a/src/pluto/kameipsec.h b/src/pluto/kameipsec.h new file mode 100644 index 000000000..5f08c7d38 --- /dev/null +++ b/src/pluto/kameipsec.h @@ -0,0 +1,47 @@ +#ifndef __IPSEC_H +#define __IPSEC_H 1 + +/* The definitions, required to talk to KAME racoon IKE. */ + +#define IPSEC_PORT_ANY 0 +#define IPSEC_ULPROTO_ANY 255 +#define IPSEC_PROTO_ANY 255 + +enum { + IPSEC_MODE_ANY = 0, /* We do not support this for SA */ + IPSEC_MODE_TRANSPORT = 1, + IPSEC_MODE_TUNNEL = 2 +}; + +enum { + IPSEC_DIR_ANY = 0, + IPSEC_DIR_INBOUND = 1, + IPSEC_DIR_OUTBOUND = 2, + IPSEC_DIR_FWD = 3, /* It is our own */ + IPSEC_DIR_MAX = 4, + IPSEC_DIR_INVALID = 5 +}; + +enum { + IPSEC_POLICY_DISCARD = 0, + IPSEC_POLICY_NONE = 1, + IPSEC_POLICY_IPSEC = 2, + IPSEC_POLICY_ENTRUST = 3, + IPSEC_POLICY_BYPASS = 4 +}; + +enum { + IPSEC_LEVEL_DEFAULT = 0, + IPSEC_LEVEL_USE = 1, + IPSEC_LEVEL_REQUIRE = 2, + IPSEC_LEVEL_UNIQUE = 3 +}; + +#define IPSEC_MANUAL_REQID_MAX 0x3fff + +#define IPSEC_REPLAYWSIZE 32 + +#define IP_IPSEC_POLICY 16 +#define IPV6_IPSEC_POLICY 34 + +#endif /* __IPSEC_H */ diff --git a/src/pluto/kernel.c b/src/pluto/kernel.c new file mode 100644 index 000000000..663fa7230 --- /dev/null +++ b/src/pluto/kernel.c @@ -0,0 +1,2995 @@ +/* routines that interface with the kernel's IPsec mechanism + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel.c,v 1.26 2006/04/29 18:16:02 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef KLIPS +#include +#include /* for select(2) */ +#include /* for select(2) */ +#include +#include +#include "kameipsec.h" +#endif /* KLIPS */ + +#include "constants.h" +#include "defs.h" +#include "rnd.h" +#include "id.h" +#include "connections.h" +#include "state.h" +#include "timer.h" +#include "kernel.h" +#include "kernel_netlink.h" +#include "kernel_pfkey.h" +#include "kernel_noklips.h" +#include "log.h" +#include "ca.h" +#include "server.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ +#include "keys.h" +#include "nat_traversal.h" +#include "alg_info.h" +#include "kernel_alg.h" + + +bool can_do_IPcomp = TRUE; /* can system actually perform IPCOMP? */ + +/* How far can IPsec messages arrive out of order before the anti-replay + * logic loses track and swats them? 64 is the best KLIPS can do. + * And 32 is the best XFRM can do... + */ +#define REPLAY_WINDOW 64 +#define REPLAY_WINDOW_XFRM 32 + +/* test if the routes required for two different connections agree + * It is assumed that the destination subnets agree; we are only + * testing that the interfaces and nexthops match. + */ +#define routes_agree(c, d) ((c)->interface == (d)->interface \ + && sameaddr(&(c)->spd.this.host_nexthop, &(d)->spd.this.host_nexthop)) + +#ifndef KLIPS + +bool no_klips = TRUE; /* don't actually use KLIPS */ + +#else /* !KLIPS */ + +/* bare (connectionless) shunt (eroute) table + * + * Bare shunts are those that don't "belong" to a connection. + * This happens because some %trapped traffic hasn't yet or cannot be + * assigned to a connection. The usual reason is that we cannot discover + * the peer SG. Another is that even when the peer has been discovered, + * it may be that no connection matches all the particulars. + * We record them so that, with scanning, we can discover + * which %holds are news and which others should expire. + */ + +#define SHUNT_SCAN_INTERVAL (60 * 2) /* time between scans of eroutes */ + +/* SHUNT_PATIENCE only has resolution down to a multiple of the sample rate, + * SHUNT_SCAN_INTERVAL. + * By making SHUNT_PATIENCE an odd multiple of half of SHUNT_SCAN_INTERVAL, + * we minimize the effects of jitter. + */ +#define SHUNT_PATIENCE (SHUNT_SCAN_INTERVAL * 15 / 2) /* inactivity timeout */ + +struct bare_shunt { + policy_prio_t policy_prio; + ip_subnet ours; + ip_subnet his; + ip_said said; + int transport_proto; + unsigned long count; + time_t last_activity; + char *why; + struct bare_shunt *next; +}; + +static struct bare_shunt *bare_shunts = NULL; + +#ifdef DEBUG +static void +DBG_bare_shunt(const char *op, const struct bare_shunt *bs) +{ + DBG(DBG_KLIPS, + { + int ourport = ntohs(portof(&(bs)->ours.addr)); + int hisport = ntohs(portof(&(bs)->his.addr)); + char ourst[SUBNETTOT_BUF]; + char hist[SUBNETTOT_BUF]; + char sat[SATOT_BUF]; + char prio[POLICY_PRIO_BUF]; + + subnettot(&(bs)->ours, 0, ourst, sizeof(ourst)); + subnettot(&(bs)->his, 0, hist, sizeof(hist)); + satot(&(bs)->said, 0, sat, sizeof(sat)); + fmt_policy_prio(bs->policy_prio, prio); + DBG_log("%s bare shunt %p %s:%d -> %s:%d => %s:%d %s %s" + , op, (const void *)(bs), ourst, ourport, hist, hisport + , sat, (bs)->transport_proto, prio, (bs)->why); + }); +} +#else /* !DEBUG */ +#define DBG_bare_shunt(op, bs) {} +#endif /* !DEBUG */ + +/* The orphaned_holds table records %holds for which we + * scan_proc_shunts found no representation of in any connection. + * The corresponding ACQUIRE message might have been lost. + */ +struct eroute_info *orphaned_holds = NULL; + +/* forward declaration */ +static bool shunt_eroute(struct connection *c + , struct spd_route *sr + , enum routing_t rt_kind + , unsigned int op, const char *opname); +static void set_text_said(char *text_said + , const ip_address *dst + , ipsec_spi_t spi + , int proto); + +bool no_klips = FALSE; /* don't actually use KLIPS */ + +static const struct pfkey_proto_info null_proto_info[2] = { + { + proto: IPPROTO_ESP, + encapsulation: ENCAPSULATION_MODE_TRANSPORT, + reqid: 0 + }, + { + proto: 0, + encapsulation: 0, + reqid: 0 + } +}; + +void +record_and_initiate_opportunistic(const ip_subnet *ours + , const ip_subnet *his + , int transport_proto + , const char *why) +{ + passert(samesubnettype(ours, his)); + + /* Add to bare shunt list. + * We need to do this because the shunt was installed by KLIPS + * which can't do this itself. + */ + { + struct bare_shunt *bs = alloc_thing(struct bare_shunt, "bare shunt"); + + bs->why = clone_str(why, "story for bare shunt"); + bs->ours = *ours; + bs->his = *his; + bs->transport_proto = transport_proto; + bs->policy_prio = BOTTOM_PRIO; + + bs->said.proto = SA_INT; + bs->said.spi = htonl(SPI_HOLD); + bs->said.dst = *aftoinfo(subnettypeof(ours))->any; + + bs->count = 0; + bs->last_activity = now(); + + bs->next = bare_shunts; + bare_shunts = bs; + DBG_bare_shunt("add", bs); + } + + /* actually initiate opportunism */ + { + ip_address src, dst; + + networkof(ours, &src); + networkof(his, &dst); + initiate_opportunistic(&src, &dst, transport_proto, TRUE, NULL_FD); + } + + /* if present, remove from orphaned_holds list. + * NOTE: we do this last in case ours or his is a pointer into a member. + */ + { + struct eroute_info **pp, *p; + + for (pp = &orphaned_holds; (p = *pp) != NULL; pp = &p->next) + { + if (samesubnet(ours, &p->ours) + && samesubnet(his, &p->his) + && transport_proto == p->transport_proto + && portof(&ours->addr) == portof(&p->ours.addr) + && portof(&his->addr) == portof(&p->his.addr)) + { + *pp = p->next; + pfree(p); + break; + } + } + } +} + +#endif /* KLIPS */ + +static unsigned get_proto_reqid(unsigned base, int proto) +{ + switch (proto) + { + default: + case IPPROTO_COMP: + base++; + /* fall through */ + case IPPROTO_ESP: + base++; + /* fall through */ + case IPPROTO_AH: + break; + } + + return base; +} + +/* Generate Unique SPI numbers. + * + * The specs say that the number must not be less than IPSEC_DOI_SPI_MIN. + * Pluto generates numbers not less than IPSEC_DOI_SPI_OUR_MIN, + * reserving numbers in between for manual keying (but we cannot so + * restrict numbers generated by our peer). + * XXX This should be replaced by a call to the kernel when + * XXX we get an API. + * The returned SPI is in network byte order. + * We use a random number as the initial SPI so that there is + * a good chance that different Pluto instances will choose + * different SPIs. This is good for two reasons. + * - the keying material for the initiator and responder only + * differs if the SPIs differ. + * - if Pluto is restarted, it would otherwise recycle the SPI + * numbers and confuse everything. When the kernel generates + * SPIs, this will no longer matter. + * We then allocate numbers sequentially. Thus we don't have to + * check if the number was previously used (assuming that no + * SPI lives longer than 4G of its successors). + */ +ipsec_spi_t +get_ipsec_spi(ipsec_spi_t avoid, int proto, struct spd_route *sr, bool tunnel) +{ + static ipsec_spi_t spi = 0; /* host order, so not returned directly! */ + char text_said[SATOT_BUF]; + + set_text_said(text_said, &sr->this.host_addr, 0, proto); + + if (kernel_ops->get_spi) + return kernel_ops->get_spi(&sr->that.host_addr + , &sr->this.host_addr, proto, tunnel + , get_proto_reqid(sr->reqid, proto) + , IPSEC_DOI_SPI_OUR_MIN, 0xffffffff + , text_said); + + spi++; + while (spi < IPSEC_DOI_SPI_OUR_MIN || spi == ntohl(avoid)) + get_rnd_bytes((u_char *)&spi, sizeof(spi)); + + DBG(DBG_CONTROL, + { + ipsec_spi_t spi_net = htonl(spi); + + DBG_dump("generate SPI:", (u_char *)&spi_net, sizeof(spi_net)); + }); + + return htonl(spi); +} + +/* Generate Unique CPI numbers. + * The result is returned as an SPI (4 bytes) in network order! + * The real bits are in the nework-low-order 2 bytes. + * Modelled on get_ipsec_spi, but range is more limited: + * 256-61439. + * If we can't find one easily, return 0 (a bad SPI, + * no matter what order) indicating failure. + */ +ipsec_spi_t +get_my_cpi(struct spd_route *sr, bool tunnel) +{ + static cpi_t + first_busy_cpi = 0, + latest_cpi; + char text_said[SATOT_BUF]; + + set_text_said(text_said, &sr->this.host_addr, 0, IPPROTO_COMP); + + if (kernel_ops->get_spi) + return kernel_ops->get_spi(&sr->that.host_addr + , &sr->this.host_addr, IPPROTO_COMP, tunnel + , get_proto_reqid(sr->reqid, IPPROTO_COMP) + , IPCOMP_FIRST_NEGOTIATED, IPCOMP_LAST_NEGOTIATED + , text_said); + + while (!(IPCOMP_FIRST_NEGOTIATED <= first_busy_cpi && first_busy_cpi < IPCOMP_LAST_NEGOTIATED)) + { + get_rnd_bytes((u_char *)&first_busy_cpi, sizeof(first_busy_cpi)); + latest_cpi = first_busy_cpi; + } + + latest_cpi++; + + if (latest_cpi == first_busy_cpi) + find_my_cpi_gap(&latest_cpi, &first_busy_cpi); + + if (latest_cpi > IPCOMP_LAST_NEGOTIATED) + latest_cpi = IPCOMP_FIRST_NEGOTIATED; + + return htonl((ipsec_spi_t)latest_cpi); +} + +/* invoke the updown script to do the routing and firewall commands required + * + * The user-specified updown script is run. Parameters are fed to it in + * the form of environment variables. All such environment variables + * have names starting with "PLUTO_". + * + * The operation to be performed is specified by PLUTO_VERB. This + * verb has a suffix "-host" if the client on this end is just the + * host; otherwise the suffix is "-client". If the address family + * of the host is IPv6, an extra suffix of "-v6" is added. + * + * "prepare-host" and "prepare-client" are used to delete a route + * that may exist (due to forces outside of Pluto). It is used to + * prepare for pluto creating a route. + * + * "route-host" and "route-client" are used to install a route. + * Since routing is based only on destination, the PLUTO_MY_CLIENT_* + * values are probably of no use (using them may signify a bug). + * + * "unroute-host" and "unroute-client" are used to delete a route. + * Since routing is based only on destination, the PLUTO_MY_CLIENT_* + * values are probably of no use (using them may signify a bug). + * + * "up-host" and "up-client" are run when an eroute is added (not replaced). + * They are useful for adjusting a firewall: usually for adding a rule + * to let processed packets flow between clients. Note that only + * one eroute may exist for a pair of client subnets but inbound + * IPsec SAs may persist without an eroute. + * + * "down-host" and "down-client" are run when an eroute is deleted. + * They are useful for adjusting a firewall. + */ + +#ifndef DEFAULT_UPDOWN +# define DEFAULT_UPDOWN "ipsec _updown" +#endif + +static bool +do_command(struct connection *c, struct spd_route *sr, const char *verb) +{ + char cmd[1536]; /* arbitrary limit on shell command length */ + const char *verb_suffix; + + /* figure out which verb suffix applies */ + { + const char *hs, *cs; + + switch (addrtypeof(&sr->this.host_addr)) + { + case AF_INET: + hs = "-host"; + cs = "-client"; + break; + case AF_INET6: + hs = "-host-v6"; + cs = "-client-v6"; + break; + default: + loglog(RC_LOG_SERIOUS, "unknown address family"); + return FALSE; + } + verb_suffix = subnetisaddr(&sr->this.client, &sr->this.host_addr) + ? hs : cs; + } + + /* form the command string */ + { + char + nexthop_str[sizeof("PLUTO_NEXT_HOP='' ") +ADDRTOT_BUF] = "", + srcip_str[sizeof("PLUTO_MY_SOURCEIP='' ")+ADDRTOT_BUF] = "", + me_str[ADDRTOT_BUF], + myid_str[BUF_LEN], + myclient_str[SUBNETTOT_BUF], + myclientnet_str[ADDRTOT_BUF], + myclientmask_str[ADDRTOT_BUF], + peer_str[ADDRTOT_BUF], + peerid_str[BUF_LEN], + peerclient_str[SUBNETTOT_BUF], + peerclientnet_str[ADDRTOT_BUF], + peerclientmask_str[ADDRTOT_BUF], + peerca_str[BUF_LEN], + secure_myid_str[BUF_LEN] = "", + secure_peerid_str[BUF_LEN] = "", + secure_peerca_str[BUF_LEN] = ""; + ip_address ta; + pubkey_list_t *p; + + if (addrbytesptr(&sr->this.host_nexthop, NULL) + && !isanyaddr(&sr->this.host_nexthop)) + { + char *n; + + strcpy(nexthop_str, "PLUTO_NEXT_HOP='"); + n = nexthop_str + strlen(nexthop_str); + + addrtot(&sr->this.host_nexthop, 0 + ,n , sizeof(nexthop_str)-strlen(nexthop_str)); + strncat(nexthop_str, "' ", sizeof(nexthop_str)); + } + + if (addrbytesptr(&sr->this.host_srcip, NULL) + && !isanyaddr(&sr->this.host_srcip)) + { + char *n; + + strcpy(srcip_str, "PLUTO_MY_SOURCEIP='"); + n = srcip_str + strlen(srcip_str); + + addrtot(&sr->this.host_srcip, 0 + ,n , sizeof(srcip_str)-strlen(srcip_str)); + strncat(srcip_str, "' ", sizeof(srcip_str)); + } + + addrtot(&sr->this.host_addr, 0, me_str, sizeof(me_str)); + idtoa(&sr->this.id, myid_str, sizeof(myid_str)); + escape_metachar(myid_str, secure_myid_str, sizeof(secure_myid_str)); + subnettot(&sr->this.client, 0, myclient_str, sizeof(myclientnet_str)); + networkof(&sr->this.client, &ta); + addrtot(&ta, 0, myclientnet_str, sizeof(myclientnet_str)); + maskof(&sr->this.client, &ta); + addrtot(&ta, 0, myclientmask_str, sizeof(myclientmask_str)); + + addrtot(&sr->that.host_addr, 0, peer_str, sizeof(peer_str)); + idtoa(&sr->that.id, peerid_str, sizeof(peerid_str)); + escape_metachar(peerid_str, secure_peerid_str, sizeof(secure_peerid_str)); + subnettot(&sr->that.client, 0, peerclient_str, sizeof(peerclientnet_str)); + networkof(&sr->that.client, &ta); + addrtot(&ta, 0, peerclientnet_str, sizeof(peerclientnet_str)); + maskof(&sr->that.client, &ta); + addrtot(&ta, 0, peerclientmask_str, sizeof(peerclientmask_str)); + + for (p = pubkeys; p != NULL; p = p->next) + { + pubkey_t *key = p->key; + int pathlen; + + if (key->alg == PUBKEY_ALG_RSA && same_id(&sr->that.id, &key->id) + && trusted_ca(key->issuer, sr->that.ca, &pathlen)) + { + dntoa_or_null(peerca_str, BUF_LEN, key->issuer, ""); + escape_metachar(peerca_str, secure_peerca_str, sizeof(secure_peerca_str)); + break; + } + } + + if (-1 == snprintf(cmd, sizeof(cmd) + , "2>&1 " /* capture stderr along with stdout */ + "PLUTO_VERSION='1.1' " /* change VERSION when interface spec changes */ + "PLUTO_VERB='%s%s' " + "PLUTO_CONNECTION='%s' " + "%s" /* optional PLUTO_NEXT_HOP */ + "PLUTO_INTERFACE='%s' " + "%s" /* optional PLUTO_HOST_ACCESS */ + "PLUTO_REQID='%u' " + "PLUTO_ME='%s' " + "PLUTO_MY_ID='%s' " + "PLUTO_MY_CLIENT='%s' " + "PLUTO_MY_CLIENT_NET='%s' " + "PLUTO_MY_CLIENT_MASK='%s' " + "PLUTO_MY_PORT='%u' " + "PLUTO_MY_PROTOCOL='%u' " + "PLUTO_PEER='%s' " + "PLUTO_PEER_ID='%s' " + "PLUTO_PEER_CLIENT='%s' " + "PLUTO_PEER_CLIENT_NET='%s' " + "PLUTO_PEER_CLIENT_MASK='%s' " + "PLUTO_PEER_PORT='%u' " + "PLUTO_PEER_PROTOCOL='%u' " + "PLUTO_PEER_CA='%s' " + "%s" /* optional PLUTO_MY_SRCIP */ + "%s" /* actual script */ + , verb, verb_suffix + , c->name + , nexthop_str + , c->interface->vname + , sr->this.hostaccess? "PLUTO_HOST_ACCESS='1' " : "" + , sr->reqid + 1 /* ESP requid */ + , me_str + , secure_myid_str + , myclient_str + , myclientnet_str + , myclientmask_str + , sr->this.port + , sr->this.protocol + , peer_str + , secure_peerid_str + , peerclient_str + , peerclientnet_str + , peerclientmask_str + , sr->that.port + , sr->that.protocol + , secure_peerca_str + , srcip_str + , sr->this.updown == NULL? DEFAULT_UPDOWN : sr->this.updown)) + { + loglog(RC_LOG_SERIOUS, "%s%s command too long!", verb, verb_suffix); + return FALSE; + } + } + + DBG(DBG_CONTROL, DBG_log("executing %s%s: %s" + , verb, verb_suffix, cmd)); + +#ifdef KLIPS + if (!no_klips) + { + /* invoke the script, catching stderr and stdout + * It may be of concern that some file descriptors will + * be inherited. For the ones under our control, we + * have done fcntl(fd, F_SETFD, FD_CLOEXEC) to prevent this. + * Any used by library routines (perhaps the resolver or syslog) + * will remain. + */ + FILE *f = popen(cmd, "r"); + + if (f == NULL) + { + loglog(RC_LOG_SERIOUS, "unable to popen %s%s command", verb, verb_suffix); + return FALSE; + } + + /* log any output */ + for (;;) + { + /* if response doesn't fit in this buffer, it will be folded */ + char resp[256]; + + if (fgets(resp, sizeof(resp), f) == NULL) + { + if (ferror(f)) + { + log_errno((e, "fgets failed on output of %s%s command" + , verb, verb_suffix)); + return FALSE; + } + else + { + passert(feof(f)); + break; + } + } + else + { + char *e = resp + strlen(resp); + + if (e > resp && e[-1] == '\n') + e[-1] = '\0'; /* trim trailing '\n' */ + plog("%s%s output: %s", verb, verb_suffix, resp); + } + } + + /* report on and react to return code */ + { + int r = pclose(f); + + if (r == -1) + { + log_errno((e, "pclose failed for %s%s command" + , verb, verb_suffix)); + return FALSE; + } + else if (WIFEXITED(r)) + { + if (WEXITSTATUS(r) != 0) + { + loglog(RC_LOG_SERIOUS, "%s%s command exited with status %d" + , verb, verb_suffix, WEXITSTATUS(r)); + return FALSE; + } + } + else if (WIFSIGNALED(r)) + { + loglog(RC_LOG_SERIOUS, "%s%s command exited with signal %d" + , verb, verb_suffix, WTERMSIG(r)); + return FALSE; + } + else + { + loglog(RC_LOG_SERIOUS, "%s%s command exited with unknown status %d" + , verb, verb_suffix, r); + return FALSE; + } + } + } +#endif /* KLIPS */ + return TRUE; +} + +/* Check that we can route (and eroute). Diagnose if we cannot. */ + +enum routability { + route_impossible = 0, + route_easy = 1, + route_nearconflict = 2, + route_farconflict = 3 +}; + +static enum routability +could_route(struct connection *c) +{ + struct spd_route *esr, *rosr; + struct connection *ero /* who, if anyone, owns our eroute? */ + , *ro = route_owner(c, &rosr, &ero, &esr); /* who owns our route? */ + + /* it makes no sense to route a connection that is ISAKMP-only */ + if (!NEVER_NEGOTIATE(c->policy) && !HAS_IPSEC_POLICY(c->policy)) + { + loglog(RC_ROUTE, "cannot route an ISAKMP-only connection"); + return route_impossible; + } + + /* if this is a Road Warrior template, we cannot route. + * Opportunistic template is OK. + */ + if (c->kind == CK_TEMPLATE && !(c->policy & POLICY_OPPO)) + { + loglog(RC_ROUTE, "cannot route Road Warrior template"); + return route_impossible; + } + + /* if we don't know nexthop, we cannot route */ + if (isanyaddr(&c->spd.this.host_nexthop)) + { + loglog(RC_ROUTE, "cannot route connection without knowing our nexthop"); + return route_impossible; + } + + /* if routing would affect IKE messages, reject */ + if (!no_klips + && c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT + && c->spd.this.host_port != IKE_UDP_PORT + && addrinsubnet(&c->spd.that.host_addr, &c->spd.that.client)) + { + loglog(RC_LOG_SERIOUS, "cannot install route: peer is within its client"); + return route_impossible; + } + + /* If there is already a route for peer's client subnet + * and it disagrees about interface or nexthop, we cannot steal it. + * Note: if this connection is already routed (perhaps for another + * state object), the route will agree. + * This is as it should be -- it will arise during rekeying. + */ + if (ro != NULL && !routes_agree(ro, c)) + { + loglog(RC_LOG_SERIOUS, "cannot route -- route already in use for \"%s\"" + , ro->name); + return route_impossible; /* another connection already + using the eroute */ + } + +#ifdef KLIPS + /* if there is an eroute for another connection, there is a problem */ + if (ero != NULL && ero != c) + { + struct connection *ero2, *ero_top; + struct connection *inside, *outside; + + /* + * note, wavesec (PERMANENT) goes *outside* and + * OE goes *inside* (TEMPLATE) + */ + inside = NULL; + outside= NULL; + if (ero->kind == CK_PERMANENT + && c->kind == CK_TEMPLATE) + { + outside = ero; + inside = c; + } + else if (c->kind == CK_PERMANENT + && ero->kind == CK_TEMPLATE) + { + outside = c; + inside = ero; + } + + /* okay, check again, with correct order */ + if (outside && outside->kind == CK_PERMANENT + && inside && inside->kind == CK_TEMPLATE) + { + char inst[CONN_INST_BUF]; + + /* this is a co-terminal attempt of the "near" kind. */ + /* when chaining, we chain from inside to outside */ + + /* XXX permit multiple deep connections? */ + passert(inside->policy_next == NULL); + + inside->policy_next = outside; + + /* since we are going to steal the eroute from the secondary + * policy, we need to make sure that it no longer thinks that + * it owns the eroute. + */ + outside->spd.eroute_owner = SOS_NOBODY; + outside->spd.routing = RT_UNROUTED_KEYED; + + /* set the priority of the new eroute owner to be higher + * than that of the current eroute owner + */ + inside->prio = outside->prio + 1; + + fmt_conn_instance(inside, inst); + + loglog(RC_LOG_SERIOUS + , "conflict on eroute (%s), switching eroute to %s and linking %s" + , inst, inside->name, outside->name); + + return route_nearconflict; + } + + /* look along the chain of policies for one with the same name */ + ero_top = ero; + + for (ero2 = ero; ero2 != NULL; ero2 = ero->policy_next) + { + if (ero2->kind == CK_TEMPLATE + && streq(ero2->name, c->name)) + break; + } + + /* If we fell of the end of the list, then we found no TEMPLATE + * so there must be a conflict that we can't resolve. + * As the names are not equal, then we aren't replacing/rekeying. + */ + if (ero2 == NULL) + { + char inst[CONN_INST_BUF]; + + fmt_conn_instance(ero, inst); + + loglog(RC_LOG_SERIOUS + , "cannot install eroute -- it is in use for \"%s\"%s #%lu" + , ero->name, inst, esr->eroute_owner); + return FALSE; /* another connection already using the eroute */ + } + } +#endif /* KLIPS */ + return route_easy; +} + +bool +trap_connection(struct connection *c) +{ + switch (could_route(c)) + { + case route_impossible: + return FALSE; + + case route_nearconflict: + case route_easy: + /* RT_ROUTED_TUNNEL is treated specially: we don't override + * because we don't want to lose track of the IPSEC_SAs etc. + */ + if (c->spd.routing < RT_ROUTED_TUNNEL) + { + return route_and_eroute(c, &c->spd, NULL); + } + return TRUE; + + case route_farconflict: + return FALSE; + } + + return FALSE; +} + +/* delete any eroute for a connection and unroute it if route isn't shared */ +void +unroute_connection(struct connection *c) +{ + struct spd_route *sr; + enum routing_t cr; + + for (sr = &c->spd; sr; sr = sr->next) + { + cr = sr->routing; + + if (erouted(cr)) + { + /* cannot handle a live one */ + passert(sr->routing != RT_ROUTED_TUNNEL); +#ifdef KLIPS + shunt_eroute(c, sr, RT_UNROUTED, ERO_DELETE, "delete"); +#endif + } + + sr->routing = RT_UNROUTED; /* do now so route_owner won't find us */ + + /* only unroute if no other connection shares it */ + if (routed(cr) && route_owner(c, NULL, NULL, NULL) == NULL) + (void) do_command(c, sr, "unroute"); + } +} + + +#ifdef KLIPS + +static void +set_text_said(char *text_said, const ip_address *dst, ipsec_spi_t spi, int proto) +{ + ip_said said; + + initsaid(dst, spi, proto, &said); + satot(&said, 0, text_said, SATOT_BUF); +} + +/* find an entry in the bare_shunt table. + * Trick: return a pointer to the pointer to the entry; + * this allows the entry to be deleted. + */ +static struct bare_shunt ** +bare_shunt_ptr(const ip_subnet *ours, const ip_subnet *his, int transport_proto) +{ + struct bare_shunt *p, **pp; + + for (pp = &bare_shunts; (p = *pp) != NULL; pp = &p->next) + { + if (samesubnet(ours, &p->ours) + && samesubnet(his, &p->his) + && transport_proto == p->transport_proto + && portof(&ours->addr) == portof(&p->ours.addr) + && portof(&his->addr) == portof(&p->his.addr)) + return pp; + } + return NULL; +} + +/* free a bare_shunt entry, given a pointer to the pointer */ +static void +free_bare_shunt(struct bare_shunt **pp) +{ + if (pp == NULL) + { + DBG(DBG_CONTROL, + DBG_log("delete bare shunt: null pointer") + ) + } + else + { + struct bare_shunt *p = *pp; + + *pp = p->next; + DBG_bare_shunt("delete", p); + pfree(p->why); + pfree(p); + } +} + +void +show_shunt_status(void) +{ + struct bare_shunt *bs; + + for (bs = bare_shunts; bs != NULL; bs = bs->next) + { + /* Print interesting fields. Ignore count and last_active. */ + + int ourport = ntohs(portof(&bs->ours.addr)); + int hisport = ntohs(portof(&bs->his.addr)); + char ourst[SUBNETTOT_BUF]; + char hist[SUBNETTOT_BUF]; + char sat[SATOT_BUF]; + char prio[POLICY_PRIO_BUF]; + + subnettot(&(bs)->ours, 0, ourst, sizeof(ourst)); + subnettot(&(bs)->his, 0, hist, sizeof(hist)); + satot(&(bs)->said, 0, sat, sizeof(sat)); + fmt_policy_prio(bs->policy_prio, prio); + + whack_log(RC_COMMENT, "%s:%d -> %s:%d => %s:%d %s %s" + , ourst, ourport, hist, hisport, sat, bs->transport_proto + , prio, bs->why); + } + if (bare_shunts != NULL) + whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ +} + +/* Setup an IPsec route entry. + * op is one of the ERO_* operators. + */ + +static bool +raw_eroute(const ip_address *this_host + , const ip_subnet *this_client + , const ip_address *that_host + , const ip_subnet *that_client + , ipsec_spi_t spi + , unsigned int proto + , unsigned int satype + , unsigned int transport_proto + , const struct pfkey_proto_info *proto_info + , time_t use_lifetime + , unsigned int op + , const char *opname USED_BY_DEBUG) +{ + char text_said[SATOT_BUF]; + + set_text_said(text_said, that_host, spi, proto); + + DBG(DBG_CONTROL | DBG_KLIPS, + { + int sport = ntohs(portof(&this_client->addr)); + int dport = ntohs(portof(&that_client->addr)); + char mybuf[SUBNETTOT_BUF]; + char peerbuf[SUBNETTOT_BUF]; + + subnettot(this_client, 0, mybuf, sizeof(mybuf)); + subnettot(that_client, 0, peerbuf, sizeof(peerbuf)); + DBG_log("%s eroute %s:%d -> %s:%d => %s:%d" + , opname, mybuf, sport, peerbuf, dport + , text_said, transport_proto); + }); + + return kernel_ops->raw_eroute(this_host, this_client + , that_host, that_client, spi, satype, transport_proto, proto_info + , use_lifetime, op, text_said); +} + +/* test to see if %hold remains */ +bool +has_bare_hold(const ip_address *src, const ip_address *dst, int transport_proto) +{ + ip_subnet this_client, that_client; + struct bare_shunt **bspp; + + passert(addrtypeof(src) == addrtypeof(dst)); + happy(addrtosubnet(src, &this_client)); + happy(addrtosubnet(dst, &that_client)); + bspp = bare_shunt_ptr(&this_client, &that_client, transport_proto); + return bspp != NULL + && (*bspp)->said.proto == SA_INT && (*bspp)->said.spi == htonl(SPI_HOLD); +} + + +/* Replace (or delete) a shunt that is in the bare_shunts table. + * Issues the PF_KEY commands and updates the bare_shunts table. + */ +bool +replace_bare_shunt(const ip_address *src, const ip_address *dst + , policy_prio_t policy_prio + , ipsec_spi_t shunt_spi /* in host order! */ + , bool repl /* if TRUE, replace; if FALSE, delete */ + , unsigned int transport_proto + , const char *why) +{ + ip_subnet this_client, that_client; + ip_subnet this_broad_client, that_broad_client; + const ip_address *null_host = aftoinfo(addrtypeof(src))->any; + + passert(addrtypeof(src) == addrtypeof(dst)); + happy(addrtosubnet(src, &this_client)); + happy(addrtosubnet(dst, &that_client)); + this_broad_client = this_client; + that_broad_client = that_client; + setportof(0, &this_broad_client.addr); + setportof(0, &that_broad_client.addr); + + if (repl) + { + struct bare_shunt **bs_pp = bare_shunt_ptr(&this_broad_client + , &that_broad_client, 0); + + /* is there already a broad host-to-host bare shunt? */ + if (bs_pp == NULL) + { + if (raw_eroute(null_host, &this_broad_client, null_host, &that_broad_client + , htonl(shunt_spi), SA_INT, SADB_X_SATYPE_INT + , 0, null_proto_info + , SHUNT_PATIENCE, ERO_ADD, why)) + { + struct bare_shunt *bs = alloc_thing(struct bare_shunt, "bare shunt"); + + bs->ours = this_broad_client; + bs->his = that_broad_client; + bs->transport_proto = 0; + bs->said.proto = SA_INT; + bs->why = clone_str(why, "bare shunt story"); + bs->policy_prio = policy_prio; + bs->said.spi = htonl(shunt_spi); + bs->said.dst = *null_host; + bs->count = 0; + bs->last_activity = now(); + bs->next = bare_shunts; + bare_shunts = bs; + DBG_bare_shunt("add", bs); + } + } + shunt_spi = SPI_HOLD; + } + + if (raw_eroute(null_host, &this_client, null_host, &that_client + , htonl(shunt_spi), SA_INT, SADB_X_SATYPE_INT + , transport_proto, null_proto_info + , SHUNT_PATIENCE, ERO_DELETE, why)) + { + struct bare_shunt **bs_pp = bare_shunt_ptr(&this_client, &that_client + , transport_proto); + + /* delete bare eroute */ + free_bare_shunt(bs_pp); + return TRUE; + } + else + { + return FALSE; + } +} + +static bool +eroute_connection(struct spd_route *sr +, ipsec_spi_t spi, unsigned int proto, unsigned int satype +, const struct pfkey_proto_info *proto_info +, unsigned int op, const char *opname) +{ + const ip_address *peer = &sr->that.host_addr; + char buf2[256]; + + snprintf(buf2, sizeof(buf2) + , "eroute_connection %s", opname); + + if (proto == SA_INT) + peer = aftoinfo(addrtypeof(peer))->any; + + return raw_eroute(&sr->this.host_addr, &sr->this.client + , peer + , &sr->that.client + , spi, proto, satype + , sr->this.protocol, proto_info, 0, op, buf2); +} + +/* assign a bare hold to a connection */ + +bool +assign_hold(struct connection *c USED_BY_DEBUG + , struct spd_route *sr + , int transport_proto + , const ip_address *src, const ip_address *dst) +{ + /* either the automatically installed %hold eroute is broad enough + * or we try to add a broader one and delete the automatic one. + * Beware: this %hold might be already handled, but still squeak + * through because of a race. + */ + enum routing_t ro = sr->routing /* routing, old */ + , rn = ro; /* routing, new */ + + passert(LHAS(LELEM(CK_PERMANENT) | LELEM(CK_INSTANCE), c->kind)); + /* figure out what routing should become */ + switch (ro) + { + case RT_UNROUTED: + rn = RT_UNROUTED_HOLD; + break; + case RT_ROUTED_PROSPECTIVE: + rn = RT_ROUTED_HOLD; + break; + default: + /* no change: this %hold is old news and should just be deleted */ + break; + } + + /* we need a broad %hold, not the narrow one. + * First we ensure that there is a broad %hold. + * There may already be one (race condition): no need to create one. + * There may already be a %trap: replace it. + * There may not be any broad eroute: add %hold. + * Once the broad %hold is in place, delete the narrow one. + */ + if (rn != ro) + { + if (erouted(ro) + ? !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT + , null_proto_info + , ERO_REPLACE, "replace %trap with broad %hold") + : !eroute_connection(sr, htonl(SPI_HOLD), SA_INT, SADB_X_SATYPE_INT + , null_proto_info + , ERO_ADD, "add broad %hold")) + { + return FALSE; + } + } + if (!replace_bare_shunt(src, dst, BOTTOM_PRIO, SPI_HOLD, FALSE + , transport_proto, "delete narrow %hold")) + { + return FALSE; + } + sr->routing = rn; + return TRUE; +} + +/* install or remove eroute for SA Group */ +static bool +sag_eroute(struct state *st, struct spd_route *sr + , unsigned op, const char *opname) +{ + u_int inner_proto = 0; + u_int inner_satype = 0; + ipsec_spi_t inner_spi = 0; + struct pfkey_proto_info proto_info[4]; + int i; + bool tunnel; + + /* figure out the SPI and protocol (in two forms) + * for the innermost transformation. + */ + + i = sizeof(proto_info) / sizeof(proto_info[0]) - 1; + proto_info[i].proto = 0; + tunnel = FALSE; + + if (st->st_ah.present) + { + inner_spi = st->st_ah.attrs.spi; + inner_proto = SA_AH; + inner_satype = SADB_SATYPE_AH; + + i--; + proto_info[i].proto = IPPROTO_AH; + proto_info[i].encapsulation = st->st_ah.attrs.encapsulation; + tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; + proto_info[i].reqid = sr->reqid; + } + + if (st->st_esp.present) + { + inner_spi = st->st_esp.attrs.spi; + inner_proto = SA_ESP; + inner_satype = SADB_SATYPE_ESP; + + i--; + proto_info[i].proto = IPPROTO_ESP; + proto_info[i].encapsulation = st->st_esp.attrs.encapsulation; + tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; + proto_info[i].reqid = sr->reqid + 1; + } + + if (st->st_ipcomp.present) + { + inner_spi = st->st_ipcomp.attrs.spi; + inner_proto = SA_COMP; + inner_satype = SADB_X_SATYPE_COMP; + + i--; + proto_info[i].proto = IPPROTO_COMP; + proto_info[i].encapsulation = st->st_ipcomp.attrs.encapsulation; + tunnel |= proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; + proto_info[i].reqid = sr->reqid + 2; + } + + if (i == sizeof(proto_info) / sizeof(proto_info[0]) - 1) + { + impossible(); /* no transform at all! */ + } + + if (tunnel) + { + int j; + + inner_spi = st->st_tunnel_out_spi; + inner_proto = SA_IPIP; + inner_satype = SADB_X_SATYPE_IPIP; + + proto_info[i].encapsulation = ENCAPSULATION_MODE_TUNNEL; + for (j = i + 1; proto_info[j].proto; j++) + { + proto_info[j].encapsulation = ENCAPSULATION_MODE_TRANSPORT; + } + } + + return eroute_connection(sr + , inner_spi, inner_proto, inner_satype, proto_info + i + , op, opname); +} + +/* compute a (host-order!) SPI to implement the policy in connection c */ +ipsec_spi_t +shunt_policy_spi(struct connection *c, bool prospective) +{ + /* note: these are in host order :-( */ + static const ipsec_spi_t shunt_spi[] = + { + SPI_TRAP, /* --initiateontraffic */ + SPI_PASS, /* --pass */ + SPI_DROP, /* --drop */ + SPI_REJECT, /* --reject */ + }; + + static const ipsec_spi_t fail_spi[] = + { + 0, /* --none*/ + SPI_PASS, /* --failpass */ + SPI_DROP, /* --faildrop */ + SPI_REJECT, /* --failreject */ + }; + + return prospective + ? shunt_spi[(c->policy & POLICY_SHUNT_MASK) >> POLICY_SHUNT_SHIFT] + : fail_spi[(c->policy & POLICY_FAIL_MASK) >> POLICY_FAIL_SHIFT]; +} + +/* Add/replace/delete a shunt eroute. + * Such an eroute determines the fate of packets without the use + * of any SAs. These are defaults, in effect. + * If a negotiation has not been attempted, use %trap. + * If negotiation has failed, the choice between %trap/%pass/%drop/%reject + * is specified in the policy of connection c. + */ +static bool +shunt_eroute(struct connection *c +, struct spd_route *sr +, enum routing_t rt_kind +, unsigned int op, const char *opname) +{ + /* We are constructing a special SAID for the eroute. + * The destination doesn't seem to matter, but the family does. + * The protocol is SA_INT -- mark this as shunt. + * The satype has no meaning, but is required for PF_KEY header! + * The SPI signifies the kind of shunt. + */ + ipsec_spi_t spi = shunt_policy_spi(c, rt_kind == RT_ROUTED_PROSPECTIVE); + bool ok; + + if (spi == 0) + { + /* we're supposed to end up with no eroute: rejig op and opname */ + switch (op) + { + case ERO_REPLACE: + /* replace with nothing == delete */ + op = ERO_DELETE; + opname = "delete"; + break; + case ERO_ADD: + /* add nothing == do nothing */ + return TRUE; + case ERO_DELETE: + /* delete remains delete */ + break; + default: + bad_case(op); + } + } + if (sr->routing == RT_ROUTED_ECLIPSED && c->kind == CK_TEMPLATE) + { + /* We think that we have an eroute, but we don't. + * Adjust the request and account for eclipses. + */ + passert(eclipsable(sr)); + switch (op) + { + case ERO_REPLACE: + /* really an add */ + op = ERO_ADD; + opname = "replace eclipsed"; + eclipse_count--; + break; + case ERO_DELETE: + /* delete unnecessary: we don't actually have an eroute */ + eclipse_count--; + return TRUE; + case ERO_ADD: + default: + bad_case(op); + } + } + else if (eclipse_count > 0 && op == ERO_DELETE && eclipsable(sr)) + { + /* maybe we are uneclipsing something */ + struct spd_route *esr; + struct connection *ue = eclipsed(c, &esr); + + if (ue != NULL) + { + esr->routing = RT_ROUTED_PROSPECTIVE; + return shunt_eroute(ue, esr + , RT_ROUTED_PROSPECTIVE, ERO_REPLACE, "restoring eclipsed"); + } + } + + ok = TRUE; + if (kernel_ops->inbound_eroute) + { + ok = raw_eroute(&c->spd.that.host_addr, &c->spd.that.client + , &c->spd.this.host_addr, &c->spd.this.client + , htonl(spi), SA_INT, SADB_X_SATYPE_INT + , 0, null_proto_info, 0 + , op | (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT), opname); + } + return eroute_connection(sr, htonl(spi), SA_INT, SADB_X_SATYPE_INT + , null_proto_info, op, opname) && ok; +} + + +/* + * This is only called when s is a likely SAID with trailing protocol i.e. + * it has the form :- + * + * %:p + * @a.b.c.d:p + * + * The task here is to remove the ":p" part so that the rest can be read + * by another routine. + */ +static const char * +read_proto(const char * s, size_t * len, int * transport_proto) +{ + const char * p; + const char * ugh; + unsigned long proto; + size_t l; + + l = *len; + p = memchr(s, ':', l); + if (p == 0) { + *transport_proto = 0; + return 0; + } + ugh = ttoul(p+1, l-((p-s)+1), 10, &proto); + if (ugh != 0) + return ugh; + if (proto > 65535) + return "protocol number is too large, legal range is 0-65535"; + *len = p-s; + *transport_proto = proto; + return 0; +} + + +/* scan /proc/net/ipsec_eroute every once in a while, looking for: + * + * - %hold shunts of which Pluto isn't aware. This situation could + * be caused by lost ACQUIRE messages. When found, they will + * added to orphan_holds. This in turn will lead to Opportunistic + * initiation. + * + * - other kinds of shunts that haven't been used recently. These will be + * deleted. They represent OE failures. + * + * - recording recent uses of tunnel eroutes so that rekeying decisions + * can be made for OE connections. + * + * Here are some sample lines: + * 10 10.3.2.1.0/24 -> 0.0.0.0/0 => %trap + * 259 10.3.2.1.115/32 -> 10.19.75.161/32 => tun0x1002@10.19.75.145 + * 71 10.44.73.97/32 -> 0.0.0.0/0 => %trap + * 4119 10.44.73.97/32 -> 10.114.121.41/32 => %pass + * Newer versions of KLIPS start each line with a 32-bit packet count. + * If available, the count is used to detect whether a %pass shunt is in use. + * + * NOTE: execution time is quadratic in the number of eroutes since the + * searching for each is sequential. If this becomes a problem, faster + * searches could be implemented (hash or radix tree, for example). + */ +void +scan_proc_shunts(void) +{ + static const char procname[] = "/proc/net/ipsec_eroute"; + FILE *f; + time_t nw = now(); + int lino; + struct eroute_info *expired = NULL; + + event_schedule(EVENT_SHUNT_SCAN, SHUNT_SCAN_INTERVAL, NULL); + + DBG(DBG_CONTROL, + DBG_log("scanning for shunt eroutes") + ) + + /* free any leftover entries: they will be refreshed if still current */ + while (orphaned_holds != NULL) + { + struct eroute_info *p = orphaned_holds; + + orphaned_holds = p->next; + pfree(orphaned_holds); + } + + /* decode the /proc file. Don't do anything strenuous to it + * (certainly no PF_KEY stuff) to minimize the chance that it + * might change underfoot. + */ + + f = fopen(procname, "r"); + if (f == NULL) + return; + + /* for each line... */ + for (lino = 1; ; lino++) + { + unsigned char buf[1024]; /* should be big enough */ + chunk_t field[10]; /* 10 is loose upper bound */ + chunk_t *ff = NULL; /* fixed fields (excluding optional count) */ + int fi; + struct eroute_info eri; + char *cp; + err_t context = "" + , ugh = NULL; + + cp = fgets(buf, sizeof(buf), f); + if (cp == NULL) + break; + + /* break out each field + * Note: if there are too many fields, just stop; + * it will be diagnosed a little later. + */ + for (fi = 0; fi < (int)elemsof(field); fi++) + { + static const char sep[] = " \t\n"; /* field-separating whitespace */ + size_t w; + + cp += strspn(cp, sep); /* find start of field */ + w = strcspn(cp, sep); /* find width of field */ + setchunk(field[fi], cp, w); + cp += w; + if (w == 0) + break; + } + + /* This odd do-hickey is to share error reporting code. + * A break will get to that common code. The setting + * of "ugh" and "context" parameterize it. + */ + do { + /* Old entries have no packet count; new ones do. + * check if things are as they should be. + */ + if (fi == 5) + ff = &field[0]; /* old form, with no count */ + else if (fi == 6) + ff = &field[1]; /* new form, with count */ + else + { + ugh = "has wrong number of fields"; + break; + } + + if (ff[1].len != 2 + || strncmp(ff[1].ptr, "->", 2) != 0 + || ff[3].len != 2 + || strncmp(ff[3].ptr, "=>", 2) != 0) + { + ugh = "is missing -> or =>"; + break; + } + + /* actually digest fields of interest */ + + /* packet count */ + + eri.count = 0; + if (ff != field) + { + context = "count field is malformed: "; + ugh = ttoul(field[0].ptr, field[0].len, 10, &eri.count); + if (ugh != NULL) + break; + } + + /* our client */ + + context = "source subnet field malformed: "; + ugh = ttosubnet(ff[0].ptr, ff[0].len, AF_INET, &eri.ours); + if (ugh != NULL) + break; + + /* his client */ + + context = "destination subnet field malformed: "; + ugh = ttosubnet(ff[2].ptr, ff[2].len, AF_INET, &eri.his); + if (ugh != NULL) + break; + + /* SAID */ + + context = "SA ID field malformed: "; + ugh = read_proto(ff[4].ptr, &ff[4].len, &eri.transport_proto); + if (ugh != NULL) + break; + ugh = ttosa(ff[4].ptr, ff[4].len, &eri.said); + } while (FALSE); + + if (ugh != NULL) + { + plog("INTERNAL ERROR: %s line %d %s%s" + , procname, lino, context, ugh); + continue; /* ignore rest of line */ + } + + /* Now we have decoded eroute, let's consider it. + * For shunt eroutes: + * + * %hold: if not known, add to orphaned_holds list for initiation + * because ACQUIRE might have been lost. + * + * %pass, %drop, %reject: determine if idle; if so, blast it away. + * Can occur bare (if DNS provided insufficient information) + * or with a connection (failure context). + * Could even be installed by ipsec manual. + * + * %trap: always welcome. + * + * For other eroutes: find state and record count change + */ + if (eri.said.proto == SA_INT) + { + /* shunt eroute */ + switch (ntohl(eri.said.spi)) + { + case SPI_HOLD: + if (bare_shunt_ptr(&eri.ours, &eri.his, eri.transport_proto) == NULL + && shunt_owner(&eri.ours, &eri.his) == NULL) + { + int ourport = ntohs(portof(&eri.ours.addr)); + int hisport = ntohs(portof(&eri.his.addr)); + char ourst[SUBNETTOT_BUF]; + char hist[SUBNETTOT_BUF]; + char sat[SATOT_BUF]; + + subnettot(&eri.ours, 0, ourst, sizeof(ourst)); + subnettot(&eri.his, 0, hist, sizeof(hist)); + satot(&eri.said, 0, sat, sizeof(sat)); + + DBG(DBG_CONTROL, + DBG_log("add orphaned shunt %s:%d -> %s:%d => %s:%d" + , ourst, ourport, hist, hisport, sat, eri.transport_proto) + ) + eri.next = orphaned_holds; + orphaned_holds = clone_thing(eri, "orphaned %hold"); + } + break; + + case SPI_PASS: + case SPI_DROP: + case SPI_REJECT: + /* nothing sensible to do if we don't have counts */ + if (ff != field) + { + struct bare_shunt **bs_pp + = bare_shunt_ptr(&eri.ours, &eri.his, eri.transport_proto); + + if (bs_pp != NULL) + { + struct bare_shunt *bs = *bs_pp; + + if (eri.count != bs->count) + { + bs->count = eri.count; + bs->last_activity = nw; + } + else if (nw - bs->last_activity > SHUNT_PATIENCE) + { + eri.next = expired; + expired = clone_thing(eri, "expired %pass"); + } + } + } + break; + + case SPI_TRAP: + break; + + default: + bad_case(ntohl(eri.said.spi)); + } + } + else + { + /* regular (non-shunt) eroute */ + state_eroute_usage(&eri.ours, &eri.his, eri.count, nw); + } + } /* for each line */ + fclose(f); + + /* Now that we've finished processing the /proc file, + * it is safe to delete the expired %pass shunts. + */ + while (expired != NULL) + { + struct eroute_info *p = expired; + ip_address src, dst; + + networkof(&p->ours, &src); + networkof(&p->his, &dst); + (void) replace_bare_shunt(&src, &dst + , BOTTOM_PRIO /* not used because we are deleting. This value is a filler */ + , SPI_PASS /* not used because we are deleting. This value is a filler */ + , FALSE, p->transport_proto, "delete expired bare shunts"); + expired = p->next; + pfree(p); + } +} + +static bool +del_spi(ipsec_spi_t spi, int proto +, const ip_address *src, const ip_address *dest) +{ + char text_said[SATOT_BUF]; + struct kernel_sa sa; + + set_text_said(text_said, dest, spi, proto); + + DBG(DBG_KLIPS, DBG_log("delete %s", text_said)); + + memset(&sa, 0, sizeof(sa)); + sa.spi = spi; + sa.proto = proto; + sa.src = src; + sa.dst = dest; + sa.text_said = text_said; + + return kernel_ops->del_sa(&sa); +} + +/* Setup a pair of SAs. Code taken from setsa.c and spigrp.c, in + * ipsec-0.5. + */ + +static bool +setup_half_ipsec_sa(struct state *st, bool inbound) +{ + /* Build an inbound or outbound SA */ + + struct connection *c = st->st_connection; + ip_subnet src, dst; + ip_subnet src_client, dst_client; + ipsec_spi_t inner_spi = 0; + u_int proto = 0; + u_int satype = SADB_SATYPE_UNSPEC; + bool replace; + + /* SPIs, saved for spigrouping or undoing, if necessary */ + struct kernel_sa + said[EM_MAXRELSPIS], + *said_next = said; + + char text_said[SATOT_BUF]; + int encapsulation; + + replace = inbound && (kernel_ops->get_spi != NULL); + + src.maskbits = 0; + dst.maskbits = 0; + + if (inbound) + { + src.addr = c->spd.that.host_addr; + dst.addr = c->spd.this.host_addr; + src_client = c->spd.that.client; + dst_client = c->spd.this.client; + } + else + { + src.addr = c->spd.this.host_addr, + dst.addr = c->spd.that.host_addr; + src_client = c->spd.this.client; + dst_client = c->spd.that.client; + } + + encapsulation = ENCAPSULATION_MODE_TRANSPORT; + if (st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL + || st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL + || st->st_ipcomp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) + { + encapsulation = ENCAPSULATION_MODE_TUNNEL; + } + + memset(said, 0, sizeof(said)); + + /* If we are tunnelling, set up IP in IP pseudo SA */ + + if (kernel_ops->inbound_eroute) + { + inner_spi = 256; + proto = SA_IPIP; + satype = SADB_SATYPE_UNSPEC; + } + else if (encapsulation == ENCAPSULATION_MODE_TUNNEL) + { + /* XXX hack alert -- we SHOULD NOT HAVE TO HAVE A DIFFERENT SPI + * XXX FOR IP-in-IP ENCAPSULATION! + */ + + ipsec_spi_t ipip_spi; + + /* Allocate an SPI for the tunnel. + * Since our peer will never see this, + * and it comes from its own number space, + * it is purely a local implementation wart. + */ + { + static ipsec_spi_t last_tunnel_spi = IPSEC_DOI_SPI_OUR_MIN; + + ipip_spi = htonl(++last_tunnel_spi); + if (inbound) + st->st_tunnel_in_spi = ipip_spi; + else + st->st_tunnel_out_spi = ipip_spi; + } + + set_text_said(text_said + , &c->spd.that.host_addr, ipip_spi, SA_IPIP); + + said_next->src = &src.addr; + said_next->dst = &dst.addr; + said_next->src_client = &src_client; + said_next->dst_client = &dst_client; + said_next->spi = ipip_spi; + said_next->satype = SADB_X_SATYPE_IPIP; + said_next->text_said = text_said; + + if (!kernel_ops->add_sa(said_next, replace)) + goto fail; + + said_next++; + + inner_spi = ipip_spi; + proto = SA_IPIP; + satype = SADB_X_SATYPE_IPIP; + } + + /* set up IPCOMP SA, if any */ + + if (st->st_ipcomp.present) + { + ipsec_spi_t ipcomp_spi = inbound? st->st_ipcomp.our_spi : st->st_ipcomp.attrs.spi; + unsigned compalg; + + switch (st->st_ipcomp.attrs.transid) + { + case IPCOMP_DEFLATE: + compalg = SADB_X_CALG_DEFLATE; + break; + + default: + loglog(RC_LOG_SERIOUS, "IPCOMP transform %s not implemented" + , enum_name(&ipcomp_transformid_names, st->st_ipcomp.attrs.transid)); + goto fail; + } + + set_text_said(text_said, &dst.addr, ipcomp_spi, SA_COMP); + + said_next->src = &src.addr; + said_next->dst = &dst.addr; + said_next->src_client = &src_client; + said_next->dst_client = &dst_client; + said_next->spi = ipcomp_spi; + said_next->satype = SADB_X_SATYPE_COMP; + said_next->compalg = compalg; + said_next->encapsulation = encapsulation; + said_next->reqid = c->spd.reqid + 2; + said_next->text_said = text_said; + + if (!kernel_ops->add_sa(said_next, replace)) + goto fail; + + said_next++; + + encapsulation = ENCAPSULATION_MODE_TRANSPORT; + } + + /* set up ESP SA, if any */ + + if (st->st_esp.present) + { + ipsec_spi_t esp_spi = inbound? st->st_esp.our_spi : st->st_esp.attrs.spi; + u_char *esp_dst_keymat = inbound? st->st_esp.our_keymat : st->st_esp.peer_keymat; + const struct esp_info *ei; + u_int16_t key_len; + + static const struct esp_info esp_info[] = { + { ESP_NULL, AUTH_ALGORITHM_HMAC_MD5, + 0, HMAC_MD5_KEY_LEN, + SADB_EALG_NULL, SADB_AALG_MD5_HMAC }, + { ESP_NULL, AUTH_ALGORITHM_HMAC_SHA1, + 0, HMAC_SHA1_KEY_LEN, + SADB_EALG_NULL, SADB_AALG_SHA1_HMAC }, + + { ESP_DES, AUTH_ALGORITHM_NONE, + DES_CBC_BLOCK_SIZE, 0, + SADB_EALG_DES_CBC, SADB_AALG_NONE }, + { ESP_DES, AUTH_ALGORITHM_HMAC_MD5, + DES_CBC_BLOCK_SIZE, HMAC_MD5_KEY_LEN, + SADB_EALG_DES_CBC, SADB_AALG_MD5_HMAC }, + { ESP_DES, AUTH_ALGORITHM_HMAC_SHA1, + DES_CBC_BLOCK_SIZE, + HMAC_SHA1_KEY_LEN, SADB_EALG_DES_CBC, SADB_AALG_SHA1_HMAC }, + + { ESP_3DES, AUTH_ALGORITHM_NONE, + DES_CBC_BLOCK_SIZE * 3, 0, + SADB_EALG_3DES_CBC, SADB_AALG_NONE }, + { ESP_3DES, AUTH_ALGORITHM_HMAC_MD5, + DES_CBC_BLOCK_SIZE * 3, HMAC_MD5_KEY_LEN, + SADB_EALG_3DES_CBC, SADB_AALG_MD5_HMAC }, + { ESP_3DES, AUTH_ALGORITHM_HMAC_SHA1, + DES_CBC_BLOCK_SIZE * 3, HMAC_SHA1_KEY_LEN, + SADB_EALG_3DES_CBC, SADB_AALG_SHA1_HMAC }, + }; + + u_int8_t natt_type = 0; + u_int16_t natt_sport = 0; + u_int16_t natt_dport = 0; + ip_address natt_oa; + + if (st->nat_traversal & NAT_T_DETECTED) + { + natt_type = (st->nat_traversal & NAT_T_WITH_PORT_FLOATING) ? + ESPINUDP_WITH_NON_ESP : ESPINUDP_WITH_NON_IKE; + natt_sport = inbound? c->spd.that.host_port : c->spd.this.host_port; + natt_dport = inbound? c->spd.this.host_port : c->spd.that.host_port; + natt_oa = st->nat_oa; + } + + for (ei = esp_info; ; ei++) + { + if (ei == &esp_info[elemsof(esp_info)]) + { + /* Check for additional kernel alg */ +#ifndef NO_KERNEL_ALG + if ((ei=kernel_alg_esp_info(st->st_esp.attrs.transid, + st->st_esp.attrs.auth))!=NULL) { + break; + } +#endif + + /* note: enum_show may use a static buffer, so two + * calls in one printf would be a mistake. + * enum_name does the same job, without a static buffer, + * assuming the name will be found. + */ + loglog(RC_LOG_SERIOUS, "ESP transform %s / auth %s not implemented yet" + , enum_name(&esp_transformid_names, st->st_esp.attrs.transid) + , enum_name(&auth_alg_names, st->st_esp.attrs.auth)); + goto fail; + } + + if (st->st_esp.attrs.transid == ei->transid + && st->st_esp.attrs.auth == ei->auth) + break; + } + + key_len = st->st_esp.attrs.key_len/8; + if (key_len) + { + /* XXX: must change to check valid _range_ key_len */ + if (key_len > ei->enckeylen) + { + loglog(RC_LOG_SERIOUS, "ESP transform %s passed key_len=%d > %d", + enum_name(&esp_transformid_names, st->st_esp.attrs.transid), + (int)key_len, (int)ei->enckeylen); + goto fail; + } + } + else + { + key_len = ei->enckeylen; + } + /* Grrrrr.... f*cking 7 bits jurassic algos */ + + /* 168 bits in kernel, need 192 bits for keymat_len */ + if (ei->transid == ESP_3DES && key_len == 21) + key_len = 24; + + /* 56 bits in kernel, need 64 bits for keymat_len */ + if (ei->transid == ESP_DES && key_len == 7) + key_len = 8; + + /* divide up keying material */ + /* passert(st->st_esp.keymat_len == ei->enckeylen + ei->authkeylen); */ + DBG(DBG_KLIPS|DBG_CONTROL|DBG_PARSING, + if(st->st_esp.keymat_len != key_len + ei->authkeylen) + DBG_log("keymat_len=%d key_len=%d authkeylen=%d", + st->st_esp.keymat_len, (int)key_len, (int)ei->authkeylen); + ) + passert(st->st_esp.keymat_len == key_len + ei->authkeylen); + + set_text_said(text_said, &dst.addr, esp_spi, SA_ESP); + + said_next->src = &src.addr; + said_next->dst = &dst.addr; + said_next->src_client = &src_client; + said_next->dst_client = &dst_client; + said_next->spi = esp_spi; + said_next->satype = SADB_SATYPE_ESP; + said_next->replay_window = (kernel_ops->type == KERNEL_TYPE_KLIPS) ? REPLAY_WINDOW : REPLAY_WINDOW_XFRM; + said_next->authalg = ei->authalg; + said_next->authkeylen = ei->authkeylen; + /* said_next->authkey = esp_dst_keymat + ei->enckeylen; */ + said_next->authkey = esp_dst_keymat + key_len; + said_next->encalg = ei->encryptalg; + /* said_next->enckeylen = ei->enckeylen; */ + said_next->enckeylen = key_len; + said_next->enckey = esp_dst_keymat; + said_next->encapsulation = encapsulation; + said_next->reqid = c->spd.reqid + 1; + said_next->natt_sport = natt_sport; + said_next->natt_dport = natt_dport; + said_next->transid = st->st_esp.attrs.transid; + said_next->natt_type = natt_type; + said_next->natt_oa = &natt_oa; + said_next->text_said = text_said; + + if (!kernel_ops->add_sa(said_next, replace)) + goto fail; + + said_next++; + + encapsulation = ENCAPSULATION_MODE_TRANSPORT; + } + + /* set up AH SA, if any */ + + if (st->st_ah.present) + { + ipsec_spi_t ah_spi = inbound? st->st_ah.our_spi : st->st_ah.attrs.spi; + u_char *ah_dst_keymat = inbound? st->st_ah.our_keymat : st->st_ah.peer_keymat; + + unsigned char authalg; + + switch (st->st_ah.attrs.auth) + { + case AUTH_ALGORITHM_HMAC_MD5: + authalg = SADB_AALG_MD5_HMAC; + break; + + case AUTH_ALGORITHM_HMAC_SHA1: + authalg = SADB_AALG_SHA1_HMAC; + break; + + default: + loglog(RC_LOG_SERIOUS, "%s not implemented yet" + , enum_show(&auth_alg_names, st->st_ah.attrs.auth)); + goto fail; + } + + set_text_said(text_said, &dst.addr, ah_spi, SA_AH); + + said_next->src = &src.addr; + said_next->dst = &dst.addr; + said_next->src_client = &src_client; + said_next->dst_client = &dst_client; + said_next->spi = ah_spi; + said_next->satype = SADB_SATYPE_AH; + said_next->replay_window = (kernel_ops->type == KERNEL_TYPE_KLIPS) ? REPLAY_WINDOW : REPLAY_WINDOW_XFRM; + said_next->authalg = authalg; + said_next->authkeylen = st->st_ah.keymat_len; + said_next->authkey = ah_dst_keymat; + said_next->encapsulation = encapsulation; + said_next->reqid = c->spd.reqid; + said_next->text_said = text_said; + + if (!kernel_ops->add_sa(said_next, replace)) + goto fail; + + said_next++; + + encapsulation = ENCAPSULATION_MODE_TRANSPORT; + } + + if (st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL + || st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL + || st->st_ipcomp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL) + { + encapsulation = ENCAPSULATION_MODE_TUNNEL; + } + + if (kernel_ops->inbound_eroute ? c->spd.eroute_owner == SOS_NOBODY + : encapsulation == ENCAPSULATION_MODE_TUNNEL) + { + /* If inbound, and policy does not specifie DISABLEARRIVALCHECK, + * tell KLIPS to enforce the IP addresses appropriate for this tunnel. + * Note reversed ends. + * Not much to be done on failure. + */ + if (inbound && (c->policy & POLICY_DISABLEARRIVALCHECK) == 0) + { + struct pfkey_proto_info proto_info[4]; + int i = 0; + + if (st->st_ipcomp.present) + { + proto_info[i].proto = IPPROTO_COMP; + proto_info[i].encapsulation = st->st_ipcomp.attrs.encapsulation; + proto_info[i].reqid = c->spd.reqid + 2; + i++; + } + + if (st->st_esp.present) + { + proto_info[i].proto = IPPROTO_ESP; + proto_info[i].encapsulation = st->st_esp.attrs.encapsulation; + proto_info[i].reqid = c->spd.reqid + 1; + i++; + } + + if (st->st_ah.present) + { + proto_info[i].proto = IPPROTO_AH; + proto_info[i].encapsulation = st->st_ah.attrs.encapsulation; + proto_info[i].reqid = c->spd.reqid; + i++; + } + + proto_info[i].proto = 0; + + if (kernel_ops->inbound_eroute + && encapsulation == ENCAPSULATION_MODE_TUNNEL) + { + proto_info[0].encapsulation = ENCAPSULATION_MODE_TUNNEL; + for (i = 1; proto_info[i].proto; i++) + { + proto_info[i].encapsulation = ENCAPSULATION_MODE_TRANSPORT; + } + } + + /* MCR - should be passed a spd_eroute structure here */ + (void) raw_eroute(&c->spd.that.host_addr, &c->spd.that.client + , &c->spd.this.host_addr, &c->spd.this.client + , inner_spi, proto, satype, c->spd.this.protocol + , proto_info, 0 + , ERO_ADD_INBOUND, "add inbound"); + } + } + + /* If there are multiple SPIs, group them. */ + + if (kernel_ops->grp_sa && said_next > &said[1]) + { + struct kernel_sa *s; + + /* group SAs, two at a time, inner to outer (backwards in said[]) + * The grouping is by pairs. So if said[] contains ah esp ipip, + * the grouping would be ipip:esp, esp:ah. + */ + for (s = said; s < said_next-1; s++) + { + char + text_said0[SATOT_BUF], + text_said1[SATOT_BUF]; + + /* group s[1] and s[0], in that order */ + + set_text_said(text_said0, s[0].dst, s[0].spi, s[0].proto); + set_text_said(text_said1, s[1].dst, s[1].spi, s[1].proto); + + DBG(DBG_KLIPS, DBG_log("grouping %s and %s", text_said1, text_said0)); + + s[0].text_said = text_said0; + s[1].text_said = text_said1; + + if (!kernel_ops->grp_sa(s + 1, s)) + goto fail; + } + /* could update said, but it will not be used */ + } + + return TRUE; + +fail: + { + /* undo the done SPIs */ + while (said_next-- != said) + (void) del_spi(said_next->spi, said_next->proto + , &src.addr, said_next->dst); + return FALSE; + } +} + +/* teardown_ipsec_sa is a canibalized version of setup_ipsec_sa */ + +static bool +teardown_half_ipsec_sa(struct state *st, bool inbound) +{ + /* We need to delete AH, ESP, and IP in IP SPIs. + * But if there is more than one, they have been grouped + * so deleting any one will do. So we just delete the + * first one found. It may or may not be the only one. + */ + struct connection *c = st->st_connection; + struct { + unsigned proto; + struct ipsec_proto_info *info; + } protos[4]; + int i; + bool result; + + i = 0; + if (kernel_ops->inbound_eroute && inbound + && c->spd.eroute_owner == SOS_NOBODY) + { + (void) raw_eroute(&c->spd.that.host_addr, &c->spd.that.client + , &c->spd.this.host_addr, &c->spd.this.client + , 256, IPSEC_PROTO_ANY, SADB_SATYPE_UNSPEC, c->spd.this.protocol + , null_proto_info, 0 + , ERO_DEL_INBOUND, "delete inbound"); + } + + if (!kernel_ops->grp_sa) + { + if (st->st_ah.present) + { + protos[i].info = &st->st_ah; + protos[i].proto = SA_AH; + i++; + } + + if (st->st_esp.present) + { + protos[i].info = &st->st_esp; + protos[i].proto = SA_ESP; + i++; + } + + if (st->st_ipcomp.present) + { + protos[i].info = &st->st_ipcomp; + protos[i].proto = SA_COMP; + i++; + } + } + else if (st->st_ah.present) + { + protos[i].info = &st->st_ah; + protos[i].proto = SA_AH; + i++; + } + else if (st->st_esp.present) + { + protos[i].info = &st->st_esp; + protos[i].proto = SA_ESP; + i++; + } + else + { + impossible(); /* neither AH nor ESP in outbound SA bundle! */ + } + protos[i].proto = 0; + + result = TRUE; + for (i = 0; protos[i].proto; i++) + { + unsigned proto = protos[i].proto; + ipsec_spi_t spi; + const ip_address *src, *dst; + + if (inbound) + { + spi = protos[i].info->our_spi; + src = &c->spd.that.host_addr; + dst = &c->spd.this.host_addr; + } + else + { + spi = protos[i].info->attrs.spi; + src = &c->spd.this.host_addr; + dst = &c->spd.that.host_addr; + } + + result &= del_spi(spi, proto, src, dst); + } + return result; +} + +/* + * get information about a given sa + */ +bool +get_sa_info(struct state *st, bool inbound, u_int *bytes, time_t *use_time) +{ + char text_said[SATOT_BUF]; + struct kernel_sa sa; + struct connection *c = st->st_connection; + + *use_time = UNDEFINED_TIME; + + if (kernel_ops->get_sa == NULL || !st->st_esp.present) + return FALSE; + + memset(&sa, 0, sizeof(sa)); + sa.proto = SA_ESP; + + if (inbound) + { + sa.src = &c->spd.that.host_addr; + sa.dst = &c->spd.this.host_addr; + sa.spi = st->st_esp.our_spi; + } + else + { + sa.src = &c->spd.this.host_addr; + sa.dst = &c->spd.that.host_addr; + sa.spi = st->st_esp.attrs.spi; + } + set_text_said(text_said, sa.dst, sa.spi, sa.proto); + + sa.text_said = text_said; + + DBG(DBG_KLIPS, + DBG_log("get %s", text_said) + ) + if (!kernel_ops->get_sa(&sa, bytes)) + return FALSE; + DBG(DBG_KLIPS, + DBG_log(" current: %d bytes", *bytes) + ) + + if (st->st_serialno == c->spd.eroute_owner) + { + DBG(DBG_KLIPS, + DBG_log("get %sbound policy with reqid %u" + , inbound? "in":"out", (u_int)c->spd.reqid + 1) + ) + sa.transport_proto = c->spd.this.protocol; + sa.encapsulation = st->st_esp.attrs.encapsulation; + + if (inbound) + { + sa.src_client = &c->spd.that.client; + sa.dst_client = &c->spd.this.client; + } + else + { + sa.src_client = &c->spd.this.client; + sa.dst_client = &c->spd.that.client; + } + if (!kernel_ops->get_policy(&sa, inbound, use_time)) + return FALSE; + DBG(DBG_KLIPS, + DBG_log(" use_time: %s", timetoa(use_time, FALSE)) + ) + } + return TRUE; +} + +const struct kernel_ops *kernel_ops; + +#endif /* KLIPS */ + +void +init_kernel(void) +{ +#ifdef KLIPS + + if (no_klips) + { + kernel_ops = &noklips_kernel_ops; + return; + } + + init_pfkey(); + + kernel_ops = &klips_kernel_ops; + +#if defined(linux) && defined(KERNEL26_SUPPORT) + { + bool linux_ipsec = 0; + struct stat buf; + + linux_ipsec = (stat("/proc/net/pfkey", &buf) == 0); + if (linux_ipsec) + { + plog("Using Linux 2.6 IPsec interface code"); + kernel_ops = &linux_kernel_ops; + } + else + { + plog("Using KLIPS IPsec interface code"); + } + } +#endif + + if (kernel_ops->init) + { + kernel_ops->init(); + } + + /* register SA types that we can negotiate */ + can_do_IPcomp = FALSE; /* until we get a response from KLIPS */ + kernel_ops->pfkey_register(); + + if (!kernel_ops->policy_lifetime) + { + event_schedule(EVENT_SHUNT_SCAN, SHUNT_SCAN_INTERVAL, NULL); + } +#endif +} + +/* Note: install_inbound_ipsec_sa is only used by the Responder. + * The Responder will subsequently use install_ipsec_sa for the outbound. + * The Initiator uses install_ipsec_sa to install both at once. + */ +bool +install_inbound_ipsec_sa(struct state *st) +{ + struct connection *const c = st->st_connection; + + /* If our peer has a fixed-address client, check if we already + * have a route for that client that conflicts. We will take this + * as proof that that route and the connections using it are + * obsolete and should be eliminated. Interestingly, this is + * the only case in which we can tell that a connection is obsolete. + */ + passert(c->kind == CK_PERMANENT || c->kind == CK_INSTANCE); + if (c->spd.that.has_client) + { + for (;;) + { + struct spd_route *esr; + struct connection *o = route_owner(c, &esr, NULL, NULL); + + if (o == NULL) + break; /* nobody has a route */ + + /* note: we ignore the client addresses at this end */ + if (sameaddr(&o->spd.that.host_addr, &c->spd.that.host_addr) + && o->interface == c->interface) + break; /* existing route is compatible */ + + if (o->kind == CK_TEMPLATE && streq(o->name, c->name)) + break; /* ??? is this good enough?? */ + + loglog(RC_LOG_SERIOUS, "route to peer's client conflicts with \"%s\" %s; releasing old connection to free the route" + , o->name, ip_str(&o->spd.that.host_addr)); + release_connection(o, FALSE); + } + } + + DBG(DBG_CONTROL, DBG_log("install_inbound_ipsec_sa() checking if we can route")); + /* check that we will be able to route and eroute */ + switch (could_route(c)) + { + case route_easy: + case route_nearconflict: + break; + + default: + return FALSE; + } + +#ifdef KLIPS + /* (attempt to) actually set up the SAs */ + return setup_half_ipsec_sa(st, TRUE); +#else /* !KLIPS */ + DBG(DBG_CONTROL, DBG_log("install_inbound_ipsec_sa()")); + return TRUE; +#endif /* !KLIPS */ +} + +/* Install a route and then a prospective shunt eroute or an SA group eroute. + * Assumption: could_route gave a go-ahead. + * Any SA Group must have already been created. + * On failure, steps will be unwound. + */ +bool +route_and_eroute(struct connection *c USED_BY_KLIPS + , struct spd_route *sr USED_BY_KLIPS + , struct state *st USED_BY_KLIPS) +{ +#ifdef KLIPS + struct spd_route *esr; + struct spd_route *rosr; + struct connection *ero /* who, if anyone, owns our eroute? */ + , *ro = route_owner(c, &rosr, &ero, &esr); + bool eroute_installed = FALSE + , firewall_notified = FALSE + , route_installed = FALSE; + + struct connection *ero_top; + struct bare_shunt **bspp; + + DBG(DBG_CONTROLMORE, + DBG_log("route_and_eroute with c: %s (next: %s) ero:%s esr:{%p} ro:%s rosr:{%p} and state: %lu" + , c->name + , (c->policy_next ? c->policy_next->name : "none") + , ero ? ero->name : "null" + , esr + , ro ? ro->name : "null" + , rosr + , st ? st->st_serialno : 0)); + + /* look along the chain of policies for one with the same name */ + ero_top = ero; + +#if 0 + /* XXX - mcr this made sense before, and likely will make sense + * again, so I'l leaving this to remind me what is up */ + if (ero!= NULL && ero->routing == RT_UNROUTED_KEYED) + ero = NULL; + + for (ero2 = ero; ero2 != NULL; ero2 = ero->policy_next) + if ((ero2->kind == CK_TEMPLATE || ero2->kind==CK_SECONDARY) + && streq(ero2->name, c->name)) + break; +#endif + + bspp = (ero == NULL) + ? bare_shunt_ptr(&sr->this.client, &sr->that.client, sr->this.protocol) + : NULL; + + /* install the eroute */ + + passert(bspp == NULL || ero == NULL); /* only one non-NULL */ + + if (bspp != NULL || ero != NULL) + { + /* We're replacing an eroute */ + + /* if no state provided, then install a shunt for later */ + if (st == NULL) + eroute_installed = shunt_eroute(c, sr, RT_ROUTED_PROSPECTIVE + , ERO_REPLACE, "replace"); + else + eroute_installed = sag_eroute(st, sr, ERO_REPLACE, "replace"); + +#if 0 + /* XXX - MCR. I previously felt that this was a bogus check */ + if (ero != NULL && ero != c && esr != sr) + { + /* By elimination, we must be eclipsing ero. Check. */ + passert(ero->kind == CK_TEMPLATE && streq(ero->name, c->name)); + passert(LHAS(LELEM(RT_ROUTED_PROSPECTIVE) | LELEM(RT_ROUTED_ECLIPSED) + , esr->routing)); + passert(samesubnet(&esr->this.client, &sr->this.client) + && samesubnet(&esr->that.client, &sr->that.client)); + } +#endif + /* remember to free bspp iff we make it out of here alive */ + } + else + { + /* we're adding an eroute */ + + /* if no state provided, then install a shunt for later */ + if (st == NULL) + eroute_installed = shunt_eroute(c, sr, RT_ROUTED_PROSPECTIVE + , ERO_ADD, "add"); + else + eroute_installed = sag_eroute(st, sr, ERO_ADD, "add"); + } + + /* notify the firewall of a new tunnel */ + + if (eroute_installed) + { + /* do we have to notify the firewall? Yes, if we are installing + * a tunnel eroute and the firewall wasn't notified + * for a previous tunnel with the same clients. Any Previous + * tunnel would have to be for our connection, so the actual + * test is simple. + */ + firewall_notified = st == NULL /* not a tunnel eroute */ + || sr->eroute_owner != SOS_NOBODY /* already notified */ + || do_command(c, sr, "up"); /* go ahead and notify */ + } + + /* install the route */ + + DBG(DBG_CONTROL, + DBG_log("route_and_eroute: firewall_notified: %s" + , firewall_notified ? "true" : "false")); + if (!firewall_notified) + { + /* we're in trouble -- don't do routing */ + } + else if (ro == NULL) + { + /* a new route: no deletion required, but preparation is */ + (void) do_command(c, sr, "prepare"); /* just in case; ignore failure */ + route_installed = do_command(c, sr, "route"); + } + else if (routed(sr->routing) + || routes_agree(ro, c)) + { + route_installed = TRUE; /* nothing to be done */ + } + else + { + /* Some other connection must own the route + * and the route must disagree. But since could_route + * must have allowed our stealing it, we'll do so. + * + * A feature of LINUX allows us to install the new route + * before deleting the old if the nexthops differ. + * This reduces the "window of vulnerability" when packets + * might flow in the clear. + */ + if (sameaddr(&sr->this.host_nexthop, &esr->this.host_nexthop)) + { + (void) do_command(ro, sr, "unroute"); + route_installed = do_command(c, sr, "route"); + } + else + { + route_installed = do_command(c, sr, "route"); + (void) do_command(ro, sr, "unroute"); + } + + /* record unrouting */ + if (route_installed) + { + do { + passert(!erouted(rosr->routing)); + rosr->routing = RT_UNROUTED; + + /* no need to keep old value */ + ro = route_owner(c, &rosr, NULL, NULL); + } while (ro != NULL); + } + } + + /* all done -- clean up */ + if (route_installed) + { + /* Success! */ + + if (bspp != NULL) + { + free_bare_shunt(bspp); + } + else if (ero != NULL && ero != c) + { + /* check if ero is an ancestor of c. */ + struct connection *ero2; + + for (ero2 = c; ero2 != NULL && ero2 != c; ero2 = ero2->policy_next) + ; + + if (ero2 == NULL) + { + /* By elimination, we must be eclipsing ero. Checked above. */ + if (ero->spd.routing != RT_ROUTED_ECLIPSED) + { + ero->spd.routing = RT_ROUTED_ECLIPSED; + eclipse_count++; + } + } + } + + if (st == NULL) + { + passert(sr->eroute_owner == SOS_NOBODY); + sr->routing = RT_ROUTED_PROSPECTIVE; + } + else + { + char cib[CONN_INST_BUF]; + sr->routing = RT_ROUTED_TUNNEL; + + DBG(DBG_CONTROL, + DBG_log("route_and_eroute: instance \"%s\"%s, setting eroute_owner {spd=%p,sr=%p} to #%ld (was #%ld) (newest_ipsec_sa=#%ld)" + , st->st_connection->name + , (fmt_conn_instance(st->st_connection, cib), cib) + , &st->st_connection->spd, sr + , st->st_serialno + , sr->eroute_owner + , st->st_connection->newest_ipsec_sa)); + sr->eroute_owner = st->st_serialno; + } + + return TRUE; + } + else + { + /* Failure! Unwind our work. */ + if (firewall_notified && sr->eroute_owner == SOS_NOBODY) + (void) do_command(c, sr, "down"); + + if (eroute_installed) + { + /* Restore original eroute, if we can. + * Since there is nothing much to be done if the restoration + * fails, ignore success or failure. + */ + if (bspp != NULL) + { + /* Restore old bare_shunt. + * I don't think that this case is very likely. + * Normally a bare shunt would have been assigned + * to a connection before we've gotten this far. + */ + struct bare_shunt *bs = *bspp; + + (void) raw_eroute(&bs->said.dst /* should be useless */ + , &bs->ours + , &bs->said.dst /* should be useless */ + , &bs->his + , bs->said.spi /* network order */ + , SA_INT + , SADB_X_SATYPE_INT + , 0 + , null_proto_info + , SHUNT_PATIENCE + , ERO_REPLACE, "restore"); + } + else if (ero != NULL) + { + /* restore ero's former glory */ + if (esr->eroute_owner == SOS_NOBODY) + { + /* note: normal or eclipse case */ + (void) shunt_eroute(ero, esr + , esr->routing, ERO_REPLACE, "restore"); + } + else + { + /* Try to find state that owned eroute. + * Don't do anything if it cannot be found. + * This case isn't likely since we don't run + * the updown script when replacing a SA group + * with its successor (for the same conn). + */ + struct state *ost = state_with_serialno(esr->eroute_owner); + + if (ost != NULL) + (void) sag_eroute(ost, esr, ERO_REPLACE, "restore"); + } + } + else + { + /* there was no previous eroute: delete whatever we installed */ + if (st == NULL) + (void) shunt_eroute(c, sr + , sr->routing, ERO_DELETE, "delete"); + else + (void) sag_eroute(st, sr + , ERO_DELETE, "delete"); + } + } + + return FALSE; + } +#else /* !KLIPS */ + return TRUE; +#endif /* !KLIPS */ +} + +bool +install_ipsec_sa(struct state *st, bool inbound_also USED_BY_KLIPS) +{ +#ifdef KLIPS + struct spd_route *sr; + + DBG(DBG_CONTROL, DBG_log("install_ipsec_sa() for #%ld: %s" + , st->st_serialno + , inbound_also? + "inbound and outbound" : "outbound only")); + + switch (could_route(st->st_connection)) + { + case route_easy: + case route_nearconflict: + break; + + default: + return FALSE; + } + + /* (attempt to) actually set up the SA group */ + if ((inbound_also && !setup_half_ipsec_sa(st, TRUE)) + || !setup_half_ipsec_sa(st, FALSE)) + return FALSE; + + for (sr = &st->st_connection->spd; sr != NULL; sr = sr->next) + { + DBG(DBG_CONTROL, DBG_log("sr for #%ld: %s" + , st->st_serialno + , enum_name(&routing_story, sr->routing))); + + /* + * if the eroute owner is not us, then make it us. + * See test co-terminal-02, pluto-rekey-01, pluto-unit-02/oppo-twice + */ + pexpect(sr->eroute_owner == SOS_NOBODY + || sr->routing >= RT_ROUTED_TUNNEL); + + if (sr->eroute_owner != st->st_serialno + && sr->routing != RT_UNROUTED_KEYED) + { + if (!route_and_eroute(st->st_connection, sr, st)) + { + delete_ipsec_sa(st, FALSE); + /* XXX go and unroute any SRs that were successfully + * routed already. + */ + return FALSE; + } + } + } +#else /* !KLIPS */ + DBG(DBG_CONTROL, DBG_log("install_ipsec_sa() %s" + , inbound_also? "inbound and oubound" : "outbound only")); + + switch (could_route(st->st_connection)) + { + case route_easy: + case route_nearconflict: + break; + + default: + return FALSE; + } + + +#endif /* !KLIPS */ + + return TRUE; +} + +/* delete an IPSEC SA. + * we may not succeed, but we bull ahead anyway because + * we cannot do anything better by recognizing failure + */ +void +delete_ipsec_sa(struct state *st USED_BY_KLIPS, bool inbound_only USED_BY_KLIPS) +{ +#ifdef KLIPS + if (!inbound_only) + { + /* If the state is the eroute owner, we must adjust + * the routing for the connection. + */ + struct connection *c = st->st_connection; + struct spd_route *sr; + + passert(st->st_connection); + + for (sr = &c->spd; sr; sr = sr->next) + { + if (sr->eroute_owner == st->st_serialno + && sr->routing == RT_ROUTED_TUNNEL) + { + sr->eroute_owner = SOS_NOBODY; + + /* Routing should become RT_ROUTED_FAILURE, + * but if POLICY_FAIL_NONE, then we just go + * right back to RT_ROUTED_PROSPECTIVE as if no + * failure happened. + */ + sr->routing = (c->policy & POLICY_FAIL_MASK) == POLICY_FAIL_NONE + ? RT_ROUTED_PROSPECTIVE : RT_ROUTED_FAILURE; + + (void) do_command(c, sr, "down"); + if ((c->policy & POLICY_DONT_REKEY) + && c->kind == CK_INSTANCE) + { + /* in this special case, even if the connection + * is still alive (due to an ISAKMP SA), + * we get rid of routing. + * Even though there is still an eroute, the c->routing + * setting will convince unroute_connection to delete it. + * unroute_connection would be upset if c->routing == RT_ROUTED_TUNNEL + */ + unroute_connection(c); + } + else + { + (void) shunt_eroute(c, sr, sr->routing, ERO_REPLACE, "replace with shunt"); + } + } + } + (void) teardown_half_ipsec_sa(st, FALSE); + } + (void) teardown_half_ipsec_sa(st, TRUE); +#else /* !KLIPS */ + DBG(DBG_CONTROL, DBG_log("if I knew how, I'd eroute() and teardown_ipsec_sa()")); +#endif /* !KLIPS */ +} + +#ifdef KLIPS +static bool update_nat_t_ipsec_esp_sa (struct state *st, bool inbound) +{ + struct connection *c = st->st_connection; + char text_said[SATOT_BUF]; + struct kernel_sa sa; + ip_address + src = inbound? c->spd.that.host_addr : c->spd.this.host_addr, + dst = inbound? c->spd.this.host_addr : c->spd.that.host_addr; + + ipsec_spi_t esp_spi = inbound? st->st_esp.our_spi : st->st_esp.attrs.spi; + + u_int16_t + natt_sport = inbound? c->spd.that.host_port : c->spd.this.host_port, + natt_dport = inbound? c->spd.this.host_port : c->spd.that.host_port; + + set_text_said(text_said, &dst, esp_spi, SA_ESP); + + memset(&sa, 0, sizeof(sa)); + sa.spi = esp_spi; + sa.src = &src; + sa.dst = &dst; + sa.text_said = text_said; + sa.authalg = alg_info_esp_aa2sadb(st->st_esp.attrs.auth); + sa.natt_sport = natt_sport; + sa.natt_dport = natt_dport; + sa.transid = st->st_esp.attrs.transid; + + return kernel_ops->add_sa(&sa, TRUE); +} +#endif + +bool update_ipsec_sa (struct state *st USED_BY_KLIPS) +{ +#ifdef KLIPS + if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) + { + if (st->st_esp.present && ( + (!update_nat_t_ipsec_esp_sa (st, TRUE)) || + (!update_nat_t_ipsec_esp_sa (st, FALSE)))) + { + return FALSE; + } + } + else if (IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(st->st_state)) + { + if (st->st_esp.present && !update_nat_t_ipsec_esp_sa (st, FALSE)) + { + return FALSE; + } + } + else + { + DBG_log("assert failed at %s:%d st_state=%d", __FILE__, __LINE__, st->st_state); + return FALSE; + } + return TRUE; +#else /* !KLIPS */ + DBG(DBG_CONTROL, DBG_log("if I knew how, I'd update_ipsec_sa()")); + return TRUE; +#endif /* !KLIPS */ +} + +/* Check if there was traffic on given SA during the last idle_max + * seconds. If TRUE, the SA was idle and DPD exchange should be performed. + * If FALSE, DPD is not necessary. We also return TRUE for errors, as they + * could mean that the SA is broken and needs to be replace anyway. + */ +bool +was_eroute_idle(struct state *st, time_t idle_max, time_t *idle_time) +{ + static const char procname[] = "/proc/net/ipsec_spi"; + FILE *f; + char buf[1024]; + u_int bytes; + int ret = TRUE; + + passert(st != NULL); + + f = fopen(procname, "r"); + if (f == NULL) + { + /* Can't open the file, perhaps were are on 26sec? */ + time_t use_time; + + if (get_sa_info(st, TRUE, &bytes, &use_time) + && use_time != UNDEFINED_TIME) + { + *idle_time = time(NULL) - use_time; + ret = *idle_time >= idle_max; + } + } + else + { + while (f != NULL) + { + char *line; + char text_said[SATOT_BUF]; + u_int8_t proto = 0; + ip_address dst; + ip_said said; + ipsec_spi_t spi = 0; + static const char idle[] = "idle="; + + dst = st->st_connection->spd.this.host_addr; /* inbound SA */ + if (st->st_ah.present) + { + proto = SA_AH; + spi = st->st_ah.our_spi; + } + if (st->st_esp.present) + { + proto = SA_ESP; + spi = st->st_esp.our_spi; + } + + if (proto == 0 && spi == 0) + { + ret = TRUE; + break; + } + + initsaid(&dst, spi, proto, &said); + satot(&said, 'x', text_said, SATOT_BUF); + + line = fgets(buf, sizeof(buf), f); + if (line == NULL) + { + /* Reached end of list */ + ret = TRUE; + break; + } + + if (strncmp(line, text_said, strlen(text_said)) == 0) + { + /* we found a match, now try to find idle= */ + char *p = strstr(line, idle); + + if (p == NULL) + { + /* SAs which haven't been used yet don't have it */ + ret = TRUE; /* it didn't have traffic */ + break; + } + p += sizeof(idle)-1; + if (*p == '\0') + { + ret = TRUE; /* be paranoid */ + break; + } + if (sscanf(p, "%d", (int *) idle_time) <= 0) + { + ret = TRUE; + break; + } + if (*idle_time >= idle_max) + { + ret = TRUE; + break; + } + else + { + ret = FALSE; + break; + } + } + } + fclose(f); + } + return ret; +} diff --git a/src/pluto/kernel.h b/src/pluto/kernel.h new file mode 100644 index 000000000..e7ff08c7b --- /dev/null +++ b/src/pluto/kernel.h @@ -0,0 +1,198 @@ +/* declarations of routines that interface with the kernel's IPsec mechanism + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel.h,v 1.10 2006/03/08 22:12:37 as Exp $ + */ + +#include "connections.h" + +extern bool no_klips; /* don't actually use KLIPS */ +extern bool can_do_IPcomp; /* can system actually perform IPCOMP? */ + +#ifdef KLIPS +/* Declare eroute things early enough for uses. + * + * Flags are encoded above the low-order byte of verbs. + * "real" eroutes are only outbound. Inbound eroutes don't exist, + * but an addflow with an INBOUND flag allows IPIP tunnels to be + * limited to appropriate source and destination addresses. + */ + +#define ERO_MASK 0xFF +#define ERO_FLAG_SHIFT 8 + +#define ERO_DELETE SADB_X_DELFLOW +#define ERO_ADD SADB_X_ADDFLOW +#define ERO_REPLACE (SADB_X_ADDFLOW | (SADB_X_SAFLAGS_REPLACEFLOW << ERO_FLAG_SHIFT)) +#define ERO_ADD_INBOUND (SADB_X_ADDFLOW | (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) +#define ERO_DEL_INBOUND (SADB_X_DELFLOW | (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) + +struct pfkey_proto_info { + int proto; + int encapsulation; + unsigned reqid; +}; +struct sadb_msg; + +struct kernel_sa { + const ip_address *src; + const ip_address *dst; + + const ip_subnet *src_client; + const ip_subnet *dst_client; + + ipsec_spi_t spi; + unsigned proto; + unsigned satype; + unsigned transport_proto; + unsigned replay_window; + unsigned reqid; + + unsigned authalg; + unsigned authkeylen; + char *authkey; + + unsigned encalg; + unsigned enckeylen; + char *enckey; + + unsigned compalg; + + int encapsulation; + + u_int16_t natt_sport, natt_dport; + u_int8_t transid, natt_type; + ip_address *natt_oa; + + const char *text_said; +}; + +struct kernel_ops { + enum { + KERNEL_TYPE_NONE, + KERNEL_TYPE_KLIPS, + KERNEL_TYPE_LINUX, + } type; + bool inbound_eroute; + bool policy_lifetime; + int *async_fdp; + + void (*init)(void); + void (*pfkey_register)(void); + void (*pfkey_register_response)(const struct sadb_msg *msg); + void (*process_queue)(void); + void (*process_msg)(void); + bool (*raw_eroute)(const ip_address *this_host, + const ip_subnet *this_client, + const ip_address *that_host, + const ip_subnet *that_client, + ipsec_spi_t spi, + unsigned int satype, + unsigned int transport_proto, + const struct pfkey_proto_info *proto_info, + time_t use_lifetime, + unsigned int op, + const char *text_said); + bool (*get_policy)(const struct kernel_sa *sa, bool inbound, + time_t *use_time); + bool (*add_sa)(const struct kernel_sa *sa, bool replace); + bool (*grp_sa)(const struct kernel_sa *sa_outer, + const struct kernel_sa *sa_inner); + bool (*del_sa)(const struct kernel_sa *sa); + bool (*get_sa)(const struct kernel_sa *sa, u_int *bytes); + ipsec_spi_t (*get_spi)(const ip_address *src, + const ip_address *dst, + int proto, + bool tunnel_mode, + unsigned reqid, + ipsec_spi_t min, + ipsec_spi_t max, + const char *text_said); +}; + + +extern const struct kernel_ops *kernel_ops; + +/* information from /proc/net/ipsec_eroute */ + +struct eroute_info { + unsigned long count; + ip_subnet ours; + ip_subnet his; + ip_address dst; + ip_said said; + int transport_proto; + struct eroute_info *next; +}; + +extern struct eroute_info *orphaned_holds; + +extern void show_shunt_status(void); +#endif + +/* A netlink header defines EM_MAXRELSPIS, the max number of SAs in a group. + * Is there a PF_KEY equivalent? + */ +#ifndef EM_MAXRELSPIS +# define EM_MAXRELSPIS 4 /* AH ESP IPCOMP IPIP */ +#endif + +extern void record_and_initiate_opportunistic(const ip_subnet * + , const ip_subnet * + , int transport_proto + , const char *why); + +extern void init_kernel(void); + +extern void scan_proc_shunts(void); + +extern bool trap_connection(struct connection *c); +extern void unroute_connection(struct connection *c); + +extern bool has_bare_hold(const ip_address *src, const ip_address *dst + , int transport_proto); + +extern bool replace_bare_shunt(const ip_address *src, const ip_address *dst + , policy_prio_t policy_prio + , ipsec_spi_t shunt_spi /* in host order! */ + , bool repl + , unsigned int transport_proto + , const char *why); + +extern bool assign_hold(struct connection *c + , struct spd_route *sr + , int transport_proto + , const ip_address *src, const ip_address *dst); + +extern ipsec_spi_t shunt_policy_spi(struct connection *c, bool prospective); + + +struct state; /* forward declaration of tag */ +extern ipsec_spi_t get_ipsec_spi(ipsec_spi_t avoid + , int proto + , struct spd_route *sr + , bool tunnel_mode); +extern ipsec_spi_t get_my_cpi(struct spd_route *sr, bool tunnel_mode); + +extern bool install_inbound_ipsec_sa(struct state *st); +extern bool install_ipsec_sa(struct state *st, bool inbound_also); +extern void delete_ipsec_sa(struct state *st, bool inbound_only); +extern bool route_and_eroute(struct connection *c + , struct spd_route *sr + , struct state *st); +extern bool was_eroute_idle(struct state *st, time_t idle_max + , time_t *idle_time); +extern bool get_sa_info(struct state *st, bool inbound, u_int *bytes + , time_t *use_time); + +extern bool update_ipsec_sa(struct state *st); diff --git a/src/pluto/kernel_alg.c b/src/pluto/kernel_alg.c new file mode 100644 index 000000000..91dfaff59 --- /dev/null +++ b/src/pluto/kernel_alg.c @@ -0,0 +1,775 @@ +/* Kernel runtime algorithm handling interface + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_alg.c,v 1.9 2005/08/17 16:31:24 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "connections.h" +#include "state.h" +#include "packet.h" +#include "spdb.h" +#include "kernel.h" +#include "kernel_alg.h" +#include "alg_info.h" + +#ifndef NO_PLUTO +#include "log.h" +#include "whack.h" +#include "db_ops.h" +#else +/* + * macros/functions for compilation without pluto (eg: spi for manual conns) + */ +extern int debug; +#include +#define passert(x) assert(x) +#define DBG(cond, action) { if (debug) { action ; } } +#define DBG_log(x, args...) fprintf(stderr, x "\n" , ##args); +#define plog(x, args...) fprintf(stderr, x "\n" , ##args); +#endif /* NO_PLUTO */ +/* ALG storage */ +static struct sadb_alg esp_aalg[SADB_AALG_MAX+1]; +static struct sadb_alg esp_ealg[SADB_EALG_MAX+1]; +static int esp_ealg_num = 0; +static int esp_aalg_num = 0; + +#define ESP_EALG_PRESENT(algo) (((algo)<=SADB_EALG_MAX)&&(esp_ealg[(algo)].sadb_alg_id==(algo))) +#define ESP_EALG_FOR_EACH_UPDOWN(algo) \ + for (algo=SADB_EALG_MAX; algo >0 ; algo--) \ + if (ESP_EALG_PRESENT(algo)) +#define ESP_AALG_PRESENT(algo) ((algo<=SADB_AALG_MAX)&&(esp_aalg[(algo)].sadb_alg_id==(algo))) +#define ESP_AALG_FOR_EACH_UPDOWN(algo) \ + for (algo=SADB_AALG_MAX; algo >0 ; algo--) \ + if (ESP_AALG_PRESENT(algo)) + +static struct sadb_alg* +sadb_alg_ptr (int satype, int exttype, int alg_id, int rw) +{ + struct sadb_alg *alg_p = NULL; + + switch (exttype) + { + case SADB_EXT_SUPPORTED_AUTH: + if (alg_id > SADB_AALG_MAX) + return NULL; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + if (alg_id > SADB_EALG_MAX) + return NULL; + break; + default: + return NULL; + } + + switch (satype) + { + case SADB_SATYPE_ESP: + alg_p = (exttype == SADB_EXT_SUPPORTED_ENCRYPT)? + &esp_ealg[alg_id] : &esp_aalg[alg_id]; + /* get for write: increment elem count */ + if (rw) + { + (exttype == SADB_EXT_SUPPORTED_ENCRYPT)? + esp_ealg_num++ : esp_aalg_num++; + } + break; + case SADB_SATYPE_AH: + default: + return NULL; + } + + return alg_p; +} + +const struct sadb_alg * +kernel_alg_sadb_alg_get(int satype, int exttype, int alg_id) +{ + return sadb_alg_ptr(satype, exttype, alg_id, 0); +} + +/* + * Forget previous registration + */ +static void +kernel_alg_init(void) +{ + DBG(DBG_KLIPS, + DBG_log("alg_init(): memset(%p, 0, %d) memset(%p, 0, %d)", + &esp_aalg, (int)sizeof (esp_aalg), + &esp_ealg, (int)sizeof (esp_ealg)) + ) + memset (&esp_aalg, 0, sizeof (esp_aalg)); + memset (&esp_ealg, 0, sizeof (esp_ealg)); + esp_ealg_num=esp_aalg_num = 0; +} + +static int +kernel_alg_add(int satype, int exttype, const struct sadb_alg *sadb_alg) +{ + struct sadb_alg *alg_p = NULL; + int alg_id = sadb_alg->sadb_alg_id; + + DBG(DBG_KLIPS, + DBG_log("kernel_alg_add(): satype=%d, exttype=%d, alg_id=%d", + satype, exttype, sadb_alg->sadb_alg_id) + ) + if (!(alg_p = sadb_alg_ptr(satype, exttype, alg_id, 1))) + return -1; + + /* This logic "mimics" KLIPS: first algo implementation will be used */ + if (alg_p->sadb_alg_id) + { + DBG(DBG_KLIPS, + DBG_log("kernel_alg_add(): discarding already setup " + "satype=%d, exttype=%d, alg_id=%d", + satype, exttype, sadb_alg->sadb_alg_id) + ) + return 0; + } + *alg_p = *sadb_alg; + return 1; +} + +bool +kernel_alg_esp_enc_ok(u_int alg_id, u_int key_len, + struct alg_info_esp *alg_info __attribute__((unused))) +{ + struct sadb_alg *alg_p = NULL; + + /* + * test #1: encrypt algo must be present + */ + int ret = ESP_EALG_PRESENT(alg_id); + if (!ret) goto out; + + alg_p = &esp_ealg[alg_id]; + + /* + * test #2: if key_len specified, it must be in range + */ + if (key_len + && (key_len < alg_p->sadb_alg_minbits || key_len > alg_p->sadb_alg_maxbits)) + { + plog("kernel_alg_db_add() key_len not in range: alg_id=%d, " + "key_len=%d, alg_minbits=%d, alg_maxbits=%d" + , alg_id, key_len + , alg_p->sadb_alg_minbits + , alg_p->sadb_alg_maxbits); + ret = FALSE; + } + +out: + if (ret) + { + DBG(DBG_KLIPS, + DBG_log("kernel_alg_esp_enc_ok(%d,%d): " + "alg_id=%d, " + "alg_ivlen=%d, alg_minbits=%d, alg_maxbits=%d, " + "res=%d, ret=%d" + , alg_id, key_len + , alg_p->sadb_alg_id + , alg_p->sadb_alg_ivlen + , alg_p->sadb_alg_minbits + , alg_p->sadb_alg_maxbits + , alg_p->sadb_alg_reserved + , ret); + ) + } + else + { + DBG(DBG_KLIPS, + DBG_log("kernel_alg_esp_enc_ok(%d,%d): NO", alg_id, key_len); + ) + } + return ret; +} + +/* + * ML: make F_STRICT logic consider enc,auth algorithms + */ +#ifndef NO_PLUTO +bool +kernel_alg_esp_ok_final(u_int ealg, u_int key_len, u_int aalg, struct alg_info_esp *alg_info) +{ + int ealg_insecure; + + /* + * key_len passed comes from esp_attrs read from peer + * For many older algoritms (eg 3DES) this key_len is fixed + * and get passed as 0. + * ... then get default key_len + */ + if (key_len == 0) + key_len = kernel_alg_esp_enc_keylen(ealg) * BITS_PER_BYTE; + + /* + * simple test to toss low key_len, will accept it only + * if specified in "esp" string + */ + ealg_insecure = (key_len < 128) ; + + if (ealg_insecure + || (alg_info && alg_info->alg_info_flags & ALG_INFO_F_STRICT)) + { + int i; + struct esp_info *esp_info; + + if (alg_info) + { + ALG_INFO_ESP_FOREACH(alg_info, esp_info, i) + { + if (esp_info->esp_ealg_id == ealg + && (esp_info->esp_ealg_keylen == 0 || key_len == 0 + || esp_info->esp_ealg_keylen == key_len) + && esp_info->esp_aalg_id == aalg) + { + if (ealg_insecure) + { + loglog(RC_LOG_SERIOUS + , "You should NOT use insecure ESP algorithms [%s (%d)]!" + , enum_name(&esp_transformid_names, ealg), key_len); + } + return TRUE; + } + } + } + plog("IPSec Transform [%s (%d), %s] refused due to %s", + enum_name(&esp_transformid_names, ealg), key_len, + enum_name(&auth_alg_names, aalg), + ealg_insecure ? "insecure key_len and enc. alg. not listed in \"esp\" string" : "strict flag"); + return FALSE; + } + return TRUE; +} +#endif /* NO_PLUTO */ + +/* + * Load kernel_alg arrays from /proc + * used in manual mode from klips/utils/spi.c + */ +int +kernel_alg_proc_read(void) +{ + int satype; + int supp_exttype; + int alg_id, ivlen, minbits, maxbits; + struct sadb_alg sadb_alg; + int ret; + char buf[128]; + + FILE *fp=fopen("/proc/net/pf_key_supported", "r"); + + if (!fp) + return -1; + + kernel_alg_init(); + + while (fgets(buf, sizeof(buf), fp)) + { + if (buf[0] != ' ') /* skip titles */ + continue; + + sscanf(buf, "%d %d %d %d %d %d" + ,&satype, &supp_exttype + , &alg_id, &ivlen + , &minbits, &maxbits); + + switch (satype) + { + case SADB_SATYPE_ESP: + switch(supp_exttype) + { + case SADB_EXT_SUPPORTED_AUTH: + case SADB_EXT_SUPPORTED_ENCRYPT: + sadb_alg.sadb_alg_id = alg_id; + sadb_alg.sadb_alg_ivlen = ivlen; + sadb_alg.sadb_alg_minbits = minbits; + sadb_alg.sadb_alg_maxbits = maxbits; + ret = kernel_alg_add(satype, supp_exttype, &sadb_alg); + DBG(DBG_CRYPT, + DBG_log("kernel_alg_proc_read() alg_id=%d, " + "alg_ivlen=%d, alg_minbits=%d, alg_maxbits=%d, " + "ret=%d" + , sadb_alg.sadb_alg_id + , sadb_alg.sadb_alg_ivlen + , sadb_alg.sadb_alg_minbits + , sadb_alg.sadb_alg_maxbits + , ret) + ) + } + default: + continue; + } + } + fclose(fp); + return 0; +} + +/* + * Load kernel_alg arrays pluto's SADB_REGISTER + * user by pluto/kernel.c + */ + +void +kernel_alg_register_pfkey(const struct sadb_msg *msg_buf, int buflen) +{ + /* Trick: one 'type-mangle-able' pointer to ease offset/assign */ + union { + const struct sadb_msg *msg; + const struct sadb_supported *supported; + const struct sadb_ext *ext; + const struct sadb_alg *alg; + const char *ch; + } sadb; + + int satype; + int msglen; + int i = 0; + + /* Initialize alg arrays */ + kernel_alg_init(); + satype = msg_buf->sadb_msg_satype; + sadb.msg = msg_buf; + msglen = sadb.msg->sadb_msg_len*IPSEC_PFKEYv2_ALIGN; + msglen -= sizeof(struct sadb_msg); + buflen -= sizeof(struct sadb_msg); + passert(buflen > 0); + + sadb.msg++; + + while(msglen) + { + int supp_exttype = sadb.supported->sadb_supported_exttype; + int supp_len = sadb.supported->sadb_supported_len*IPSEC_PFKEYv2_ALIGN; + + DBG(DBG_KLIPS, + DBG_log("kernel_alg_register_pfkey(): SADB_SATYPE_%s: " + "sadb_msg_len=%d sadb_supported_len=%d" + , satype==SADB_SATYPE_ESP? "ESP" : "AH" + , msg_buf->sadb_msg_len, supp_len) + ) + sadb.supported++; + msglen -= supp_len; + buflen -= supp_len; + passert(buflen >= 0); + + for (supp_len -= sizeof(struct sadb_supported); + supp_len; + supp_len -= sizeof(struct sadb_alg), sadb.alg++,i++) + { + int ret = kernel_alg_add(satype, supp_exttype, sadb.alg); + + DBG(DBG_KLIPS, + DBG_log("kernel_alg_register_pfkey(): SADB_SATYPE_%s: " + "alg[%d], exttype=%d, satype=%d, alg_id=%d, " + "alg_ivlen=%d, alg_minbits=%d, alg_maxbits=%d, " + "res=%d, ret=%d" + , satype==SADB_SATYPE_ESP? "ESP" : "AH" + , i + , supp_exttype + , satype + , sadb.alg->sadb_alg_id + , sadb.alg->sadb_alg_ivlen + , sadb.alg->sadb_alg_minbits + , sadb.alg->sadb_alg_maxbits + , sadb.alg->sadb_alg_reserved + , ret) + ) + } + } +} + +u_int +kernel_alg_esp_enc_keylen(u_int alg_id) +{ + u_int keylen = 0; + + if (!ESP_EALG_PRESENT(alg_id)) + goto none; + + keylen = esp_ealg[alg_id].sadb_alg_maxbits/BITS_PER_BYTE; + + switch (alg_id) + { + /* + * this is veryUgly[TM] + * Peer should have sent KEY_LENGTH attribute for ESP_AES + * but if not do force it to 128 instead of using sadb_alg_maxbits + * from kernel. + */ + case ESP_AES: + keylen = 128/BITS_PER_BYTE; + break; + } + +none: + DBG(DBG_KLIPS, + DBG_log("kernel_alg_esp_enc_keylen():" + "alg_id=%d, keylen=%d", + alg_id, keylen) + ) + return keylen; +} + +struct sadb_alg * +kernel_alg_esp_sadb_alg(u_int alg_id) +{ + struct sadb_alg *sadb_alg = (ESP_EALG_PRESENT(alg_id)) + ? &esp_ealg[alg_id] : NULL; + + DBG(DBG_KLIPS, + DBG_log("kernel_alg_esp_sadb_alg(): alg_id=%d, sadb_alg=%p" + , alg_id, sadb_alg) + ) + return sadb_alg; +} + +#ifndef NO_PLUTO +void kernel_alg_list(void) +{ + u_int sadb_id; + + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of registered ESP Encryption Algorithms:"); + whack_log(RC_COMMENT, " "); + + for (sadb_id = 1; sadb_id <= SADB_EALG_MAX; sadb_id++) + { + if (ESP_EALG_PRESENT(sadb_id)) + { + struct sadb_alg *alg_p = &esp_ealg[sadb_id]; + + whack_log(RC_COMMENT, "#%-5d %s, blocksize: %d, keylen: %d-%d" + , sadb_id + , enum_name(&esp_transformid_names, sadb_id) + , alg_p->sadb_alg_ivlen + , alg_p->sadb_alg_minbits + , alg_p->sadb_alg_maxbits + ); + } + } + + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of registered ESP Authentication Algorithms:"); + whack_log(RC_COMMENT, " "); + + for (sadb_id = 1; sadb_id <= SADB_AALG_MAX; sadb_id++) + { + if (ESP_AALG_PRESENT(sadb_id)) + { + u_int aaid = alg_info_esp_sadb2aa(sadb_id); + struct sadb_alg *alg_p = &esp_aalg[sadb_id]; + + whack_log(RC_COMMENT, "#%-5d %s, keylen: %d-%d" + , aaid + , enum_name(&auth_alg_names, aaid) + , alg_p->sadb_alg_minbits + , alg_p->sadb_alg_maxbits + ); + } + } +} + +void +kernel_alg_show_connection(struct connection *c, const char *instance) +{ + char buf[256]; + struct state *st; + + if (c->alg_info_esp) + { + alg_info_snprint(buf, sizeof(buf), (struct alg_info *)c->alg_info_esp); + whack_log(RC_COMMENT + , "\"%s\"%s: ESP algorithms wanted: %s" + , c->name + , instance + , buf); + } + if (c->alg_info_esp) + { + alg_info_snprint_esp(buf, sizeof(buf), c->alg_info_esp); + whack_log(RC_COMMENT + , "\"%s\"%s: ESP algorithms loaded: %s" + , c->name + , instance + , buf); + } + st = state_with_serialno(c->newest_ipsec_sa); + if (st && st->st_esp.present) + whack_log(RC_COMMENT + , "\"%s\"%s: ESP algorithm newest: %s_%d-%s; pfsgroup=%s" + , c->name + , instance + , enum_show(&esp_transformid_names, st->st_esp.attrs.transid) + +4 /* strlen("ESP_") */ + , st->st_esp.attrs.key_len + , enum_show(&auth_alg_names, st->st_esp.attrs.auth)+ + +15 /* strlen("AUTH_ALGORITHM_") */ + , c->policy & POLICY_PFS ? + c->alg_info_esp->esp_pfsgroup ? + enum_show(&oakley_group_names, + c->alg_info_esp->esp_pfsgroup) + +13 /*strlen("OAKLEY_GROUP_")*/ + : "" + : "" + ); +} +#endif /* NO_PLUTO */ + +bool +kernel_alg_esp_auth_ok(u_int auth, + struct alg_info_esp *alg_info __attribute__((unused))) +{ + return ESP_AALG_PRESENT(alg_info_esp_aa2sadb(auth)); +} + +u_int +kernel_alg_esp_auth_keylen(u_int auth) +{ + u_int sadb_aalg = alg_info_esp_aa2sadb(auth); + + u_int a_keylen = (sadb_aalg) + ? esp_aalg[sadb_aalg].sadb_alg_maxbits/BITS_PER_BYTE + : 0; + + DBG(DBG_CONTROL | DBG_CRYPT | DBG_PARSING, + DBG_log("kernel_alg_esp_auth_keylen(auth=%d, sadb_aalg=%d): " + "a_keylen=%d", auth, sadb_aalg, a_keylen) + ) + return a_keylen; +} + +struct esp_info * +kernel_alg_esp_info(int transid, int auth) +{ + int sadb_aalg, sadb_ealg; + static struct esp_info ei_buf; + + sadb_ealg = transid; + sadb_aalg = alg_info_esp_aa2sadb(auth); + + if (!ESP_EALG_PRESENT(sadb_ealg)) + goto none; + if (!ESP_AALG_PRESENT(sadb_aalg)) + goto none; + + memset(&ei_buf, 0, sizeof (ei_buf)); + ei_buf.transid = transid; + ei_buf.auth = auth; + + /* don't return "default" keylen because this value is used from + * setup_half_ipsec_sa() to "validate" keylen + * In effect, enckeylen will be used as "max" value + */ + ei_buf.enckeylen = esp_ealg[sadb_ealg].sadb_alg_maxbits/BITS_PER_BYTE; + ei_buf.authkeylen = esp_aalg[sadb_aalg].sadb_alg_maxbits/BITS_PER_BYTE; + ei_buf.encryptalg = sadb_ealg; + ei_buf.authalg = sadb_aalg; + + DBG(DBG_PARSING, + DBG_log("kernel_alg_esp_info():" + "transid=%d, auth=%d, ei=%p, " + "enckeylen=%d, authkeylen=%d, encryptalg=%d, authalg=%d", + transid, auth, &ei_buf, + (int)ei_buf.enckeylen, (int)ei_buf.authkeylen, + ei_buf.encryptalg, ei_buf.authalg) + ) + return &ei_buf; + +none: + DBG(DBG_PARSING, + DBG_log("kernel_alg_esp_info():" + "transid=%d, auth=%d, ei=NULL", + transid, auth) + ) + return NULL; +} + +#ifndef NO_PLUTO +static void +kernel_alg_policy_algorithms(struct esp_info *esp_info) +{ + u_int ealg_id = esp_info->esp_ealg_id; + + switch(ealg_id) + { + case 0: + case ESP_DES: + case ESP_3DES: + case ESP_NULL: + case ESP_CAST: + break; + default: + if (!esp_info->esp_ealg_keylen) + { + /* algos that need KEY_LENGTH + * + * Note: this is a very dirty hack ;-) + * Idea: Add a key_length_needed attribute to + * esp_ealg ?? + */ + esp_info->esp_ealg_keylen = esp_ealg[ealg_id].sadb_alg_maxbits; + } + } +} + +static bool +kernel_alg_db_add(struct db_context *db_ctx, struct esp_info *esp_info, lset_t policy) +{ + u_int ealg_id, aalg_id; + + ealg_id = esp_info->esp_ealg_id; + + if (!ESP_EALG_PRESENT(ealg_id)) + { + DBG_log("kernel_alg_db_add() kernel enc ealg_id=%d not present", ealg_id); + return FALSE; + } + + if (!(policy & POLICY_AUTHENTICATE)) /* skip ESP auth attrs for AH */ + { + aalg_id = alg_info_esp_aa2sadb(esp_info->esp_aalg_id); + + if (!ESP_AALG_PRESENT(aalg_id)) + { + DBG_log("kernel_alg_db_add() kernel auth " + "aalg_id=%d not present", aalg_id); + return FALSE; + } + } + + /* do algo policy */ + kernel_alg_policy_algorithms(esp_info); + + /* open new transformation */ + db_trans_add(db_ctx, ealg_id); + + /* add ESP auth attr */ + if (!(policy & POLICY_AUTHENTICATE)) + db_attr_add_values(db_ctx, AUTH_ALGORITHM, esp_info->esp_aalg_id); + + /* add keylegth if specified in esp= string */ + if (esp_info->esp_ealg_keylen) + db_attr_add_values(db_ctx, KEY_LENGTH, esp_info->esp_ealg_keylen); + + return TRUE; +} + +/* + * Create proposal with runtime kernel algos, merging + * with passed proposal if not NULL + * + * for now this function does free() previous returned + * malloced pointer (this quirk allows easier spdb.c change) + */ +struct db_context * +kernel_alg_db_new(struct alg_info_esp *alg_info, lset_t policy ) +{ + const struct esp_info *esp_info; + struct esp_info tmp_esp_info; + struct db_context *ctx_new=NULL; + struct db_trans *t; + struct db_prop *prop; + u_int trans_cnt; + int tn = 0; + + if (!(policy & POLICY_ENCRYPT)) /* not possible, I think */ + return NULL; + + trans_cnt = esp_ealg_num * esp_aalg_num; + DBG(DBG_EMITTING, + DBG_log("kernel_alg_db_prop_new() initial trans_cnt=%d" + , trans_cnt) + ) + + /* pass aprox. number of transforms and attributes */ + ctx_new = db_prop_new(PROTO_IPSEC_ESP, trans_cnt, trans_cnt * 2); + + /* + * Loop: for each element (struct esp_info) of alg_info, + * if kernel support is present then build the transform (and attrs) + * if NULL alg_info, propose everything ... + */ + + if (alg_info) + { + int i; + + ALG_INFO_ESP_FOREACH(alg_info, esp_info, i) + { + tmp_esp_info = *esp_info; + kernel_alg_db_add(ctx_new, &tmp_esp_info, policy); + } + } + else + { + u_int ealg_id; + + ESP_EALG_FOR_EACH_UPDOWN(ealg_id) + { + u_int aalg_id; + + tmp_esp_info.esp_ealg_id = ealg_id; + tmp_esp_info.esp_ealg_keylen = 0; + + for (aalg_id = 1; aalg_id <= SADB_AALG_MAX; aalg_id++) + { + if (ESP_AALG_PRESENT(aalg_id)) + { + tmp_esp_info.esp_aalg_id = alg_info_esp_sadb2aa(aalg_id); + tmp_esp_info.esp_aalg_keylen = 0; + kernel_alg_db_add(ctx_new, &tmp_esp_info, policy); + } + } + } + } + + prop = db_prop_get(ctx_new); + + DBG(DBG_CONTROL|DBG_EMITTING, + DBG_log("kernel_alg_db_prop_new() " + "will return p_new->protoid=%d, p_new->trans_cnt=%d" + , prop->protoid, prop->trans_cnt) + ) + + for (t = prop->trans, tn = 0; tn < prop->trans_cnt; tn++) + { + DBG(DBG_CONTROL|DBG_EMITTING, + DBG_log("kernel_alg_db_prop_new() " + " trans[%d]: transid=%d, attr_cnt=%d, " + "attrs[0].type=%d, attrs[0].val=%d" + , tn + , t[tn].transid, t[tn].attr_cnt + , t[tn].attrs[0].type, t[tn].attrs[0].val) + ) + } + return ctx_new; +} +#endif /* NO_PLUTO */ diff --git a/src/pluto/kernel_alg.h b/src/pluto/kernel_alg.h new file mode 100644 index 000000000..483e97da1 --- /dev/null +++ b/src/pluto/kernel_alg.h @@ -0,0 +1,46 @@ +/* Kernel runtime algorithm handling interface definitions + * Author: JuanJo Ciarlante + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_alg.h,v 1.5 2005/08/17 16:31:24 as Exp $ + */ + +#ifndef _KERNEL_ALG_H +#define _KERNEL_ALG_H + +#include "alg_info.h" +#include "spdb.h" + +/* status info */ +extern void kernel_alg_show_status(void); +void kernel_alg_show_connection(struct connection *c, const char *instance); + +/* Registration messages from pluto */ +extern void kernel_alg_register_pfkey(const struct sadb_msg *msg, int buflen); + +/* ESP interface */ +extern struct sadb_alg *kernel_alg_esp_sadb_alg(u_int alg_id); +extern u_int kernel_alg_esp_ivlen(u_int alg_id); +extern bool kernel_alg_esp_enc_ok(u_int alg_id, u_int key_len, struct alg_info_esp *nfo); +extern bool kernel_alg_esp_ok_final(u_int ealg, u_int key_len, u_int aalg, struct alg_info_esp *alg_info); +extern u_int kernel_alg_esp_enc_keylen(u_int alg_id); +extern bool kernel_alg_esp_auth_ok(u_int auth, struct alg_info_esp *nfo); +extern u_int kernel_alg_esp_auth_keylen(u_int auth); +extern int kernel_alg_proc_read(void); +extern void kernel_alg_list(void); + +/* get sadb_alg for passed args */ +extern const struct sadb_alg * kernel_alg_sadb_alg_get(int satype, int exttype, int alg_id); + +extern struct db_context * kernel_alg_db_new(struct alg_info_esp *ai, lset_t policy); +struct esp_info * kernel_alg_esp_info(int esp_id, int auth_id); +#endif /* _KERNEL_ALG_H */ diff --git a/src/pluto/kernel_netlink.c b/src/pluto/kernel_netlink.c new file mode 100644 index 000000000..1947ddbac --- /dev/null +++ b/src/pluto/kernel_netlink.c @@ -0,0 +1,1219 @@ +/* netlink interface to the kernel's IPsec mechanism + * Copyright (C) 2003 Herbert Xu. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_netlink.c,v 1.24 2006/03/10 14:49:43 as Exp $ + */ + +#if defined(linux) && defined(KERNEL26_SUPPORT) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kameipsec.h" +#include "linux26/rtnetlink.h" +#include "linux26/xfrm.h" + +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "kernel.h" +#include "kernel_netlink.h" +#include "kernel_pfkey.h" +#include "log.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ +#include "kernel_alg.h" + +/* Minimum priority number in SPD used by pluto. */ +#define MIN_SPD_PRIORITY 1024 + +static int netlinkfd = NULL_FD; +static int netlink_bcast_fd = NULL_FD; + +#define NE(x) { x, #x } /* Name Entry -- shorthand for sparse_names */ + +static sparse_names xfrm_type_names = { + NE(NLMSG_NOOP), + NE(NLMSG_ERROR), + NE(NLMSG_DONE), + NE(NLMSG_OVERRUN), + + NE(XFRM_MSG_NEWSA), + NE(XFRM_MSG_DELSA), + NE(XFRM_MSG_GETSA), + + NE(XFRM_MSG_NEWPOLICY), + NE(XFRM_MSG_DELPOLICY), + NE(XFRM_MSG_GETPOLICY), + + NE(XFRM_MSG_ALLOCSPI), + NE(XFRM_MSG_ACQUIRE), + NE(XFRM_MSG_EXPIRE), + + NE(XFRM_MSG_UPDPOLICY), + NE(XFRM_MSG_UPDSA), + + NE(XFRM_MSG_POLEXPIRE), + + NE(XFRM_MSG_MAX), + + { 0, sparse_end } +}; + +#undef NE + +/* Authentication algorithms */ +static sparse_names aalg_list = { + { SADB_X_AALG_NULL, "digest_null" }, + { SADB_AALG_MD5_HMAC, "md5" }, + { SADB_AALG_SHA1_HMAC, "sha1" }, + { SADB_AALG_SHA2_256_HMAC, "sha256" }, + { SADB_AALG_SHA2_384_HMAC, "sha384" }, + { SADB_AALG_SHA2_512_HMAC, "sha512" }, + { SADB_AALG_RIPEMD_160_HMAC, "ripemd160" }, + { SADB_X_AALG_NULL, "null" }, + { 0, sparse_end } +}; + +/* Encryption algorithms */ +static sparse_names ealg_list = { + { SADB_EALG_NULL, "cipher_null" }, + { SADB_EALG_DES_CBC, "des" }, + { SADB_EALG_3DES_CBC, "des3_ede" }, + { SADB_EALG_IDEA_CBC, "idea" }, + { SADB_EALG_CAST_CBC, "cast128" }, + { SADB_EALG_BLOWFISH_CBC, "blowfish" }, + { SADB_EALG_AES_CBC, "aes" }, + { SADB_X_EALG_SERPENT_CBC, "serpent" }, + { SADB_X_EALG_TWOFISH_CBC, "twofish" }, + { 0, sparse_end } +}; + +/* Compression algorithms */ +static sparse_names calg_list = { + { SADB_X_CALG_DEFLATE, "deflate" }, + { SADB_X_CALG_LZS, "lzs" }, + { SADB_X_CALG_LZJH, "lzjh" }, + { 0, sparse_end } +}; + +/** ip2xfrm - Take an IP address and convert to an xfrm. + * + * @param addr ip_address + * @param xaddr xfrm_address_t - IPv[46] Address from addr is copied here. + */ +static void +ip2xfrm(const ip_address *addr, xfrm_address_t *xaddr) +{ + if (addr->u.v4.sin_family == AF_INET) + { + xaddr->a4 = addr->u.v4.sin_addr.s_addr; + } + else + { + memcpy(xaddr->a6, &addr->u.v6.sin6_addr, sizeof(xaddr->a6)); + } +} + +/** init_netlink - Initialize the netlink inferface. Opens the sockets and + * then binds to the broadcast socket. + */ +static void +init_netlink(void) +{ + struct sockaddr_nl addr; + + netlinkfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM); + + if (netlinkfd < 0) + exit_log_errno((e, "socket() in init_netlink()")); + + if (fcntl(netlinkfd, F_SETFD, FD_CLOEXEC) != 0) + exit_log_errno((e, "fcntl(FD_CLOEXEC) in init_netlink()")); + + netlink_bcast_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_XFRM); + + if (netlink_bcast_fd < 0) + exit_log_errno((e, "socket() for bcast in init_netlink()")); + + if (fcntl(netlink_bcast_fd, F_SETFD, FD_CLOEXEC) != 0) + exit_log_errno((e, "fcntl(FD_CLOEXEC) for bcast in init_netlink()")); + + if (fcntl(netlink_bcast_fd, F_SETFL, O_NONBLOCK) != 0) + exit_log_errno((e, "fcntl(O_NONBLOCK) for bcast in init_netlink()")); + + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE; + if (bind(netlink_bcast_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) + exit_log_errno((e, "Failed to bind bcast socket in init_netlink()")); +} + +/** send_netlink_msg + * + * @param hdr - Data to be sent. + * @param rbuf - Return Buffer - contains data returned from the send. + * @param rbuf_len - Length of rbuf + * @param description - String - user friendly description of what is + * being attempted. Used for diagnostics + * @param text_said - String + * @return bool True if the message was succesfully sent. + */ +static bool +send_netlink_msg(struct nlmsghdr *hdr, struct nlmsghdr *rbuf, size_t rbuf_len +, const char *description, const char *text_said) +{ + struct { + struct nlmsghdr n; + struct nlmsgerr e; + char data[1024]; + } rsp; + + size_t len; + ssize_t r; + struct sockaddr_nl addr; + static uint32_t seq; + + if (no_klips) + { + return TRUE; + } + + hdr->nlmsg_seq = ++seq; + len = hdr->nlmsg_len; + do { + r = write(netlinkfd, hdr, len); + } while (r < 0 && errno == EINTR); + if (r < 0) + { + log_errno((e + , "netlink write() of %s message" + " for %s %s failed" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , description, text_said)); + return FALSE; + } + else if ((size_t)r != len) + { + loglog(RC_LOG_SERIOUS + , "ERROR: netlink write() of %s message" + " for %s %s truncated: %ld instead of %lu" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , description, text_said + , (long)r, (unsigned long)len); + return FALSE; + } + + for (;;) { + socklen_t alen; + + alen = sizeof(addr); + r = recvfrom(netlinkfd, &rsp, sizeof(rsp), 0 + , (struct sockaddr *)&addr, &alen); + if (r < 0) + { + if (errno == EINTR) + { + continue; + } + log_errno((e + , "netlink recvfrom() of response to our %s message" + " for %s %s failed" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , description, text_said)); + return FALSE; + } + else if ((size_t) r < sizeof(rsp.n)) + { + plog("netlink read truncated message: %ld bytes; ignore message" + , (long) r); + continue; + } + else if (addr.nl_pid != 0) + { + /* not for us: ignore */ + DBG(DBG_KLIPS, + DBG_log("netlink: ignoring %s message from process %u" + , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type) + , addr.nl_pid)); + continue; + } + else if (rsp.n.nlmsg_seq != seq) + { + DBG(DBG_KLIPS, + DBG_log("netlink: ignoring out of sequence (%u/%u) message %s" + , rsp.n.nlmsg_seq, seq + , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type))); + continue; + } + break; + } + + if (rsp.n.nlmsg_len > (size_t) r) + { + loglog(RC_LOG_SERIOUS + , "netlink recvfrom() of response to our %s message" + " for %s %s was truncated: %ld instead of %lu" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , description, text_said + , (long) len, (unsigned long) rsp.n.nlmsg_len); + return FALSE; + } + else if (rsp.n.nlmsg_type != NLMSG_ERROR + && (rbuf && rsp.n.nlmsg_type != rbuf->nlmsg_type)) + { + loglog(RC_LOG_SERIOUS + , "netlink recvfrom() of response to our %s message" + " for %s %s was of wrong type (%s)" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , description, text_said + , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type)); + return FALSE; + } + else if (rbuf) + { + if ((size_t) r > rbuf_len) + { + loglog(RC_LOG_SERIOUS + , "netlink recvfrom() of response to our %s message" + " for %s %s was too long: %ld > %lu" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , description, text_said + , (long)r, (unsigned long)rbuf_len); + return FALSE; + } + memcpy(rbuf, &rsp, r); + return TRUE; + } + else if (rsp.n.nlmsg_type == NLMSG_ERROR && rsp.e.error) + { + loglog(RC_LOG_SERIOUS + , "ERROR: netlink response for %s %s included errno %d: %s" + , description, text_said + , -rsp.e.error + , strerror(-rsp.e.error)); + return FALSE; + } + + return TRUE; +} + +/** netlink_policy - + * + * @param hdr - Data to check + * @param enoent_ok - Boolean - OK or not OK. + * @param text_said - String + * @return boolean + */ +static bool +netlink_policy(struct nlmsghdr *hdr, bool enoent_ok, const char *text_said) +{ + struct { + struct nlmsghdr n; + struct nlmsgerr e; + } rsp; + int error; + + rsp.n.nlmsg_type = NLMSG_ERROR; + if (!send_netlink_msg(hdr, &rsp.n, sizeof(rsp), "policy", text_said)) + { + return FALSE; + } + + error = -rsp.e.error; + if (!error) + { + return TRUE; + } + + if (error == ENOENT && enoent_ok) + { + return TRUE; + } + + loglog(RC_LOG_SERIOUS + , "ERROR: netlink %s response for flow %s included errno %d: %s" + , sparse_val_show(xfrm_type_names, hdr->nlmsg_type) + , text_said + , error + , strerror(error)); + return FALSE; +} + +/** netlink_raw_eroute + * + * @param this_host ip_address + * @param this_client ip_subnet + * @param that_host ip_address + * @param that_client ip_subnet + * @param spi + * @param proto int (Currently unused) Contains protocol (u=tcp, 17=udp, etc...) + * @param transport_proto int (Currently unused) 0=tunnel, 1=transport + * @param satype int + * @param proto_info + * @param lifetime (Currently unused) + * @param ip int + * @return boolean True if successful + */ +static bool +netlink_raw_eroute(const ip_address *this_host + , const ip_subnet *this_client + , const ip_address *that_host + , const ip_subnet *that_client + , ipsec_spi_t spi + , unsigned int satype + , unsigned int transport_proto + , const struct pfkey_proto_info *proto_info + , time_t use_lifetime UNUSED + , unsigned int op + , const char *text_said) +{ + struct { + struct nlmsghdr n; + union { + struct xfrm_userpolicy_info p; + struct xfrm_userpolicy_id id; + } u; + char data[1024]; + } req; + int shift; + int dir; + int family; + int policy; + bool ok; + bool enoent_ok; + + policy = IPSEC_POLICY_IPSEC; + + if (satype == SADB_X_SATYPE_INT) + { + /* shunt route */ + switch (ntohl(spi)) + { + case SPI_PASS: + policy = IPSEC_POLICY_NONE; + break; + case SPI_DROP: + case SPI_REJECT: + default: + policy = IPSEC_POLICY_DISCARD; + break; + case SPI_TRAP: + case SPI_TRAPSUBNET: + case SPI_HOLD: + if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) + { + return TRUE; + } + break; + } + } + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + family = that_client->addr.u.v4.sin_family; + shift = (family == AF_INET) ? 5 : 7; + + req.u.p.sel.sport = portof(&this_client->addr); + req.u.p.sel.dport = portof(&that_client->addr); + req.u.p.sel.sport_mask = (req.u.p.sel.sport) ? ~0:0; + req.u.p.sel.dport_mask = (req.u.p.sel.dport) ? ~0:0; + ip2xfrm(&this_client->addr, &req.u.p.sel.saddr); + ip2xfrm(&that_client->addr, &req.u.p.sel.daddr); + req.u.p.sel.prefixlen_s = this_client->maskbits; + req.u.p.sel.prefixlen_d = that_client->maskbits; + req.u.p.sel.proto = transport_proto; + req.u.p.sel.family = family; + + dir = XFRM_POLICY_OUT; + if (op & (SADB_X_SAFLAGS_INFLOW << ERO_FLAG_SHIFT)) + { + dir = XFRM_POLICY_IN; + } + + if ((op & ERO_MASK) == ERO_DELETE) + { + req.u.id.dir = dir; + req.n.nlmsg_type = XFRM_MSG_DELPOLICY; + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.u.id))); + } + else + { + int src, dst; + + req.u.p.dir = dir; + + src = req.u.p.sel.prefixlen_s; + dst = req.u.p.sel.prefixlen_d; + if (dir != XFRM_POLICY_OUT) { + src = req.u.p.sel.prefixlen_d; + dst = req.u.p.sel.prefixlen_s; + } + req.u.p.priority = MIN_SPD_PRIORITY + + (((2 << shift) - src) << shift) + + (2 << shift) - dst; + + req.u.p.action = XFRM_POLICY_ALLOW; + if (policy == IPSEC_POLICY_DISCARD) + { + req.u.p.action = XFRM_POLICY_BLOCK; + } + req.u.p.lft.soft_use_expires_seconds = use_lifetime; + req.u.p.lft.soft_byte_limit = XFRM_INF; + req.u.p.lft.soft_packet_limit = XFRM_INF; + req.u.p.lft.hard_byte_limit = XFRM_INF; + req.u.p.lft.hard_packet_limit = XFRM_INF; + + req.n.nlmsg_type = XFRM_MSG_NEWPOLICY; + if (op & (SADB_X_SAFLAGS_REPLACEFLOW << ERO_FLAG_SHIFT)) + { + req.n.nlmsg_type = XFRM_MSG_UPDPOLICY; + } + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.u.p))); + } + + if (policy == IPSEC_POLICY_IPSEC && (op & ERO_MASK) != ERO_DELETE) + { + struct rtattr *attr; + struct xfrm_user_tmpl tmpl[4]; + int i; + + memset(tmpl, 0, sizeof(tmpl)); + for (i = 0; proto_info[i].proto; i++) + { + tmpl[i].reqid = proto_info[i].reqid; + tmpl[i].id.proto = proto_info[i].proto; + tmpl[i].optional = + proto_info[i].proto == IPPROTO_COMP && dir != XFRM_POLICY_OUT; + tmpl[i].aalgos = tmpl[i].ealgos = tmpl[i].calgos = ~0; + tmpl[i].mode = + proto_info[i].encapsulation == ENCAPSULATION_MODE_TUNNEL; + + if (!tmpl[i].mode) + { + continue; + } + + ip2xfrm(this_host, &tmpl[i].saddr); + ip2xfrm(that_host, &tmpl[i].id.daddr); + } + + attr = (struct rtattr *)((char *)&req + req.n.nlmsg_len); + attr->rta_type = XFRMA_TMPL; + attr->rta_len = i * sizeof(tmpl[0]); + memcpy(RTA_DATA(attr), tmpl, attr->rta_len); + attr->rta_len = RTA_LENGTH(attr->rta_len); + req.n.nlmsg_len += attr->rta_len; + } + + enoent_ok = FALSE; + if (op == ERO_DEL_INBOUND) + { + enoent_ok = TRUE; + } + else if (op == ERO_DELETE && ntohl(spi) == SPI_HOLD) + { + enoent_ok = TRUE; + } + + ok = netlink_policy(&req.n, enoent_ok, text_said); + switch (dir) + { + case XFRM_POLICY_IN: + if (req.n.nlmsg_type == XFRM_MSG_DELPOLICY) + { + req.u.id.dir = XFRM_POLICY_FWD; + } + else if (!ok) + { + break; + } + else if (proto_info[0].encapsulation != ENCAPSULATION_MODE_TUNNEL + && satype != SADB_X_SATYPE_INT) + { + break; + } + else + { + req.u.p.dir = XFRM_POLICY_FWD; + } + ok &= netlink_policy(&req.n, enoent_ok, text_said); + break; + } + + return ok; +} + +/** netlink_add_sa - Add an SA into the kernel SPDB via netlink + * + * @param sa Kernel SA to add/modify + * @param replace boolean - true if this replaces an existing SA + * @return bool True if successfull + */ +static bool +netlink_add_sa(const struct kernel_sa *sa, bool replace) +{ + struct { + struct nlmsghdr n; + struct xfrm_usersa_info p; + char data[1024]; + } req; + struct rtattr *attr; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.n.nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + + ip2xfrm(sa->src, &req.p.saddr); + ip2xfrm(sa->dst, &req.p.id.daddr); + + req.p.id.spi = sa->spi; + req.p.id.proto = satype2proto(sa->satype); + req.p.family = sa->src->u.v4.sin_family; + req.p.mode = (sa->encapsulation == ENCAPSULATION_MODE_TUNNEL); + req.p.replay_window = sa->replay_window; + req.p.reqid = sa->reqid; + req.p.lft.soft_byte_limit = XFRM_INF; + req.p.lft.soft_packet_limit = XFRM_INF; + req.p.lft.hard_byte_limit = XFRM_INF; + req.p.lft.hard_packet_limit = XFRM_INF; + + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.p))); + + attr = (struct rtattr *)((char *)&req + req.n.nlmsg_len); + + if (sa->authalg) + { + struct xfrm_algo algo; + const char *name; + + name = sparse_name(aalg_list, sa->authalg); + if (!name) { + loglog(RC_LOG_SERIOUS, "unknown authentication algorithm: %u" + , sa->authalg); + return FALSE; + } + + strcpy(algo.alg_name, name); + algo.alg_key_len = sa->authkeylen * BITS_PER_BYTE; + + attr->rta_type = XFRMA_ALG_AUTH; + attr->rta_len = RTA_LENGTH(sizeof(algo) + sa->authkeylen); + + memcpy(RTA_DATA(attr), &algo, sizeof(algo)); + memcpy((char *)RTA_DATA(attr) + sizeof(algo), sa->authkey + , sa->authkeylen); + + req.n.nlmsg_len += attr->rta_len; + attr = (struct rtattr *)((char *)attr + attr->rta_len); + } + + if (sa->encalg) + { + struct xfrm_algo algo; + const char *name; + + name = sparse_name(ealg_list, sa->encalg); + if (!name) { + loglog(RC_LOG_SERIOUS, "unknown encryption algorithm: %u" + , sa->encalg); + return FALSE; + } + + strcpy(algo.alg_name, name); + algo.alg_key_len = sa->enckeylen * BITS_PER_BYTE; + + attr->rta_type = XFRMA_ALG_CRYPT; + attr->rta_len = RTA_LENGTH(sizeof(algo) + sa->enckeylen); + + memcpy(RTA_DATA(attr), &algo, sizeof(algo)); + memcpy((char *)RTA_DATA(attr) + sizeof(algo), sa->enckey + , sa->enckeylen); + + req.n.nlmsg_len += attr->rta_len; + attr = (struct rtattr *)((char *)attr + attr->rta_len); + } + + if (sa->compalg) + { + struct xfrm_algo algo; + const char *name; + + name = sparse_name(calg_list, sa->compalg); + if (!name) { + loglog(RC_LOG_SERIOUS, "unknown compression algorithm: %u" + , sa->compalg); + return FALSE; + } + + strcpy(algo.alg_name, name); + algo.alg_key_len = 0; + + attr->rta_type = XFRMA_ALG_COMP; + attr->rta_len = RTA_LENGTH(sizeof(algo)); + + memcpy(RTA_DATA(attr), &algo, sizeof(algo)); + + req.n.nlmsg_len += attr->rta_len; + attr = (struct rtattr *)((char *)attr + attr->rta_len); + } + + if (sa->natt_type) + { + struct xfrm_encap_tmpl natt; + + natt.encap_type = sa->natt_type; + natt.encap_sport = ntohs(sa->natt_sport); + natt.encap_dport = ntohs(sa->natt_dport); + memset (&natt.encap_oa, 0, sizeof (natt.encap_oa)); + + attr->rta_type = XFRMA_ENCAP; + attr->rta_len = RTA_LENGTH(sizeof(natt)); + + memcpy(RTA_DATA(attr), &natt, sizeof(natt)); + + req.n.nlmsg_len += attr->rta_len; + attr = (struct rtattr *)((char *)attr + attr->rta_len); + } + + return send_netlink_msg(&req.n, NULL, 0, "Add SA", sa->text_said); +} + +/** netlink_del_sa - Delete an SA from the Kernel + * + * @param sa Kernel SA to be deleted + * @return bool True if successfull + */ +static bool +netlink_del_sa(const struct kernel_sa *sa) +{ + struct { + struct nlmsghdr n; + struct xfrm_usersa_id id; + char data[1024]; + } req; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.n.nlmsg_type = XFRM_MSG_DELSA; + + ip2xfrm(sa->dst, &req.id.daddr); + + req.id.spi = sa->spi; + req.id.family = sa->src->u.v4.sin_family; + req.id.proto = sa->proto; + + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id))); + + return send_netlink_msg(&req.n, NULL, 0, "Del SA", sa->text_said); +} + +static bool +netlink_error(const char *req_type, const struct nlmsghdr *n +, const struct nlmsgerr *e, int rsp_size) +{ + if (n->nlmsg_type == NLMSG_ERROR) + { + DBG(DBG_KLIPS, + DBG_log("%s returned with errno %d: %s" + , req_type + , -e->error + , strerror(-e->error)) + ) + return TRUE; + } + if (n->nlmsg_len < NLMSG_LENGTH(rsp_size)) + { + plog("%s returned message with length %lu < %lu bytes" + , req_type + , (unsigned long) n->nlmsg_len + , (unsigned long) rsp_size); + return TRUE; + } + return FALSE; +} + +static bool +netlink_get_policy(const struct kernel_sa *sa, bool inbound, time_t *use_time) +{ + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_id id; + } req; + + struct { + struct nlmsghdr n; + union { + struct nlmsgerr e; + struct xfrm_userpolicy_info info; + } u; + char data[1024]; + } rsp; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_GETPOLICY; + + req.id.sel.sport = portof(&sa->src_client->addr); + req.id.sel.dport = portof(&sa->dst_client->addr); + req.id.sel.sport_mask = (req.id.sel.sport) ? ~0:0; + req.id.sel.dport_mask = (req.id.sel.dport) ? ~0:0; + ip2xfrm(&sa->src_client->addr, &req.id.sel.saddr); + ip2xfrm(&sa->dst_client->addr, &req.id.sel.daddr); + req.id.sel.prefixlen_s = sa->src_client->maskbits; + req.id.sel.prefixlen_d = sa->dst_client->maskbits; + req.id.sel.proto = sa->transport_proto; + req.id.sel.family = sa->dst_client->addr.u.v4.sin_family; + + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id))); + rsp.n.nlmsg_type = XFRM_MSG_NEWPOLICY; + + req.id.dir = (inbound)? XFRM_POLICY_IN:XFRM_POLICY_OUT; + + if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get policy", "?")) + return FALSE; + + if (netlink_error("XFRM_MSG_GETPOLICY", &rsp.n, &rsp.u.e, sizeof(rsp.u.info))) + return FALSE; + + *use_time = (time_t)rsp.u.info.curlft.use_time; + + if (inbound && sa->encapsulation == ENCAPSULATION_MODE_TUNNEL) + { + time_t use_time_fwd; + + req.id.dir = XFRM_POLICY_FWD; + + if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get policy", "?")) + return FALSE; + + if (netlink_error("XFRM_MSG_GETPOLICY", &rsp.n, &rsp.u.e, sizeof(rsp.u.info))) + return FALSE; + + use_time_fwd = (time_t)rsp.u.info.curlft.use_time; + *use_time = (*use_time > use_time_fwd)? *use_time : use_time_fwd; + } + return TRUE; +} + + +/** netlink_get_sa - Get information about an SA from the Kernel + * + * @param sa Kernel SA to be queried + * @return bool True if successfull + */ +static bool +netlink_get_sa(const struct kernel_sa *sa, u_int *bytes) +{ + struct { + struct nlmsghdr n; + struct xfrm_usersa_id id; + } req; + + struct { + struct nlmsghdr n; + union { + struct nlmsgerr e; + struct xfrm_usersa_info info; + } u; + char data[1024]; + } rsp; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_GETSA; + + ip2xfrm(sa->dst, &req.id.daddr); + + req.id.spi = sa->spi; + req.id.family = sa->src->u.v4.sin_family; + req.id.proto = sa->proto; + + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id))); + rsp.n.nlmsg_type = XFRM_MSG_NEWSA; + + if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get SA", sa->text_said)) + return FALSE; + + if (netlink_error("XFRM_MSG_GETSA", &rsp.n, &rsp.u.e, sizeof(rsp.u.info))) + return FALSE; + + *bytes = (u_int) rsp.u.info.curlft.bytes; + + return TRUE; +} + +static void +linux_pfkey_register_response(const struct sadb_msg *msg) +{ + switch (msg->sadb_msg_satype) + { + case SADB_SATYPE_ESP: +#ifndef NO_KERNEL_ALG + kernel_alg_register_pfkey(msg, msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN); +#endif + break; + case SADB_X_SATYPE_IPCOMP: + can_do_IPcomp = TRUE; + break; + default: + break; + } +} + +/** linux_pfkey_register - Register via PFKEY our capabilities + * + */ +static void +linux_pfkey_register(void) +{ + pfkey_register_proto(SADB_SATYPE_AH, "AH"); + pfkey_register_proto(SADB_SATYPE_ESP, "ESP"); + pfkey_register_proto(SADB_X_SATYPE_IPCOMP, "IPCOMP"); + pfkey_close(); +} + +/** Create ip_address out of xfrm_address_t. + * + * @param family + * @param src xfrm formatted IP address + * @param dst ip_address formatted destination + * @return err_t NULL if okay, otherwise an error + */ +static err_t +xfrm_to_ip_address(unsigned family, const xfrm_address_t *src, ip_address *dst) +{ + switch (family) + { + case AF_INET: /* IPv4 */ + case AF_UNSPEC: /* Unspecified, we assume IPv4 */ + initaddr((const void *) &src->a4, sizeof(src->a4), AF_INET, dst); + return NULL; + case AF_INET6: /* IPv6 */ + initaddr((const void *) &src->a6, sizeof(src->a6), AF_INET6, dst); + return NULL; + default: + return "unknown address family"; + } +} + +/* Create a pair of ip_address's out of xfrm_sel. + * + * @param sel xfrm selector + * @param src ip_address formatted source + * @param dst ip_address formatted destination + * @return err_t NULL if okay, otherwise an error + */ +static err_t +xfrm_sel_to_ip_pair(const struct xfrm_selector *sel + , ip_address *src + , ip_address *dst) +{ + int family; + err_t ugh; + + family = sel->family; + + if ((ugh = xfrm_to_ip_address(family, &sel->saddr, src)) + || (ugh = xfrm_to_ip_address(family, &sel->daddr, dst))) + return ugh; + + /* family has been verified in xfrm_to_ip_address. */ + if (family == AF_INET) + { + src->u.v4.sin_port = sel->sport; + dst->u.v4.sin_port = sel->dport; + } + else + { + src->u.v6.sin6_port = sel->sport; + dst->u.v6.sin6_port = sel->dport; + } + + return NULL; +} + +static void +netlink_acquire(struct nlmsghdr *n) +{ + struct xfrm_user_acquire *acquire; + ip_address src, dst; + ip_subnet ours, his; + unsigned transport_proto; + err_t ugh = NULL; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*acquire))) + { + plog("netlink_acquire got message with length %lu < %lu bytes; ignore message" + , (unsigned long) n->nlmsg_len + , (unsigned long) sizeof(*acquire)); + return; + } + + acquire = NLMSG_DATA(n); + transport_proto = acquire->sel.proto; + + /* XXX also the type of src/dst should be checked to make sure + * that they aren't v4 to v6 or something goofy + */ + + if (!(ugh = xfrm_sel_to_ip_pair(&acquire->sel, &src, &dst)) + && !(ugh = addrtosubnet(&src, &ours)) + && !(ugh = addrtosubnet(&dst, &his))) + record_and_initiate_opportunistic(&ours, &his, transport_proto + , "%acquire-netlink"); + + if (ugh != NULL) + plog("XFRM_MSG_ACQUIRE message from kernel malformed: %s", ugh); +} + +static void +netlink_shunt_expire(struct xfrm_userpolicy_info *pol) +{ + ip_address src, dst; + unsigned transport_proto; + err_t ugh = NULL; + + transport_proto = pol->sel.proto; + + if (!(ugh = xfrm_sel_to_ip_pair(&pol->sel, &src, &dst))) + { + plog("XFRM_MSG_POLEXPIRE message from kernel malformed: %s", ugh); + return; + } + + replace_bare_shunt(&src, &dst, BOTTOM_PRIO, SPI_PASS, FALSE, transport_proto + , "delete expired bare shunt"); +} + +static void +netlink_policy_expire(struct nlmsghdr *n) +{ + struct xfrm_user_polexpire *upe; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_id id; + } req; + + struct { + struct nlmsghdr n; + union { + struct nlmsgerr e; + struct xfrm_userpolicy_info pol; + } u; + char data[1024]; + } rsp; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*upe))) + { + plog("netlink_policy_expire got message with length %lu < %lu bytes; ignore message" + , (unsigned long) n->nlmsg_len + , (unsigned long) sizeof(*upe)); + return; + } + + upe = NLMSG_DATA(n); + req.id.dir = upe->pol.dir; + req.id.index = upe->pol.index; + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_GETPOLICY; + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.id))); + + rsp.n.nlmsg_type = XFRM_MSG_NEWPOLICY; + + if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get policy", "?")) + return; + + if (netlink_error("XFRM_MSG_GETPOLICY", &rsp.n, &rsp.u.e, sizeof(rsp.u.pol))) + return; + + if (req.id.index != rsp.u.pol.index) + { + DBG(DBG_KLIPS, + DBG_log("netlink_policy_expire: policy was replaced: " + "dir=%d, oldindex=%d, newindex=%d" + , req.id.dir, req.id.index, rsp.u.pol.index)); + return; + } + + if (upe->pol.curlft.add_time != rsp.u.pol.curlft.add_time) + { + DBG(DBG_KLIPS, + DBG_log("netlink_policy_expire: policy was replaced " + " and you have won the lottery: " + "dir=%d, index=%d" + , req.id.dir, req.id.index)); + return; + } + + switch (upe->pol.dir) + { + case XFRM_POLICY_OUT: + netlink_shunt_expire(&rsp.u.pol); + break; + } +} + +static bool +netlink_get(void) +{ + struct { + struct nlmsghdr n; + char data[1024]; + } rsp; + ssize_t r; + struct sockaddr_nl addr; + socklen_t alen; + + alen = sizeof(addr); + r = recvfrom(netlink_bcast_fd, &rsp, sizeof(rsp), 0 + , (struct sockaddr *)&addr, &alen); + if (r < 0) + { + if (errno == EAGAIN) + return FALSE; + if (errno != EINTR) + log_errno((e, "recvfrom() failed in netlink_get")); + return TRUE; + } + else if ((size_t) r < sizeof(rsp.n)) + { + plog("netlink_get read truncated message: %ld bytes; ignore message" + , (long) r); + return TRUE; + } + else if (addr.nl_pid != 0) + { + /* not for us: ignore */ + DBG(DBG_KLIPS, + DBG_log("netlink_get: ignoring %s message from process %u" + , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type) + , addr.nl_pid)); + return TRUE; + } + else if ((size_t) r != rsp.n.nlmsg_len) + { + plog("netlink_get read message with length %ld that doesn't equal nlmsg_len %lu bytes; ignore message" + , (long) r + , (unsigned long) rsp.n.nlmsg_len); + return TRUE; + } + + DBG(DBG_KLIPS, + DBG_log("netlink_get: %s message" + , sparse_val_show(xfrm_type_names, rsp.n.nlmsg_type))); + + switch (rsp.n.nlmsg_type) + { + case XFRM_MSG_ACQUIRE: + netlink_acquire(&rsp.n); + break; + case XFRM_MSG_POLEXPIRE: + netlink_policy_expire(&rsp.n); + break; + default: + /* ignored */ + break; + } + + return TRUE; +} + +static void +netlink_process_msg(void) +{ + while (netlink_get()) + ; +} + +static ipsec_spi_t +netlink_get_spi(const ip_address *src +, const ip_address *dst +, int proto +, bool tunnel_mode +, unsigned reqid +, ipsec_spi_t min +, ipsec_spi_t max +, const char *text_said) +{ + struct { + struct nlmsghdr n; + struct xfrm_userspi_info spi; + } req; + + struct { + struct nlmsghdr n; + union { + struct nlmsgerr e; + struct xfrm_usersa_info sa; + } u; + char data[1024]; + } rsp; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_ALLOCSPI; + + ip2xfrm(src, &req.spi.info.saddr); + ip2xfrm(dst, &req.spi.info.id.daddr); + req.spi.info.mode = tunnel_mode; + req.spi.info.reqid = reqid; + req.spi.info.id.proto = proto; + req.spi.info.family = src->u.v4.sin_family; + req.spi.min = min; + req.spi.max = max; + + req.n.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.spi))); + rsp.n.nlmsg_type = XFRM_MSG_NEWSA; + + if (!send_netlink_msg(&req.n, &rsp.n, sizeof(rsp), "Get SPI", text_said)) + return 0; + + if (netlink_error("XFRM_MSG_ALLOCSPI", &rsp.n, &rsp.u.e, sizeof(rsp.u.sa))) + return 0; + + DBG(DBG_KLIPS, + DBG_log("netlink_get_spi: allocated 0x%x for %s" + , ntohl(rsp.u.sa.id.spi), text_said)); + return rsp.u.sa.id.spi; +} + +const struct kernel_ops linux_kernel_ops = { + type: KERNEL_TYPE_LINUX, + inbound_eroute: 1, + policy_lifetime: 1, + async_fdp: &netlink_bcast_fd, + + init: init_netlink, + pfkey_register: linux_pfkey_register, + pfkey_register_response: linux_pfkey_register_response, + process_msg: netlink_process_msg, + raw_eroute: netlink_raw_eroute, + get_policy: netlink_get_policy, + add_sa: netlink_add_sa, + del_sa: netlink_del_sa, + get_sa: netlink_get_sa, + process_queue: NULL, + grp_sa: NULL, + get_spi: netlink_get_spi, +}; +#endif /* linux && KLIPS */ diff --git a/src/pluto/kernel_netlink.h b/src/pluto/kernel_netlink.h new file mode 100644 index 000000000..1b5f42e48 --- /dev/null +++ b/src/pluto/kernel_netlink.h @@ -0,0 +1,20 @@ +/* declarations of routines that interface with the kernel's pfkey mechanism + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * Copyright (C) 2003 Herbert Xu + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_netlink.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#if defined(KLIPS) && defined(linux) +extern const struct kernel_ops linux_kernel_ops; +#endif diff --git a/src/pluto/kernel_noklips.c b/src/pluto/kernel_noklips.c new file mode 100644 index 000000000..570bb0470 --- /dev/null +++ b/src/pluto/kernel_noklips.c @@ -0,0 +1,126 @@ +/* interface to fake kernel interface, used for testing pluto in-vitro. + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * Copyright (C) 2003 Michael Richardson + * Copyright (C) 2003 Herbert Xu. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_noklips.c,v 1.5 2006/02/04 00:01:22 as Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "kernel.h" +#include "kernel_noklips.h" +#include "log.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ + +void +init_noklips(void) +{ + return; +} + +/* asynchronous messages from our queue */ +static void +noklips_dequeue(void) +{ +} + +/* asynchronous messages directly from PF_KEY socket */ +static void +noklips_event(void) +{ +} + +static void +noklips_register_response(const struct sadb_msg *msg UNUSED) +{ +} + +static void +noklips_register(void) +{ +} + +static bool +noklips_raw_eroute(const ip_address *this_host UNUSED + , const ip_subnet *this_client UNUSED + , const ip_address *that_host UNUSED + , const ip_subnet *that_client UNUSED + , ipsec_spi_t spi UNUSED + , unsigned int satype UNUSED + , unsigned int transport_proto UNUSED + , const struct pfkey_proto_info *proto_info UNUSED + , time_t use_lifetime UNUSED + , unsigned int op UNUSED + , const char *text_said UNUSED) +{ + return TRUE; +} + +static bool +noklips_add_sa(const struct kernel_sa *sa UNUSED + , bool replace UNUSED) +{ + return TRUE; +} + +static bool +noklips_grp_sa(const struct kernel_sa *sa0 UNUSED + , const struct kernel_sa *sa1 UNUSED) +{ + return TRUE; +} + +static bool +noklips_del_sa(const struct kernel_sa *sa UNUSED) +{ + return TRUE; +} + + +const struct kernel_ops noklips_kernel_ops = { + type: KERNEL_TYPE_NONE, + async_fdp: NULL, + + init: init_noklips, + pfkey_register: noklips_register, + pfkey_register_response: noklips_register_response, + process_queue: noklips_dequeue, + process_msg: noklips_event, + raw_eroute: noklips_raw_eroute, + add_sa: noklips_add_sa, + grp_sa: noklips_grp_sa, + del_sa: noklips_del_sa, + get_sa: NULL, + get_spi: NULL, + inbound_eroute: FALSE, + policy_lifetime: FALSE +}; diff --git a/src/pluto/kernel_noklips.h b/src/pluto/kernel_noklips.h new file mode 100644 index 000000000..fe4e77ec4 --- /dev/null +++ b/src/pluto/kernel_noklips.h @@ -0,0 +1,19 @@ +/* declarations of routines that interface with the kernel's pfkey mechanism + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * Copyright (C) 2003 Herbert Xu + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_noklips.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +extern void init_noklips(void); +extern const struct kernel_ops noklips_kernel_ops; diff --git a/src/pluto/kernel_pfkey.c b/src/pluto/kernel_pfkey.c new file mode 100644 index 000000000..ced7a1453 --- /dev/null +++ b/src/pluto/kernel_pfkey.c @@ -0,0 +1,926 @@ +/* pfkey interface to the kernel's IPsec mechanism + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * Copyright (C) 2003 Herbert Xu. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_pfkey.c,v 1.8 2006/02/04 00:01:22 as Exp $ + */ + +#ifdef KLIPS + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "kernel.h" +#include "kernel_pfkey.h" +#include "log.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ +#include "demux.h" +#include "nat_traversal.h" +#include "alg_info.h" +#include "kernel_alg.h" + + +static int pfkeyfd = NULL_FD; + +typedef u_int32_t pfkey_seq_t; +static pfkey_seq_t pfkey_seq = 0; /* sequence number for our PF_KEY messages */ + +static pid_t pid; + +#define NE(x) { x, #x } /* Name Entry -- shorthand for sparse_names */ + +static sparse_names pfkey_type_names = { + NE(SADB_RESERVED), + NE(SADB_GETSPI), + NE(SADB_UPDATE), + NE(SADB_ADD), + NE(SADB_DELETE), + NE(SADB_GET), + NE(SADB_ACQUIRE), + NE(SADB_REGISTER), + NE(SADB_EXPIRE), + NE(SADB_FLUSH), + NE(SADB_DUMP), + NE(SADB_X_PROMISC), + NE(SADB_X_PCHANGE), + NE(SADB_X_GRPSA), + NE(SADB_X_ADDFLOW), + NE(SADB_X_DELFLOW), + NE(SADB_X_DEBUG), + NE(SADB_X_NAT_T_NEW_MAPPING), + NE(SADB_MAX), + { 0, sparse_end } +}; + +#ifdef NEVER /* not needed yet */ +static sparse_names pfkey_ext_names = { + NE(SADB_EXT_RESERVED), + NE(SADB_EXT_SA), + NE(SADB_EXT_LIFETIME_CURRENT), + NE(SADB_EXT_LIFETIME_HARD), + NE(SADB_EXT_LIFETIME_SOFT), + NE(SADB_EXT_ADDRESS_SRC), + NE(SADB_EXT_ADDRESS_DST), + NE(SADB_EXT_ADDRESS_PROXY), + NE(SADB_EXT_KEY_AUTH), + NE(SADB_EXT_KEY_ENCRYPT), + NE(SADB_EXT_IDENTITY_SRC), + NE(SADB_EXT_IDENTITY_DST), + NE(SADB_EXT_SENSITIVITY), + NE(SADB_EXT_PROPOSAL), + NE(SADB_EXT_SUPPORTED_AUTH), + NE(SADB_EXT_SUPPORTED_ENCRYPT), + NE(SADB_EXT_SPIRANGE), + NE(SADB_X_EXT_KMPRIVATE), + NE(SADB_X_EXT_SATYPE2), + NE(SADB_X_EXT_SA2), + NE(SADB_X_EXT_ADDRESS_DST2), + NE(SADB_X_EXT_ADDRESS_SRC_FLOW), + NE(SADB_X_EXT_ADDRESS_DST_FLOW), + NE(SADB_X_EXT_ADDRESS_SRC_MASK), + NE(SADB_X_EXT_ADDRESS_DST_MASK), + NE(SADB_X_EXT_DEBUG), + { 0, sparse_end } +}; +#endif /* NEVER */ + +#undef NE + +void +init_pfkey(void) +{ + pid = getpid(); + + /* open PF_KEY socket */ + + pfkeyfd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + + if (pfkeyfd == -1) + exit_log_errno((e, "socket() in init_pfkeyfd()")); + +#ifdef NEVER /* apparently unsupported! */ + if (fcntl(pfkeyfd, F_SETFL, O_NONBLOCK) != 0) + exit_log_errno((e, "fcntl(O_NONBLOCK) in init_pfkeyfd()")); +#endif + if (fcntl(pfkeyfd, F_SETFD, FD_CLOEXEC) != 0) + exit_log_errno((e, "fcntl(FD_CLOEXEC) in init_pfkeyfd()")); + + DBG(DBG_KLIPS, + DBG_log("process %u listening for PF_KEY_V2 on file descriptor %d", (unsigned)pid, pfkeyfd)); +} + +/* Kinds of PF_KEY message from the kernel: + * - response to a request from us + * + ACK/NAK + * + Register: indicates transforms supported by kernel + * + SPI requested by getspi + * - Acquire, requesting us to deal with trapped clear packet + * - expiration of of one of our SAs + * - messages to other processes + * + * To minimize the effect on the event-driven structure of Pluto, + * responses are dealt with synchronously. We hope that the Kernel + * produces them synchronously. We must "read ahead" in the PF_KEY + * stream, saving Acquire and Expiry messages that are encountered. + * We ignore messages to other processes. + */ + +typedef union { + unsigned char bytes[PFKEYv2_MAX_MSGSIZE]; + struct sadb_msg msg; + } pfkey_buf; + +/* queue of unprocessed PF_KEY messages input from kernel + * Note that the pfkey_buf may be partly allocated, reflecting + * the variable length nature of the messages. So the link field + * must come first. + */ +typedef struct pfkey_item { + struct pfkey_item *next; + pfkey_buf buf; + } pfkey_item; + +static pfkey_item *pfkey_iq_head = NULL; /* oldest */ +static pfkey_item *pfkey_iq_tail; /* youngest */ + +static bool +pfkey_input_ready(void) +{ + fd_set readfds; + int ndes; + struct timeval tm; + + tm.tv_sec = 0; /* don't wait at all */ + tm.tv_usec = 0; + + FD_ZERO(&readfds); /* we only care about pfkeyfd */ + FD_SET(pfkeyfd, &readfds); + + do { + ndes = select(pfkeyfd + 1, &readfds, NULL, NULL, &tm); + } while (ndes == -1 && errno == EINTR); + + if (ndes < 0) + { + log_errno((e, "select() failed in pfkey_get()")); + return FALSE; + } + + if (ndes == 0) + return FALSE; /* nothing to read */ + + passert(ndes == 1 && FD_ISSET(pfkeyfd, &readfds)); + return TRUE; +} + +/* get a PF_KEY message from kernel. + * Returns TRUE is message found, FALSE if no message pending, + * and aborts or keeps trying when an error is encountered. + * The only validation of the message is that the message length + * received matches that in the message header, and that the message + * is for this process. + */ +static bool +pfkey_get(pfkey_buf *buf) +{ + for (;;) + { + /* len must be less than PFKEYv2_MAX_MSGSIZE, + * so it should fit in an int. We use this fact when printing it. + */ + ssize_t len; + + if (!pfkey_input_ready()) + return FALSE; + + len = read(pfkeyfd, buf->bytes, sizeof(buf->bytes)); + + if (len < 0) + { + if (errno == EAGAIN) + return FALSE; + + log_errno((e, "read() failed in pfkey_get()")); + return FALSE; + } + else if ((size_t) len < sizeof(buf->msg)) + { + plog("pfkey_get read truncated PF_KEY message: %d bytes; ignoring message" + , (int) len); + } + else if ((size_t) len != buf->msg.sadb_msg_len * IPSEC_PFKEYv2_ALIGN) + { + plog("pfkey_get read PF_KEY message with length %d that doesn't equal sadb_msg_len %u * %u; ignoring message" + , (int) len + , (unsigned) buf->msg.sadb_msg_len + , (unsigned) IPSEC_PFKEYv2_ALIGN); + } + else if (!(buf->msg.sadb_msg_pid == (unsigned)pid + || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_ACQUIRE) + || (buf->msg.sadb_msg_type == SADB_REGISTER) + || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_X_NAT_T_NEW_MAPPING))) + { + /* not for us: ignore */ + DBG(DBG_KLIPS, + DBG_log("pfkey_get: ignoring PF_KEY %s message %u for process %u" + , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) + , buf->msg.sadb_msg_seq + , buf->msg.sadb_msg_pid)); + } + else + { + DBG(DBG_KLIPS, + DBG_log("pfkey_get: %s message %u" + , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) + , buf->msg.sadb_msg_seq)); + return TRUE; + } + } +} + +/* get a response to a specific message */ +static bool +pfkey_get_response(pfkey_buf *buf, pfkey_seq_t seq) +{ + while (pfkey_get(buf)) + { + if (buf->msg.sadb_msg_pid == (unsigned)pid + && buf->msg.sadb_msg_seq == seq) + { + return TRUE; + } + else + { + /* Not for us: queue it. */ + size_t bl = buf->msg.sadb_msg_len * IPSEC_PFKEYv2_ALIGN; + pfkey_item *it = alloc_bytes(offsetof(pfkey_item, buf) + bl, "pfkey_item"); + + memcpy(&it->buf, buf, bl); + + it->next = NULL; + if (pfkey_iq_head == NULL) + { + pfkey_iq_head = it; + } + else + { + pfkey_iq_tail->next = it; + } + pfkey_iq_tail = it; + } + } + return FALSE; +} + +/* Process a SADB_REGISTER message from the kernel. + * This will be a response to one of ours, but it may be asynchronous + * (if kernel modules are loaded and unloaded). + * Some sanity checking has already been performed. + */ +static void +klips_pfkey_register_response(const struct sadb_msg *msg) +{ + /* Find out what the kernel can support. + * In fact, the only question at the moment + * is whether it can support IPcomp. + * So we ignore the rest. + * ??? we really should pay attention to what transforms are supported. + */ + switch (msg->sadb_msg_satype) + { + case SADB_SATYPE_AH: + break; + case SADB_SATYPE_ESP: +#ifndef NO_KERNEL_ALG + kernel_alg_register_pfkey(msg, sizeof (pfkey_buf)); +#endif + break; + case SADB_X_SATYPE_COMP: + /* ??? There ought to be an extension to list the + * supported algorithms, but RFC 2367 doesn't + * list one for IPcomp. KLIPS uses SADB_X_CALG_DEFLATE. + * Since we only implement deflate, we'll assume this. + */ + can_do_IPcomp = TRUE; + break; + case SADB_X_SATYPE_IPIP: + break; + default: + break; + } +} + +/* Processs a SADB_ACQUIRE message from KLIPS. + * Try to build an opportunistic connection! + * See RFC 2367 "PF_KEY Key Management API, Version 2" 3.1.6 + * + * - extensions for source and data IP addresses + * - optional extensions for identity [not useful for us?] + * - optional extension for sensitivity [not useful for us?] + * - expension for proposal [not useful for us?] + * + * ??? We must use the sequence number in creating an SA. + * We actually need to create up to 4 SAs each way. Which one? + * I guess it depends on the protocol present in the sadb_msg_satype. + * For now, we'll ignore this requirement. + * + * ??? We need some mechanism to make sure that multiple ACQUIRE messages + * don't cause a whole bunch of redundant negotiations. + */ +static void +process_pfkey_acquire(pfkey_buf *buf, struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + struct sadb_address *srcx = (void *) extensions[SADB_EXT_ADDRESS_SRC]; + struct sadb_address *dstx = (void *) extensions[SADB_EXT_ADDRESS_DST]; + int src_proto = srcx->sadb_address_proto; + int dst_proto = dstx->sadb_address_proto; + ip_address *src = (ip_address*)&srcx[1]; + ip_address *dst = (ip_address*)&dstx[1]; + ip_subnet ours, his; + err_t ugh = NULL; + + /* assumption: we're only catching our own outgoing packets + * so source is our end and destination is the other end. + * Verifying this is not actually convenient. + * + * This stylized control structure yields a complaint or + * desired results. For compactness, a pointer value is + * treated as a boolean. Logically, the structure is: + * keep going as long as things are OK. + */ + if (buf->msg.sadb_msg_pid == 0 /* we only wish to hear from kernel */ + && !(ugh = src_proto == dst_proto? NULL : "src and dst protocols differ") + && !(ugh = addrtypeof(src) == addrtypeof(dst)? NULL : "conflicting address types") + && !(ugh = addrtosubnet(src, &ours)) + && !(ugh = addrtosubnet(dst, &his))) + record_and_initiate_opportunistic(&ours, &his, src_proto, "%acquire"); + + if (ugh != NULL) + plog("SADB_ACQUIRE message from KLIPS malformed: %s", ugh); + +} + +/* Handle PF_KEY messages from the kernel that are not dealt with + * synchronously. In other words, all but responses to PF_KEY messages + * that we sent. + */ +static void +pfkey_async(pfkey_buf *buf) +{ + struct sadb_ext *extensions[SADB_EXT_MAX + 1]; + + if (pfkey_msg_parse(&buf->msg, NULL, extensions, EXT_BITS_OUT)) + { + plog("pfkey_async:" + " unparseable PF_KEY message:" + " %s len=%d, errno=%d, seq=%d, pid=%d; message ignored" + , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) + , buf->msg.sadb_msg_len + , buf->msg.sadb_msg_errno + , buf->msg.sadb_msg_seq + , buf->msg.sadb_msg_pid); + } + else + { + DBG(DBG_CONTROL | DBG_KLIPS, DBG_log("pfkey_async:" + " %s len=%u, errno=%u, satype=%u, seq=%u, pid=%u" + , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type) + , buf->msg.sadb_msg_len + , buf->msg.sadb_msg_errno + , buf->msg.sadb_msg_satype + , buf->msg.sadb_msg_seq + , buf->msg.sadb_msg_pid)); + + switch (buf->msg.sadb_msg_type) + { + case SADB_REGISTER: + kernel_ops->pfkey_register_response(&buf->msg); + break; + case SADB_ACQUIRE: + /* to simulate loss of ACQUIRE, delete this call */ + process_pfkey_acquire(buf, extensions); + break; + case SADB_X_NAT_T_NEW_MAPPING: + process_pfkey_nat_t_new_mapping(&(buf->msg), extensions); + break; + default: + /* ignored */ + break; + } + } +} + +/* asynchronous messages from our queue */ +static void +pfkey_dequeue(void) +{ + while (pfkey_iq_head != NULL) + { + pfkey_item *it = pfkey_iq_head; + + pfkey_async(&it->buf); + pfkey_iq_head = it->next; + pfree(it); + } + + /* Handle any orphaned holds, but only if no pfkey input is pending. + * For each, we initiate Opportunistic. + * note: we don't need to advance the pointer because + * record_and_initiate_opportunistic will remove the current + * record each time we call it. + */ + while (orphaned_holds != NULL && !pfkey_input_ready()) + record_and_initiate_opportunistic(&orphaned_holds->ours + , &orphaned_holds->his + , orphaned_holds->transport_proto + , "%hold found-pfkey"); + +} + +/* asynchronous messages directly from PF_KEY socket */ +static void +pfkey_event(void) +{ + pfkey_buf buf; + + if (pfkey_get(&buf)) + pfkey_async(&buf); +} + +static bool +pfkey_build(int error +, const char *description +, const char *text_said +, struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + if (error == 0) + { + return TRUE; + } + else + { + loglog(RC_LOG_SERIOUS, "building of %s %s failed, code %d" + , description, text_said, error); + pfkey_extensions_free(extensions); + return FALSE; + } +} + +/* pfkey_extensions_init + pfkey_build + pfkey_msg_hdr_build */ +static bool +pfkey_msg_start(u_int8_t msg_type +, u_int8_t satype +, const char *description +, const char *text_said +, struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + pfkey_extensions_init(extensions); + return pfkey_build(pfkey_msg_hdr_build(&extensions[0], msg_type + , satype, 0, ++pfkey_seq, pid) + , description, text_said, extensions); +} + +/* pfkey_build + pfkey_address_build */ +static bool +pfkeyext_address(u_int16_t exttype +, const ip_address *address +, const char *description +, const char *text_said +, struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + /* the following variable is only needed to silence + * a warning caused by the fact that the argument + * to sockaddrof is NOT pointer to const! + */ + ip_address t = *address; + + return pfkey_build(pfkey_address_build(extensions + exttype + , exttype, 0, 0, sockaddrof(&t)) + , description, text_said, extensions); +} + +/* pfkey_build + pfkey_x_protocol_build */ +static bool +pfkeyext_protocol(int transport_proto +, const char *description +, const char *text_said +, struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + return (transport_proto == 0)? TRUE + : pfkey_build( + pfkey_x_protocol_build(extensions + SADB_X_EXT_PROTOCOL, transport_proto) + , description, text_said, extensions); +} + + +/* Finish (building, sending, accepting response for) PF_KEY message. + * If response isn't NULL, the response from the kernel will be + * placed there (and its errno field will not be examined). + * Returns TRUE iff all appears well. + */ +static bool +finish_pfkey_msg(struct sadb_ext *extensions[SADB_EXT_MAX + 1] +, const char *description +, const char *text_said +, pfkey_buf *response) +{ + struct sadb_msg *pfkey_msg; + bool success = TRUE; + int error; + + error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN); + + if (error != 0) + { + loglog(RC_LOG_SERIOUS, "pfkey_msg_build of %s %s failed, code %d" + , description, text_said, error); + success = FALSE; + } + else + { + size_t len = pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN; + + DBG(DBG_KLIPS, + DBG_log("finish_pfkey_msg: %s message %u for %s %s" + , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type) + , pfkey_msg->sadb_msg_seq + , description, text_said); + DBG_dump(NULL, (void *) pfkey_msg, len)); + + if (!no_klips) + { + ssize_t r = write(pfkeyfd, pfkey_msg, len); + + if (r != (ssize_t)len) + { + if (r < 0) + { + log_errno((e + , "pfkey write() of %s message %u" + " for %s %s failed" + , sparse_val_show(pfkey_type_names + , pfkey_msg->sadb_msg_type) + , pfkey_msg->sadb_msg_seq + , description, text_said)); + } + else + { + loglog(RC_LOG_SERIOUS + , "ERROR: pfkey write() of %s message %u" + " for %s %s truncated: %ld instead of %ld" + , sparse_val_show(pfkey_type_names + , pfkey_msg->sadb_msg_type) + , pfkey_msg->sadb_msg_seq + , description, text_said + , (long)r, (long)len); + } + success = FALSE; + + /* if we were compiled with debugging, but we haven't already + * dumped the KLIPS command, do so. + */ +#ifdef DEBUG + if ((cur_debugging & DBG_KLIPS) == 0) + DBG_dump(NULL, (void *) pfkey_msg, len); +#endif + } + else + { + /* Check response from KLIPS. + * It ought to be an echo, perhaps with additional info. + * If the caller wants it, response will point to space. + */ + pfkey_buf b; + pfkey_buf *bp = response != NULL? response : &b; + + if (!pfkey_get_response(bp, ((struct sadb_msg *) extensions[0])->sadb_msg_seq)) + { + loglog(RC_LOG_SERIOUS + , "ERROR: no response to our PF_KEY %s message for %s %s" + , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type) + , description, text_said); + success = FALSE; + } + else if (pfkey_msg->sadb_msg_type != bp->msg.sadb_msg_type) + { + loglog(RC_LOG_SERIOUS + , "FreeS/WAN ERROR: response to our PF_KEY %s message for %s %s was of wrong type (%s)" + , sparse_name(pfkey_type_names, pfkey_msg->sadb_msg_type) + , description, text_said + , sparse_val_show(pfkey_type_names, bp->msg.sadb_msg_type)); + success = FALSE; + } + else if (response == NULL && bp->msg.sadb_msg_errno != 0) + { + /* KLIPS is signalling a problem */ + loglog(RC_LOG_SERIOUS + , "ERROR: PF_KEY %s response for %s %s included errno %u: %s" + , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type) + , description, text_said + , (unsigned) bp->msg.sadb_msg_errno + , strerror(bp->msg.sadb_msg_errno)); + success = FALSE; + } + } + } + } + + /* all paths must exit this way to free resources */ + pfkey_extensions_free(extensions); + pfkey_msg_free(&pfkey_msg); + return success; +} + +/* register SA types that can be negotiated */ +void +pfkey_register_proto(unsigned satype, const char *satypename) +{ + struct sadb_ext *extensions[SADB_EXT_MAX + 1]; + pfkey_buf pfb; + + if (!(pfkey_msg_start(SADB_REGISTER + , satype + , satypename, NULL, extensions) + && finish_pfkey_msg(extensions, satypename, "", &pfb))) + { + /* ??? should this be loglog */ + plog("no KLIPS support for %s", satypename); + } + else + { + kernel_ops->pfkey_register_response(&pfb.msg); + DBG(DBG_KLIPS, + DBG_log("%s registered with kernel.", satypename)); + } +} + +static void +klips_pfkey_register(void) +{ + pfkey_register_proto(SADB_SATYPE_AH, "AH"); + pfkey_register_proto(SADB_SATYPE_ESP, "ESP"); + can_do_IPcomp = FALSE; /* until we get a response from KLIPS */ + pfkey_register_proto(SADB_X_SATYPE_COMP, "IPCOMP"); + pfkey_register_proto(SADB_X_SATYPE_IPIP, "IPIP"); +} + +static bool +pfkey_raw_eroute(const ip_address *this_host + , const ip_subnet *this_client + , const ip_address *that_host + , const ip_subnet *that_client + , ipsec_spi_t spi + , unsigned int satype + , unsigned int transport_proto + , const struct pfkey_proto_info *proto_info UNUSED + , time_t use_lifetime UNUSED + , unsigned int op + , const char *text_said) +{ + struct sadb_ext *extensions[SADB_EXT_MAX + 1]; + ip_address + sflow_ska, + dflow_ska, + smask_ska, + dmask_ska; + int sport = ntohs(portof(&this_client->addr)); + int dport = ntohs(portof(&that_client->addr)); + + networkof(this_client, &sflow_ska); + maskof(this_client, &smask_ska); + setportof(sport ? ~0:0, &smask_ska); + + networkof(that_client, &dflow_ska); + maskof(that_client, &dmask_ska); + setportof(dport ? ~0:0, &dmask_ska); + + if (!pfkey_msg_start(op & ERO_MASK, satype + , "pfkey_msg_hdr flow", text_said, extensions)) + { + return FALSE; + } + + if (op != ERO_DELETE) + { + if (!(pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA] + , SADB_EXT_SA + , spi /* in network order */ + , 0, 0, 0, 0, op >> ERO_FLAG_SHIFT) + , "pfkey_sa add flow", text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_SRC, this_host + , "pfkey_addr_s add flow", text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_DST, that_host + , "pfkey_addr_d add flow", text_said + , extensions))) + { + return FALSE; + } + } + + if (!pfkeyext_address(SADB_X_EXT_ADDRESS_SRC_FLOW, &sflow_ska + , "pfkey_addr_sflow", text_said, extensions)) + { + return FALSE; + } + + if (!pfkeyext_address(SADB_X_EXT_ADDRESS_DST_FLOW, &dflow_ska + , "pfkey_addr_dflow", text_said, extensions)) + { + return FALSE; + } + + if (!pfkeyext_address(SADB_X_EXT_ADDRESS_SRC_MASK, &smask_ska + , "pfkey_addr_smask", text_said, extensions)) + { + return FALSE; + } + + if (!pfkeyext_address(SADB_X_EXT_ADDRESS_DST_MASK, &dmask_ska + , "pfkey_addr_dmask", text_said, extensions)) + { + return FALSE; + } + + if (!pfkeyext_protocol(transport_proto + , "pfkey_x_protocol", text_said, extensions)) + { + return FALSE; + } + + return finish_pfkey_msg(extensions, "flow", text_said, NULL); +} + +static bool +pfkey_add_sa(const struct kernel_sa *sa, bool replace) +{ + struct sadb_ext *extensions[SADB_EXT_MAX + 1]; + + return pfkey_msg_start(replace ? SADB_UPDATE : SADB_ADD, sa->satype + , "pfkey_msg_hdr Add SA", sa->text_said, extensions) + + && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA] + , SADB_EXT_SA + , sa->spi /* in network order */ + , sa->replay_window, SADB_SASTATE_MATURE + , sa->authalg, sa->encalg ? sa->encalg: sa->compalg, 0) + , "pfkey_sa Add SA", sa->text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_SRC, sa->src + , "pfkey_addr_s Add SA", sa->text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa->dst + , "pfkey_addr_d Add SA", sa->text_said, extensions) + + && (sa->authkeylen == 0 + || pfkey_build(pfkey_key_build(&extensions[SADB_EXT_KEY_AUTH] + , SADB_EXT_KEY_AUTH, sa->authkeylen * BITS_PER_BYTE + , sa->authkey) + , "pfkey_key_a Add SA", sa->text_said, extensions)) + + && (sa->enckeylen == 0 + || pfkey_build(pfkey_key_build(&extensions[SADB_EXT_KEY_ENCRYPT] + , SADB_EXT_KEY_ENCRYPT, sa->enckeylen * BITS_PER_BYTE + , sa->enckey) + , "pfkey_key_e Add SA", sa->text_said, extensions)) + + && (sa->natt_type == 0 + || pfkey_build(pfkey_x_nat_t_type_build( + &extensions[SADB_X_EXT_NAT_T_TYPE], sa->natt_type), + "pfkey_nat_t_type Add ESP SA", sa->text_said, extensions)) + && (sa->natt_sport == 0 + || pfkey_build(pfkey_x_nat_t_port_build( + &extensions[SADB_X_EXT_NAT_T_SPORT], SADB_X_EXT_NAT_T_SPORT, + sa->natt_sport), "pfkey_nat_t_sport Add ESP SA", sa->text_said, + extensions)) + && (sa->natt_dport == 0 + || pfkey_build(pfkey_x_nat_t_port_build( + &extensions[SADB_X_EXT_NAT_T_DPORT], SADB_X_EXT_NAT_T_DPORT, + sa->natt_dport), "pfkey_nat_t_dport Add ESP SA", sa->text_said, + extensions)) + && (sa->natt_type == 0 || isanyaddr(sa->natt_oa) + || pfkeyext_address(SADB_X_EXT_NAT_T_OA, sa->natt_oa + , "pfkey_nat_t_oa Add ESP SA", sa->text_said, extensions)) + + && finish_pfkey_msg(extensions, "Add SA", sa->text_said, NULL); + +} + +static bool +pfkey_grp_sa(const struct kernel_sa *sa0, const struct kernel_sa *sa1) +{ + struct sadb_ext *extensions[SADB_EXT_MAX + 1]; + + return pfkey_msg_start(SADB_X_GRPSA, sa1->satype + , "pfkey_msg_hdr group", sa1->text_said, extensions) + + && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA] + , SADB_EXT_SA + , sa1->spi /* in network order */ + , 0, 0, 0, 0, 0) + , "pfkey_sa group", sa1->text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa1->dst + , "pfkey_addr_d group", sa1->text_said, extensions) + + && pfkey_build(pfkey_x_satype_build(&extensions[SADB_X_EXT_SATYPE2] + , sa0->satype) + , "pfkey_satype group", sa0->text_said, extensions) + + && pfkey_build(pfkey_sa_build(&extensions[SADB_X_EXT_SA2] + , SADB_X_EXT_SA2 + , sa0->spi /* in network order */ + , 0, 0, 0, 0, 0) + , "pfkey_sa2 group", sa0->text_said, extensions) + + && pfkeyext_address(SADB_X_EXT_ADDRESS_DST2, sa0->dst + , "pfkey_addr_d2 group", sa0->text_said, extensions) + + && finish_pfkey_msg(extensions, "group", sa1->text_said, NULL); +} + +static bool +pfkey_del_sa(const struct kernel_sa *sa) +{ + struct sadb_ext *extensions[SADB_EXT_MAX + 1]; + + return pfkey_msg_start(SADB_DELETE, proto2satype(sa->proto) + , "pfkey_msg_hdr delete SA", sa->text_said, extensions) + + && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA] + , SADB_EXT_SA + , sa->spi /* in host order */ + , 0, SADB_SASTATE_MATURE, 0, 0, 0) + , "pfkey_sa delete SA", sa->text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_SRC, sa->src + , "pfkey_addr_s delete SA", sa->text_said, extensions) + + && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa->dst + , "pfkey_addr_d delete SA", sa->text_said, extensions) + + && finish_pfkey_msg(extensions, "Delete SA", sa->text_said, NULL); +} + +void +pfkey_close(void) +{ + while (pfkey_iq_head != NULL) + { + pfkey_item *it = pfkey_iq_head; + + pfkey_iq_head = it->next; + pfree(it); + } + + close(pfkeyfd); + pfkeyfd = NULL_FD; +} + +const struct kernel_ops klips_kernel_ops = { + type: KERNEL_TYPE_KLIPS, + async_fdp: &pfkeyfd, + + pfkey_register: klips_pfkey_register, + pfkey_register_response: klips_pfkey_register_response, + process_queue: pfkey_dequeue, + process_msg: pfkey_event, + raw_eroute: pfkey_raw_eroute, + add_sa: pfkey_add_sa, + grp_sa: pfkey_grp_sa, + del_sa: pfkey_del_sa, + get_sa: NULL, + get_spi: NULL, + inbound_eroute: FALSE, + policy_lifetime: FALSE, + init: NULL +}; +#endif /* KLIPS */ diff --git a/src/pluto/kernel_pfkey.h b/src/pluto/kernel_pfkey.h new file mode 100644 index 000000000..9dbcdd341 --- /dev/null +++ b/src/pluto/kernel_pfkey.h @@ -0,0 +1,23 @@ +/* declarations of routines that interface with the kernel's pfkey mechanism + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * Copyright (C) 2003 Herbert Xu + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: kernel_pfkey.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#ifdef KLIPS +extern void init_pfkey(void); +extern void pfkey_register_proto(unsigned satype, const char *satypename); +extern void pfkey_close(void); +extern const struct kernel_ops klips_kernel_ops; +#endif diff --git a/src/pluto/keys.c b/src/pluto/keys.c new file mode 100644 index 000000000..eed81230f --- /dev/null +++ b/src/pluto/keys.c @@ -0,0 +1,1514 @@ +/* mechanisms for preshared keys (public, private, and preshared secrets) + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: keys.c,v 1.24 2006/01/27 08:59:40 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* missing from on old systems */ +#include + +#include +#ifndef GLOB_ABORTED +# define GLOB_ABORTED GLOB_ABEND /* fix for old versions */ +#endif + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "id.h" +#include "x509.h" +#include "pgp.h" +#include "certs.h" +#include "smartcard.h" +#include "connections.h" +#include "state.h" +#include "lex.h" +#include "keys.h" +#include "adns.h" /* needs */ +#include "dnskey.h" /* needs keys.h and adns.h */ +#include "log.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ +#include "timer.h" +#include "fetch.h" +#include "xauth.h" + +const char *shared_secrets_file = SHARED_SECRETS_FILE; + +typedef struct id_list id_list_t; + +struct id_list { + struct id id; + id_list_t *next; +}; + +typedef struct secret secret_t; + +struct secret { + id_list_t *ids; + enum PrivateKeyKind kind; + union { + chunk_t preshared_secret; + RSA_private_key_t RSA_private_key; + xauth_t xauth_secret; + smartcard_t *smartcard; + } u; + secret_t *next; +}; + +static pubkey_t* +allocate_RSA_public_key(const cert_t cert) +{ + pubkey_t *pk = alloc_thing(pubkey_t, "pubkey"); + chunk_t e, n; + + switch (cert.type) + { + case CERT_PGP: + e = cert.u.pgp->publicExponent; + n = cert.u.pgp->modulus; + break; + case CERT_X509_SIGNATURE: + e = cert.u.x509->publicExponent; + n = cert.u.x509->modulus; + break; + default: + plog("RSA public key allocation error"); + } + + init_RSA_public_key(&pk->u.rsa, e, n); + DBG(DBG_RAW, + RSA_show_public_key(&pk->u.rsa) + ) + + pk->alg = PUBKEY_ALG_RSA; + pk->id = empty_id; + pk->issuer = empty_chunk; + pk->serial = empty_chunk; + + return pk; +} + +/* + * free a public key struct + */ +static void +free_public_key(pubkey_t *pk) +{ + free_id_content(&pk->id); + freeanychunk(pk->issuer); + freeanychunk(pk->serial); + + /* algorithm-specific freeing */ + switch (pk->alg) + { + case PUBKEY_ALG_RSA: + free_RSA_public_content(&pk->u.rsa); + break; + default: + bad_case(pk->alg); + } + pfree(pk); +} + +secret_t *secrets = NULL; + +/* find the struct secret associated with the combination of + * me and the peer. We match the Id (if none, the IP address). + * Failure is indicated by a NULL. + */ +static const secret_t * +get_secret(const struct connection *c, enum PrivateKeyKind kind, bool asym) +{ + enum { /* bits */ + match_default = 01, + match_him = 02, + match_me = 04 + }; + + unsigned int best_match = 0; + secret_t *best = NULL; + secret_t *s; + const struct id *my_id = &c->spd.this.id + , *his_id = &c->spd.that.id; + struct id rw_id; + + /* is there a certificate assigned to this connection? */ + if (kind == PPK_RSA && c->spd.this.cert.type != CERT_NONE) + { + pubkey_t *my_public_key = allocate_RSA_public_key(c->spd.this.cert); + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == kind && + same_RSA_public_key(&s->u.RSA_private_key.pub, &my_public_key->u.rsa)) + { + best = s; + break; /* we have found the private key - no sense in searching further */ + } + } + free_public_key(my_public_key); + return best; + } + + if (his_id_was_instantiated(c)) + { + /* roadwarrior: replace him with 0.0.0.0 */ + rw_id.kind = c->spd.that.id.kind; + rw_id.name = empty_chunk; + happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr)); + his_id = &rw_id; + } + else if (kind == PPK_PSK + && (c->policy & (POLICY_PSK | POLICY_XAUTH_PSK)) + && ((c->kind == CK_TEMPLATE && c->spd.that.id.kind == ID_NONE) || + (c->kind == CK_INSTANCE && id_is_ipaddr(&c->spd.that.id)))) + { + /* roadwarrior: replace him with 0.0.0.0 */ + rw_id.kind = ID_IPV4_ADDR; + happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr)); + his_id = &rw_id; + } + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == kind) + { + unsigned int match = 0; + + if (s->ids == NULL) + { + /* a default (signified by lack of ids): + * accept if no more specific match found + */ + match = match_default; + } + else + { + /* check if both ends match ids */ + id_list_t *i; + + for (i = s->ids; i != NULL; i = i->next) + { + if (same_id(my_id, &i->id)) + match |= match_me; + + if (same_id(his_id, &i->id)) + match |= match_him; + } + + /* If our end matched the only id in the list, + * default to matching any peer. + * A more specific match will trump this. + */ + if (match == match_me + && s->ids->next == NULL) + match |= match_default; + } + + switch (match) + { + case match_me: + /* if this is an asymmetric (eg. public key) system, + * allow this-side-only match to count, even if + * there are other ids in the list. + */ + if (!asym) + break; + /* FALLTHROUGH */ + case match_default: /* default all */ + case match_me | match_default: /* default peer */ + case match_me | match_him: /* explicit */ + if (match == best_match) + { + /* two good matches are equally good: + * do they agree? + */ + bool same = FALSE; + + switch (kind) + { + case PPK_PSK: + same = s->u.preshared_secret.len == best->u.preshared_secret.len + && memcmp(s->u.preshared_secret.ptr, best->u.preshared_secret.ptr, s->u.preshared_secret.len) == 0; + break; + case PPK_RSA: + /* Dirty trick: since we have code to compare + * RSA public keys, but not private keys, we + * make the assumption that equal public keys + * mean equal private keys. This ought to work. + */ + same = same_RSA_public_key(&s->u.RSA_private_key.pub + , &best->u.RSA_private_key.pub); + break; + default: + bad_case(kind); + } + if (!same) + { + loglog(RC_LOG_SERIOUS, "multiple ipsec.secrets entries with distinct secrets match endpoints:" + " first secret used"); + best = s; /* list is backwards: take latest in list */ + } + } + else if (match > best_match) + { + /* this is the best match so far */ + best_match = match; + best = s; + } + } + } + } + return best; +} + +/* find the appropriate preshared key (see get_secret). + * Failure is indicated by a NULL pointer. + * Note: the result is not to be freed by the caller. + */ +const chunk_t * +get_preshared_secret(const struct connection *c) +{ + const secret_t *s = get_secret(c, PPK_PSK, FALSE); + + DBG(DBG_PRIVATE, + if (s == NULL) + DBG_log("no Preshared Key Found"); + else + DBG_dump_chunk("Preshared Key", s->u.preshared_secret); + ) + return s == NULL? NULL : &s->u.preshared_secret; +} + +/* check the existence of an RSA private key matching an RSA public + * key contained in an X.509 or OpenPGP certificate + */ +bool +has_private_key(cert_t cert) +{ + secret_t *s; + bool has_key = FALSE; + pubkey_t *pubkey = allocate_RSA_public_key(cert); + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == PPK_RSA && + same_RSA_public_key(&s->u.RSA_private_key.pub, &pubkey->u.rsa)) + { + has_key = TRUE; + break; + } + } + free_public_key(pubkey); + return has_key; +} + +/* + * get the matching RSA private key belonging to a given X.509 certificate + */ +const RSA_private_key_t* +get_x509_private_key(const x509cert_t *cert) +{ + secret_t *s; + const RSA_private_key_t *pri = NULL; + const cert_t c = {CERT_X509_SIGNATURE, {cert}}; + + pubkey_t *pubkey = allocate_RSA_public_key(c); + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == PPK_RSA && + same_RSA_public_key(&s->u.RSA_private_key.pub, &pubkey->u.rsa)) + { + pri = &s->u.RSA_private_key; + break; + } + } + free_public_key(pubkey); + return pri; +} + +/* find the appropriate RSA private key (see get_secret). + * Failure is indicated by a NULL pointer. + */ +const RSA_private_key_t * +get_RSA_private_key(const struct connection *c) +{ + const secret_t *s = get_secret(c, PPK_RSA, TRUE); + + return s == NULL? NULL : &s->u.RSA_private_key; +} + +/* digest a secrets file + * + * The file is a sequence of records. A record is a maximal sequence of + * tokens such that the first, and only the first, is in the first column + * of a line. + * + * Tokens are generally separated by whitespace and are key words, ids, + * strings, or data suitable for ttodata(3). As a nod to convention, + * a trailing ":" on what would otherwise be a token is taken as a + * separate token. If preceded by whitespace, a "#" is taken as starting + * a comment: it and the rest of the line are ignored. + * + * One kind of record is an include directive. It starts with "include". + * The filename is the only other token in the record. + * If the filename does not start with /, it is taken to + * be relative to the directory containing the current file. + * + * The other kind of record describes a key. It starts with a + * sequence of ids and ends with key information. Each id + * is an IP address, a Fully Qualified Domain Name (which will immediately + * be resolved), or @FQDN which will be left as a name. + * + * The key part can be in several forms. + * + * The old form of the key is still supported: a simple + * quoted strings (with no escapes) is taken as a preshred key. + * + * The new form starts the key part with a ":". + * + * For Preshared Key, use the "PSK" keyword, and follow it by a string + * or a data token suitable for ttodata(3). + * + * For RSA Private Key, use the "RSA" keyword, followed by a + * brace-enclosed list of key field keywords and data values. + * The data values are large integers to be decoded by ttodata(3). + * The fields are a subset of those used by BIND 8.2 and have the + * same names. + */ + +/* parse PSK from file */ +static err_t +process_psk_secret(chunk_t *psk) +{ + err_t ugh = NULL; + + if (*tok == '"' || *tok == '\'') + { + clonetochunk(*psk, tok+1, flp->cur - tok - 2, "PSK"); + (void) shift(); + } + else + { + char buf[BUF_LEN]; /* limit on size of binary representation of key */ + size_t sz; + + ugh = ttodatav(tok, flp->cur - tok, 0, buf, sizeof(buf), &sz + , diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS); + if (ugh != NULL) + { + /* ttodata didn't like PSK data */ + ugh = builddiag("PSK data malformed (%s): %s", ugh, tok); + } + else + { + clonetochunk(*psk, buf, sz, "PSK"); + (void) shift(); + } + } + return ugh; +} + +/* Parse fields of RSA private key. + * A braced list of keyword and value pairs. + * At the moment, each field is required, in order. + * The fields come from BIND 8.2's representation + */ +static err_t +process_rsa_secret(RSA_private_key_t *rsak) +{ + char buf[RSA_MAX_ENCODING_BYTES]; /* limit on size of binary representation of key */ + const struct fld *p; + + /* save bytes of Modulus and PublicExponent for keyid calculation */ + unsigned char ebytes[sizeof(buf)]; + unsigned char *eb_next = ebytes; + chunk_t pub_bytes[2]; + chunk_t *pb_next = &pub_bytes[0]; + + for (p = RSA_private_field; p < &RSA_private_field[RSA_PRIVATE_FIELD_ELEMENTS]; p++) + { + size_t sz; + err_t ugh; + + if (!shift()) + { + return "premature end of RSA key"; + } + else if (!tokeqword(p->name)) + { + return builddiag("%s keyword not found where expected in RSA key" + , p->name); + } + else if (!(shift() + && (!tokeq(":") || shift()))) /* ignore optional ":" */ + { + return "premature end of RSA key"; + } + else if (NULL != (ugh = ttodatav(tok, flp->cur - tok + , 0, buf, sizeof(buf), &sz, diag_space, sizeof(diag_space) + , TTODATAV_SPACECOUNTS))) + { + /* in RSA key, ttodata didn't like */ + return builddiag("RSA data malformed (%s): %s", ugh, tok); + } + else + { + MP_INT *n = (MP_INT *) ((char *)rsak + p->offset); + + n_to_mpz(n, buf, sz); + if (pb_next < &pub_bytes[elemsof(pub_bytes)]) + { + if (eb_next - ebytes + sz > sizeof(ebytes)) + return "public key takes too many bytes"; + + setchunk(*pb_next, eb_next, sz); + memcpy(eb_next, buf, sz); + eb_next += sz; + pb_next++; + } +#if 0 /* debugging info that compromises security */ + { + size_t sz = mpz_sizeinbase(n, 16); + char buf[RSA_MAX_OCTETS * 2 + 2]; /* ought to be big enough */ + + passert(sz <= sizeof(buf)); + mpz_get_str(buf, 16, n); + + loglog(RC_LOG_SERIOUS, "%s: %s", p->name, buf); + } +#endif + } + } + + /* We require an (indented) '}' and the end of the record. + * We break down the test so that the diagnostic will be + * more helpful. Some people don't seem to wish to indent + * the brace! + */ + if (!shift() || !tokeq("}")) + { + return "malformed end of RSA private key -- indented '}' required"; + } + else if (shift()) + { + return "malformed end of RSA private key -- unexpected token after '}'"; + } + else + { + unsigned bits = mpz_sizeinbase(&rsak->pub.n, 2); + + rsak->pub.k = (bits + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + rsak->pub.keyid[0] = '\0'; /* in case of splitkeytoid failure */ + splitkeytoid(pub_bytes[1].ptr, pub_bytes[1].len + , pub_bytes[0].ptr, pub_bytes[0].len + , rsak->pub.keyid, sizeof(rsak->pub.keyid)); + return RSA_private_key_sanity(rsak); + } +} + +/* process rsa key file protected with optional passphrase which can either be + * read from ipsec.secrets or prompted for by using whack + */ +static err_t +process_rsa_keyfile(RSA_private_key_t *rsak, int whackfd) +{ + char filename[BUF_LEN]; + prompt_pass_t pass; + + memset(filename,'\0', BUF_LEN); + memset(pass.secret,'\0', sizeof(pass.secret)); + pass.prompt = FALSE; + pass.fd = whackfd; + + /* we expect the filename of a PKCS#1 private key file */ + + if (*tok == '"' || *tok == '\'') /* quoted filename */ + memcpy(filename, tok+1, flp->cur - tok - 2); + else + memcpy(filename, tok, flp->cur - tok); + + if (shift()) + { + /* we expect an appended passphrase or passphrase prompt*/ + if (tokeqword("%prompt")) + { + if (pass.fd == NULL_FD) + return "RSA private key file -- enter passphrase using 'ipsec secrets'"; + pass.prompt = TRUE; + } + else + { + char *passphrase = tok; + size_t len = flp->cur - passphrase; + + if (*tok == '"' || *tok == '\'') /* quoted passphrase */ + { + passphrase++; + len -= 2; + } + if (len > PROMPT_PASS_LEN) + return "RSA private key file -- passphrase exceeds 64 characters"; + + memcpy(pass.secret, passphrase, len); + } + if (shift()) + return "RSA private key file -- unexpected token after passphrase"; + } + return load_rsa_private_key(filename, &pass, rsak); +} + +/* + * process xauth secret read from ipsec.secrets + */ +static err_t +process_xauth(secret_t *s) +{ + chunk_t user_name; + + s->kind = PPK_XAUTH; + + if (!shift()) + return "missing xauth user name"; + if (*tok == '"' || *tok == '\'') /* quoted user name */ + { + user_name.ptr = tok + 1; + user_name.len = flp->cur - tok - 2; + } + else + { + user_name.ptr = tok; + user_name.len = flp->cur - tok; + } + plog(" loaded xauth credentials of user '%.*s'" + , user_name.len + , user_name.ptr); + clonetochunk(s->u.xauth_secret.user_name + , user_name.ptr, user_name.len, "xauth user name"); + + if (!shift()) + return "missing xauth user password"; + return process_psk_secret(&s->u.xauth_secret.user_password); +} + +/* get XAUTH secret from chained secrets lists + * only one entry is currently supported + */ +static bool +xauth_get_secret(xauth_t *xauth_secret) +{ + secret_t *s; + bool found = FALSE; + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == PPK_XAUTH) + { + if (found) + { + plog("found multiple xauth secrets - first selected"); + } + else + { + found = TRUE; + *xauth_secret = s->u.xauth_secret; + } + } + } + return found; +} + +/* + * find a matching secret + */ +static bool +xauth_verify_secret(const xauth_t *xauth_secret) +{ + bool found = FALSE; + secret_t *s; + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == PPK_XAUTH) + { + if (!same_chunk(xauth_secret->user_name, s->u.xauth_secret.user_name)) + continue; + found = TRUE; + if (same_chunk(xauth_secret->user_password, s->u.xauth_secret.user_password)) + return TRUE; + } + } + plog("xauth user '%.*s' %s" + , xauth_secret->user_name.len, xauth_secret->user_name.ptr + , found? "sent wrong password":"not found"); + return FALSE; +} + +/* + * the global xauth_module struct is defined here + */ +xauth_module_t xauth_module; + +/* + * assign the default xauth functions to any null function pointers + */ +void +xauth_defaults(void) +{ + if (xauth_module.get_secret == NULL) + { + DBG(DBG_CONTROL, + DBG_log("xauth module: using default get_secret() function") + ) + xauth_module.get_secret = xauth_get_secret; + } + if (xauth_module.verify_secret == NULL) + { + DBG(DBG_CONTROL, + DBG_log("xauth module: using default verify_secret() function") + ) + xauth_module.verify_secret = xauth_verify_secret; + } +}; + +/* + * process pin read from ipsec.secrets or prompted for it using whack + */ +static err_t +process_pin(secret_t *s, int whackfd) +{ + smartcard_t *sc; + const char *pin_status = "no pin"; + + s->kind = PPK_PIN; + + /* looking for the smartcard keyword */ + if (!shift() || strncmp(tok, SCX_TOKEN, strlen(SCX_TOKEN)) != 0) + return "PIN keyword must be followed by %smartcard:"; + + sc = scx_add(scx_parse_number_slot_id(tok + strlen(SCX_TOKEN))); + s->u.smartcard = sc; + scx_share(sc); + if (sc->pin.ptr != NULL) + { + scx_release_context(sc); + scx_free_pin(&sc->pin); + } + sc->valid = FALSE; + + if (!shift()) + return "PIN statement must be terminated either by , %pinpad or %prompt"; + + if (tokeqword("%prompt")) + { + shift(); + /* if whackfd exists, whack will be used to prompt for a pin */ + if (whackfd != NULL_FD) + pin_status = scx_get_pin(sc, whackfd) ? "valid pin" : "invalid pin"; + else + pin_status = "pin entry via prompt"; + } + else if (tokeqword("%pinpad")) + { + shift(); + /* pin will be entered via pin pad during verification */ + clonetochunk(sc->pin, "", 0, "empty pin"); + sc->pinpad = TRUE; + sc->valid = TRUE; + pin_status = "pin entry via pad"; + if (pkcs11_keep_state) + scx_verify_pin(sc); + } + else + { + /* we read the pin directly from ipsec.secrets */ + err_t ugh = process_psk_secret(&sc->pin); + if (ugh != NULL) + return ugh; + /* verify the pin */ + pin_status = scx_verify_pin(sc) ? "valid PIN" : "invalid PIN"; + } +#ifdef SMARTCARD + { + char buf[BUF_LEN]; + + if (sc->any_slot) + snprintf(buf, BUF_LEN, "any slot"); + else + snprintf(buf, BUF_LEN, "slot: %lu", sc->slot); + + plog(" %s for #%d (%s, id: %s)" + , pin_status, sc->number, scx_print_slot(sc, ""), sc->id); + } +#else + plog(" warning: SMARTCARD support is deactivated in pluto/Makefile!"); +#endif + return NULL; +} + +static void +process_secret(secret_t *s, int whackfd) +{ + err_t ugh = NULL; + + s->kind = PPK_PSK; /* default */ + if (*tok == '"' || *tok == '\'') + { + /* old PSK format: just a string */ + ugh = process_psk_secret(&s->u.preshared_secret); + } + else if (tokeqword("psk")) + { + /* preshared key: quoted string or ttodata format */ + ugh = !shift()? "unexpected end of record in PSK" + : process_psk_secret(&s->u.preshared_secret); + } + else if (tokeqword("rsa")) + { + /* RSA key: the fun begins. + * A braced list of keyword and value pairs. + */ + s->kind = PPK_RSA; + if (!shift()) + { + ugh = "bad RSA key syntax"; + } + else if (tokeq("{")) + { + ugh = process_rsa_secret(&s->u.RSA_private_key); + } + else + { + ugh = process_rsa_keyfile(&s->u.RSA_private_key, whackfd); + } + } + else if (tokeqword("xauth")) + { + ugh = process_xauth(s); + } + else if (tokeqword("pin")) + { + ugh = process_pin(s, whackfd); + } + else + { + ugh = builddiag("unrecognized key format: %s", tok); + } + + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s" + , flp->filename, flp->lino, ugh); + pfree(s); + } + else if (flushline("expected record boundary in key")) + { + /* gauntlet has been run: install new secret */ + lock_certs_and_keys("process_secret"); + s->next = secrets; + secrets = s; + unlock_certs_and_keys("process_secrets"); + } +} + +static void process_secrets_file(const char *file_pat, int whackfd); /* forward declaration */ + +static void +process_secret_records(int whackfd) +{ + /* read records from ipsec.secrets and load them into our table */ + for (;;) + { + (void)flushline(NULL); /* silently ditch leftovers, if any */ + if (flp->bdry == B_file) + break; + + flp->bdry = B_none; /* eat the Record Boundary */ + (void)shift(); /* get real first token */ + + if (tokeqword("include")) + { + /* an include directive */ + char fn[MAX_TOK_LEN]; /* space for filename (I hope) */ + char *p = fn; + char *end_prefix = strrchr(flp->filename, '/'); + + if (!shift()) + { + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end of include directive" + , flp->filename, flp->lino); + continue; /* abandon this record */ + } + + /* if path is relative and including file's pathname has + * a non-empty dirname, prefix this path with that dirname. + */ + if (tok[0] != '/' && end_prefix != NULL) + { + size_t pl = end_prefix - flp->filename + 1; + + /* "clamp" length to prevent problems now; + * will be rediscovered and reported later. + */ + if (pl > sizeof(fn)) + pl = sizeof(fn); + memcpy(fn, flp->filename, pl); + p += pl; + } + if (flp->cur - tok >= &fn[sizeof(fn)] - p) + { + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: include pathname too long" + , flp->filename, flp->lino); + continue; /* abandon this record */ + } + strcpy(p, tok); + (void) shift(); /* move to Record Boundary, we hope */ + if (flushline("ignoring malformed INCLUDE -- expected Record Boundary after filename")) + { + process_secrets_file(fn, whackfd); + tok = NULL; /* correct, but probably redundant */ + } + } + else + { + /* expecting a list of indices and then the key info */ + secret_t *s = alloc_thing(secret_t, "secret"); + + s->ids = NULL; + s->kind = PPK_PSK; /* default */ + setchunk(s->u.preshared_secret, NULL, 0); + s->next = NULL; + + for (;;) + { + if (tok[0] == '"' || tok[0] == '\'') + { + /* found key part */ + process_secret(s, whackfd); + break; + } + else if (tokeq(":")) + { + /* found key part */ + shift(); /* discard explicit separator */ + process_secret(s, whackfd); + break; + } + else + { + /* an id + * See RFC2407 IPsec Domain of Interpretation 4.6.2 + */ + struct id id; + err_t ugh; + + if (tokeq("%any")) + { + id = empty_id; + id.kind = ID_IPV4_ADDR; + ugh = anyaddr(AF_INET, &id.ip_addr); + } + else if (tokeq("%any6")) + { + id = empty_id; + id.kind = ID_IPV6_ADDR; + ugh = anyaddr(AF_INET6, &id.ip_addr); + } + else + { + ugh = atoid(tok, &id, FALSE); + } + + if (ugh != NULL) + { + loglog(RC_LOG_SERIOUS + , "ERROR \"%s\" line %d: index \"%s\" %s" + , flp->filename, flp->lino, tok, ugh); + } + else + { + id_list_t *i = alloc_thing(id_list_t + , "id_list"); + + i->id = id; + unshare_id_content(&i->id); + i->next = s->ids; + s->ids = i; + /* DBG_log("id type %d: %s %.*s", i->kind, ip_str(&i->ip_addr), (int)i->name.len, i->name.ptr); */ + } + if (!shift()) + { + /* unexpected Record Boundary or EOF */ + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end of id list" + , flp->filename, flp->lino); + break; + } + } + } + } + } +} + +static int +globugh(const char *epath, int eerrno) +{ + log_errno_routine(eerrno, "problem with secrets file \"%s\"", epath); + return 1; /* stop glob */ +} + +static void +process_secrets_file(const char *file_pat, int whackfd) +{ + struct file_lex_position pos; + char **fnp; + glob_t globbuf; + + pos.depth = flp == NULL? 0 : flp->depth + 1; + + if (pos.depth > 10) + { + loglog(RC_LOG_SERIOUS, "preshared secrets file \"%s\" nested too deeply", file_pat); + return; + } + + /* do globbing */ + { + int r = glob(file_pat, GLOB_ERR, globugh, &globbuf); + + if (r != 0) + { + switch (r) + { + case GLOB_NOSPACE: + loglog(RC_LOG_SERIOUS, "out of space processing secrets filename \"%s\"", file_pat); + break; + case GLOB_ABORTED: + break; /* already logged */ + case GLOB_NOMATCH: + loglog(RC_LOG_SERIOUS, "no secrets filename matched \"%s\"", file_pat); + break; + default: + loglog(RC_LOG_SERIOUS, "unknown glob error %d", r); + break; + } + globfree(&globbuf); + return; + } + } + + /* for each file... */ + for (fnp = globbuf.gl_pathv; *fnp != NULL; fnp++) + { + if (lexopen(&pos, *fnp, FALSE)) + { + plog("loading secrets from \"%s\"", *fnp); + (void) flushline("file starts with indentation (continuation notation)"); + process_secret_records(whackfd); + lexclose(); + } + } + + globfree(&globbuf); +} + +void +free_preshared_secrets(void) +{ + lock_certs_and_keys("free_preshared_secrets"); + + if (secrets != NULL) + { + secret_t *s, *ns; + + plog("forgetting secrets"); + + for (s = secrets; s != NULL; s = ns) + { + id_list_t *i, *ni; + + ns = s->next; /* grab before freeing s */ + for (i = s->ids; i != NULL; i = ni) + { + ni = i->next; /* grab before freeing i */ + free_id_content(&i->id); + pfree(i); + } + switch (s->kind) + { + case PPK_PSK: + pfree(s->u.preshared_secret.ptr); + break; + case PPK_RSA: + free_RSA_private_content(&s->u.RSA_private_key); + break; + case PPK_XAUTH: + pfree(s->u.xauth_secret.user_name.ptr); + pfree(s->u.xauth_secret.user_password.ptr); + break; + case PPK_PIN: + scx_release(s->u.smartcard); + break; + default: + bad_case(s->kind); + } + pfree(s); + } + secrets = NULL; + } + + unlock_certs_and_keys("free_preshard_secrets"); +} + +void +load_preshared_secrets(int whackfd) +{ + free_preshared_secrets(); + (void) process_secrets_file(shared_secrets_file, whackfd); +} + +/* public key machinery + * Note: caller must set dns_auth_level. + */ + +pubkey_t * +public_key_from_rsa(const RSA_public_key_t *k) +{ + pubkey_t *p = alloc_thing(pubkey_t, "pubkey"); + + p->id = empty_id; /* don't know, doesn't matter */ + p->issuer = empty_chunk; + p->serial = empty_chunk; + p->alg = PUBKEY_ALG_RSA; + + memcpy(p->u.rsa.keyid, k->keyid, sizeof(p->u.rsa.keyid)); + p->u.rsa.k = k->k; + mpz_init_set(&p->u.rsa.e, &k->e); + mpz_init_set(&p->u.rsa.n, &k->n); + + /* note that we return a 1 reference count upon creation: + * invariant: recount > 0. + */ + p->refcnt = 1; + time(&p->installed_time); + return p; +} + +/* Free a public key record. + * As a convenience, this returns a pointer to next. + */ +pubkey_list_t * +free_public_keyentry(pubkey_list_t *p) +{ + pubkey_list_t *nxt = p->next; + + if (p->key != NULL) + unreference_key(&p->key); + pfree(p); + return nxt; +} + +void +free_public_keys(pubkey_list_t **keys) +{ + while (*keys != NULL) + *keys = free_public_keyentry(*keys); +} + +/* root of chained public key list */ + +pubkey_list_t *pubkeys = NULL; /* keys from ipsec.conf */ + +void +free_remembered_public_keys(void) +{ + free_public_keys(&pubkeys); +} + +/* transfer public keys from *keys list to front of pubkeys list */ +void +transfer_to_public_keys(struct gw_info *gateways_from_dns +#ifdef USE_KEYRR +, pubkey_list_t **keys +#endif /* USE_KEYRR */ +) +{ + { + struct gw_info *gwp; + + for (gwp = gateways_from_dns; gwp != NULL; gwp = gwp->next) + { + pubkey_list_t *pl = alloc_thing(pubkey_list_t, "from TXT"); + + pl->key = gwp->key; /* note: this is a transfer */ + gwp->key = NULL; /* really, it is! */ + pl->next = pubkeys; + pubkeys = pl; + } + } + +#ifdef USE_KEYRR + { + pubkey_list_t **pp = keys; + + while (*pp != NULL) + pp = &(*pp)->next; + *pp = pubkeys; + pubkeys = *keys; + *keys = NULL; + } +#endif /* USE_KEYRR */ +} + +/* decode of RSA pubkey chunk + * - format specified in RFC 2537 RSA/MD5 Keys and SIGs in the DNS + * - exponent length in bytes (1 or 3 octets) + * + 1 byte if in [1, 255] + * + otherwise 0x00 followed by 2 bytes of length + * - exponent + * - modulus + */ +err_t +unpack_RSA_public_key(RSA_public_key_t *rsa, const chunk_t *pubkey) +{ + chunk_t exp; + chunk_t mod; + + if (pubkey->len < 3) + return "RSA public key blob way to short"; /* not even room for length! */ + + if (pubkey->ptr[0] != 0x00) + { + setchunk(exp, pubkey->ptr + 1, pubkey->ptr[0]); + } + else + { + setchunk(exp, pubkey->ptr + 3 + , (pubkey->ptr[1] << BITS_PER_BYTE) + pubkey->ptr[2]); + } + + if (pubkey->len - (exp.ptr - pubkey->ptr) < exp.len + RSA_MIN_OCTETS_RFC) + return "RSA public key blob too short"; + + mod.ptr = exp.ptr + exp.len; + mod.len = &pubkey->ptr[pubkey->len] - mod.ptr; + + if (mod.len < RSA_MIN_OCTETS) + return RSA_MIN_OCTETS_UGH; + + if (mod.len > RSA_MAX_OCTETS) + return RSA_MAX_OCTETS_UGH; + + init_RSA_public_key(rsa, exp, mod); + rsa->k = mpz_sizeinbase(&rsa->n, 2); /* size in bits, for a start */ + rsa->k = (rsa->k + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* now octets */ + DBG(DBG_RAW, + RSA_show_public_key(rsa) + ) + + if (rsa->k != mod.len) + { + mpz_clear(&rsa->e); + mpz_clear(&rsa->n); + return "RSA modulus shorter than specified"; + } + + return NULL; +} + +static void +install_public_key(pubkey_t *pk, pubkey_list_t **head) +{ + pubkey_list_t *p = alloc_thing(pubkey_list_t, "pubkey entry"); + + unshare_id_content(&pk->id); + + /* copy issuer dn */ + if (pk->issuer.ptr != NULL) + pk->issuer.ptr = clone_bytes(pk->issuer.ptr, pk->issuer.len, "issuer dn"); + + /* copy serial number */ + if (pk->serial.ptr != NULL) + pk->serial.ptr = clone_bytes(pk->serial.ptr, pk->serial.len, "serialNumber"); + + /* store the time the public key was installed */ + time(&pk->installed_time); + + /* install new key at front */ + p->key = reference_key(pk); + p->next = *head; + *head = p; +} + + +void +delete_public_keys(const struct id *id, enum pubkey_alg alg +, chunk_t issuer, chunk_t serial) +{ + pubkey_list_t **pp, *p; + pubkey_t *pk; + + for (pp = &pubkeys; (p = *pp) != NULL; ) + { + pk = p->key; + + if (same_id(id, &pk->id) && pk->alg == alg + && (issuer.ptr == NULL || pk->issuer.ptr == NULL + || same_dn(issuer, pk->issuer)) + && same_serial(serial, pk->serial)) + *pp = free_public_keyentry(p); + else + pp = &p->next; + } +} + +pubkey_t * +reference_key(pubkey_t *pk) +{ + pk->refcnt++; + return pk; +} + +void +unreference_key(pubkey_t **pkp) +{ + pubkey_t *pk = *pkp; + char b[BUF_LEN]; + + if (pk == NULL) + return; + + /* print stuff */ + DBG(DBG_CONTROLMORE, + idtoa(&pk->id, b, sizeof(b)); + DBG_log("unreference key: %p %s cnt %d--", pk, b, pk->refcnt) + ) + + /* cancel out the pointer */ + *pkp = NULL; + + passert(pk->refcnt != 0); + pk->refcnt--; + if (pk->refcnt == 0) + free_public_key(pk); +} + +err_t +add_public_key(const struct id *id +, enum dns_auth_level dns_auth_level +, enum pubkey_alg alg +, const chunk_t *key +, pubkey_list_t **head) +{ + pubkey_t *pk = alloc_thing(pubkey_t, "pubkey"); + + /* first: algorithm-specific decoding of key chunk */ + switch (alg) + { + case PUBKEY_ALG_RSA: + { + err_t ugh = unpack_RSA_public_key(&pk->u.rsa, key); + + if (ugh != NULL) + { + pfree(pk); + return ugh; + } + } + break; + default: + bad_case(alg); + } + + pk->id = *id; + pk->dns_auth_level = dns_auth_level; + pk->alg = alg; + pk->until_time = UNDEFINED_TIME; + pk->issuer = empty_chunk; + pk->serial = empty_chunk; + + install_public_key(pk, head); + return NULL; +} + +/* extract id and public key from x.509 certificate and + * insert it into a pubkeyrec + */ +void +add_x509_public_key(x509cert_t *cert , time_t until + , enum dns_auth_level dns_auth_level) +{ + generalName_t *gn; + pubkey_t *pk; + cert_t c = { CERT_X509_SIGNATURE, {cert} }; + + /* we support RSA only */ + if (cert->subjectPublicKeyAlgorithm != PUBKEY_ALG_RSA) + return; + + /* ID type: ID_DER_ASN1_DN (X.509 subject field) */ + pk = allocate_RSA_public_key(c); + pk->id.kind = ID_DER_ASN1_DN; + pk->id.name = cert->subject; + pk->dns_auth_level = dns_auth_level; + pk->until_time = until; + pk->issuer = cert->issuer; + pk->serial = cert->serialNumber; + delete_public_keys(&pk->id, pk->alg, pk->issuer, pk->serial); + install_public_key(pk, &pubkeys); + + gn = cert->subjectAltName; + + while (gn != NULL) /* insert all subjectAltNames */ + { + struct id id = empty_id; + + gntoid(&id, gn); + if (id.kind != ID_NONE) + { + pk = allocate_RSA_public_key(c); + pk->id = id; + pk->dns_auth_level = dns_auth_level; + pk->until_time = until; + pk->issuer = cert->issuer; + pk->serial = cert->serialNumber; + delete_public_keys(&pk->id, pk->alg, pk->issuer, pk->serial); + install_public_key(pk, &pubkeys); + } + gn = gn->next; + } +} + +/* extract id and public key from OpenPGP certificate and + * insert it into a pubkeyrec + */ +void +add_pgp_public_key(pgpcert_t *cert , time_t until + , enum dns_auth_level dns_auth_level) +{ + pubkey_t *pk; + cert_t c; + + c.type = CERT_PGP; + c.u.pgp = cert; + + /* we support RSA only */ + if (cert->pubkeyAlg != PUBKEY_ALG_RSA) + { + plog(" RSA public keys supported only"); + return; + } + + pk = allocate_RSA_public_key(c); + pk->id.kind = ID_KEY_ID; + pk->id.name.ptr = cert->fingerprint; + pk->id.name.len = PGP_FINGERPRINT_SIZE; + pk->dns_auth_level = dns_auth_level; + pk->until_time = until; + delete_public_keys(&pk->id, pk->alg, empty_chunk, empty_chunk); + install_public_key(pk, &pubkeys); +} + +/* when a X.509 certificate gets revoked, all instances of + * the corresponding public key must be removed + */ +void +remove_x509_public_key(const x509cert_t *cert) +{ + const cert_t c = {CERT_X509_SIGNATURE, {cert}}; + pubkey_list_t *p, **pp; + pubkey_t *revoked_pk; + + revoked_pk = allocate_RSA_public_key(c); + p = pubkeys; + pp = &pubkeys; + + while(p != NULL) + { + if (same_RSA_public_key(&p->key->u.rsa, &revoked_pk->u.rsa)) + { + /* remove p from list and free memory */ + *pp = free_public_keyentry(p); + loglog(RC_LOG_SERIOUS, + "invalid RSA public key deleted"); + } + else + { + pp = &p->next; + } + p =*pp; + } + free_public_key(revoked_pk); +} + +/* + * list all public keys in the chained list + */ +void list_public_keys(bool utc) +{ + pubkey_list_t *p = pubkeys; + + if (p != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of Public Keys:"); + whack_log(RC_COMMENT, " "); + } + + while (p != NULL) + { + pubkey_t *key = p->key; + + if (key->alg == PUBKEY_ALG_RSA) + { + char buf[BUF_LEN]; + char expires_buf[TIMETOA_BUF]; + + idtoa(&key->id, buf, BUF_LEN); + strcpy(expires_buf, timetoa(&key->until_time, utc)); + whack_log(RC_COMMENT, "%s, %4d RSA Key %s, until %s %s", + + timetoa(&key->installed_time, utc), 8*key->u.rsa.k, key->u.rsa.keyid, + expires_buf, + check_expiry(key->until_time, PUBKEY_WARNING_INTERVAL, TRUE)); + whack_log(RC_COMMENT," %s '%s'", + enum_show(&ident_names, key->id.kind), buf); + if (key->issuer.len > 0) + { + dntoa(buf, BUF_LEN, key->issuer); + whack_log(RC_COMMENT," issuer: '%s'", buf); + } + if (key->serial.len > 0) + { + datatot(key->serial.ptr, key->serial.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT," serial: %s", buf); + } + } + p = p->next; + } +} diff --git a/src/pluto/keys.h b/src/pluto/keys.h new file mode 100644 index 000000000..415bdc3c1 --- /dev/null +++ b/src/pluto/keys.h @@ -0,0 +1,113 @@ +/* mechanisms for preshared keys (public, private, and preshared secrets) + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: keys.h,v 1.7 2006/01/26 20:10:34 as Exp $ + */ + +#ifndef _KEYS_H +#define _KEYS_H + +#include /* GNU Multi-Precision library */ + +#include "pkcs1.h" +#include "certs.h" + +#ifndef SHARED_SECRETS_FILE +# define SHARED_SECRETS_FILE IPSEC_CONFDIR "/ipsec.secrets" +#endif + +const char *shared_secrets_file; + +extern void load_preshared_secrets(int whackfd); +extern void free_preshared_secrets(void); + +enum PrivateKeyKind { + PPK_PSK, + /* PPK_DSS, */ /* not implemented */ + PPK_RSA, + PPK_XAUTH, + PPK_PIN +}; + +extern void xauth_defaults(void); + +/* forward declaration */ +struct connection; + +extern const chunk_t *get_preshared_secret(const struct connection *c); +extern err_t unpack_RSA_public_key(RSA_public_key_t *rsa, const chunk_t *pubkey); +extern const RSA_private_key_t *get_RSA_private_key(const struct connection *c); +extern const RSA_private_key_t *get_x509_private_key(const x509cert_t *cert); + +/* public key machinery */ + +typedef struct pubkey pubkey_t; + +struct pubkey { + struct id id; + unsigned refcnt; /* reference counted! */ + enum dns_auth_level dns_auth_level; + char *dns_sig; + time_t installed_time + , last_tried_time + , last_worked_time + , until_time; + chunk_t issuer; + chunk_t serial; + enum pubkey_alg alg; + union { + RSA_public_key_t rsa; + } u; +}; + +typedef struct pubkey_list pubkey_list_t; + +struct pubkey_list { + pubkey_t *key; + pubkey_list_t *next; +}; + +extern pubkey_list_t *pubkeys; /* keys from ipsec.conf or from certs */ + +extern pubkey_t *public_key_from_rsa(const RSA_public_key_t *k); +extern pubkey_list_t *free_public_keyentry(pubkey_list_t *p); +extern void free_public_keys(pubkey_list_t **keys); +extern void free_remembered_public_keys(void); +extern void delete_public_keys(const struct id *id, enum pubkey_alg alg + , chunk_t issuer, chunk_t serial); + +extern pubkey_t *reference_key(pubkey_t *pk); +extern void unreference_key(pubkey_t **pkp); + +extern err_t add_public_key(const struct id *id + , enum dns_auth_level dns_auth_level + , enum pubkey_alg alg + , const chunk_t *key + , pubkey_list_t **head); + +extern bool has_private_key(cert_t cert); +extern void add_x509_public_key(x509cert_t *cert, time_t until + , enum dns_auth_level dns_auth_level); +extern void add_pgp_public_key(pgpcert_t *cert, time_t until + , enum dns_auth_level dns_auth_level); +extern void remove_x509_public_key(const x509cert_t *cert); +extern void list_public_keys(bool utc); + +struct gw_info; /* forward declaration of tag (defined in dnskey.h) */ +extern void transfer_to_public_keys(struct gw_info *gateways_from_dns +#ifdef USE_KEYRR + , pubkey_list_t **keys +#endif /* USE_KEYRR */ + ); + +#endif /* _KEYS_H */ diff --git a/src/pluto/lex.c b/src/pluto/lex.c new file mode 100644 index 000000000..5c811725a --- /dev/null +++ b/src/pluto/lex.c @@ -0,0 +1,213 @@ +/* lexer (lexical analyzer) for control files + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: lex.c,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ +#include "lex.h" + +struct file_lex_position *flp = NULL; + +/* Open a file for lexical processing. + * new_flp and name must point into storage with will live + * at least until the file is closed. + */ +bool +lexopen(struct file_lex_position *new_flp, const char *name, bool optional) +{ + FILE *f = fopen(name, "r"); + + if (f == NULL) + { + if (!optional || errno != ENOENT) + log_errno((e, "could not open \"%s\"", name)); + return FALSE; + } + else + { + new_flp->previous = flp; + flp = new_flp; + flp->filename = name; + flp->fp = f; + flp->lino = 0; + flp->bdry = B_none; + + flp->cur = flp->buffer; /* nothing loaded yet */ + flp->under = *flp->cur = '\0'; + + (void) shift(); /* prime tok */ + return TRUE; + } +} + +void +lexclose(void) +{ + fclose(flp->fp); + flp = flp->previous; +} + +/* Token decoding: shift() loads the next token into tok. + * Iff a token starts at the left margin, it is considered + * to be the first in a record. We create a special condition, + * Record Boundary (analogous to EOF), just before such a token. + * We are unwilling to shift through a record boundary: + * it must be overridden first. + * Returns FALSE iff Record Boundary or EOF (i.e. no token); + * tok will then be NULL. + */ + +char *tok; +#define tokeq(s) (streq(tok, (s))) +#define tokeqword(s) (strcasecmp(tok, (s)) == 0) + +bool +shift(void) +{ + char *p = flp->cur; + char *sor = NULL; /* start of record for any new lines */ + + passert(flp->bdry == B_none); + + *p = flp->under; + flp->under = '\0'; + + for (;;) + { + switch (*p) + { + case '\0': /* end of line */ + case '#': /* comment to end of line: treat as end of line */ + /* get the next line */ + if (fgets(flp->buffer, sizeof(flp->buffer)-1, flp->fp) == NULL) + { + flp->bdry = B_file; + tok = flp->cur = NULL; + return FALSE; + } + else + { + /* strip trailing whitespace, including \n */ + + for (p = flp->buffer+strlen(flp->buffer)-1 + ; p>flp->buffer && isspace(p[-1]); p--) + ; + *p = '\0'; + + flp->lino++; + sor = p = flp->buffer; + } + break; /* try again for a token */ + + case ' ': /* whitespace */ + case '\t': + p++; + break; /* try again for a token */ + + case '"': /* quoted token */ + case '\'': + if (p != sor) + { + /* we have a quoted token: note and advance to its end */ + tok = p; + p = strchr(p+1, *p); + if (p == NULL) + { + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unterminated string" + , flp->filename, flp->lino); + p = tok + strlen(tok); + } + else + { + p++; /* include delimiter in token */ + } + + /* remember token delimiter and replace with '\0' */ + flp->under = *p; + *p = '\0'; + flp->cur = p; + return TRUE; + } + /* FALL THROUGH */ + default: + if (p != sor) + { + /* we seem to have a token: note and advance to its end */ + tok = p; + + if (p[0] == '0' && p[1] == 't') + { + /* 0t... token goes to end of line */ + p += strlen(p); + } + else + { + /* "ordinary" token: up to whitespace or end of line */ + do { + p++; + } while (*p != '\0' && !isspace(*p)) + ; + + /* fudge to separate ':' from a preceding adjacent token */ + if (p-1 > tok && p[-1] == ':') + p--; + } + + /* remember token delimiter and replace with '\0' */ + flp->under = *p; + *p = '\0'; + flp->cur = p; + return TRUE; + } + + /* we have a start-of-record: return it, deferring "real" token */ + flp->bdry = B_record; + tok = NULL; + flp->under = *p; + flp->cur = p; + return FALSE; + } + } +} + +/* ensures we are at a Record (or File) boundary, optionally warning if not */ + +bool +flushline(const char *m) +{ + if (flp->bdry != B_none) + { + return TRUE; + } + else + { + if (m != NULL) + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s", flp->filename, flp->lino, m); + do ; while (shift()); + return FALSE; + } +} diff --git a/src/pluto/lex.h b/src/pluto/lex.h new file mode 100644 index 000000000..fb6c15236 --- /dev/null +++ b/src/pluto/lex.h @@ -0,0 +1,52 @@ +/* lexer (lexical analyzer) for control files + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: lex.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +#define MAX_TOK_LEN 2048 /* includes terminal '\0' */ +struct file_lex_position +{ + int depth; /* how deeply we are nested */ + const char *filename; + FILE *fp; + enum { B_none, B_record, B_file } bdry; /* current boundary */ + int lino; /* line number in file */ + char buffer[MAX_TOK_LEN + 1]; /* note: one extra char for our use (jamming '"') */ + char *cur; /* cursor */ + char under; /* except in shift(): character orignally at *cur */ + struct file_lex_position *previous; +}; + +extern struct file_lex_position *flp; + +extern bool lexopen(struct file_lex_position *new_flp, const char *name, bool optional); +extern void lexclose(void); + + +/* Token decoding: shift() loads the next token into tok. + * Iff a token starts at the left margin, it is considered + * to be the first in a record. We create a special condition, + * Record Boundary (analogous to EOF), just before such a token. + * We are unwilling to shift through a record boundary: + * it must be overridden first. + * Returns FALSE iff Record Boundary or EOF (i.e. no token); + * tok will then be NULL. + */ + +extern char *tok; +#define tokeq(s) (streq(tok, (s))) +#define tokeqword(s) (strcasecmp(tok, (s)) == 0) + +extern bool shift(void); +extern bool flushline(const char *m); diff --git a/src/pluto/linux26/netlink.h b/src/pluto/linux26/netlink.h new file mode 100644 index 000000000..6b0896da6 --- /dev/null +++ b/src/pluto/linux26/netlink.h @@ -0,0 +1,90 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include +#include /* for sa_family_t */ + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_SKIP 1 /* Reserved for ENskip */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_TCPDIAG 4 /* TCP socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_ARPD 8 +#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */ + +#define MAX_LINKS 32 + +struct sockaddr_nl +{ + sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + uint32_t nl_pid; /* process pid */ + uint32_t nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr +{ + uint32_t nlmsg_len; /* Length of message including header */ + uint16_t nlmsg_type; /* Message content */ + uint16_t nlmsg_flags; /* Additional flags */ + uint32_t nlmsg_seq; /* Sequence number */ + uint32_t nlmsg_pid; /* Sending process PID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 1 /* It is request message. */ +#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 8 /* Echo this request */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) > 0 && (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +struct nlmsgerr +{ + int error; + struct nlmsghdr msg; +}; + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ +#endif /* __LINUX_NETLINK_H */ diff --git a/src/pluto/linux26/rtnetlink.h b/src/pluto/linux26/rtnetlink.h new file mode 100644 index 000000000..341bc1f86 --- /dev/null +++ b/src/pluto/linux26/rtnetlink.h @@ -0,0 +1,562 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include "netlink.h" +#include + +#define RTNL_DEBUG 1 + + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +#define RTM_BASE 0x10 + +#define RTM_NEWLINK (RTM_BASE+0) +#define RTM_DELLINK (RTM_BASE+1) +#define RTM_GETLINK (RTM_BASE+2) +#define RTM_SETLINK (RTM_BASE+3) + +#define RTM_NEWADDR (RTM_BASE+4) +#define RTM_DELADDR (RTM_BASE+5) +#define RTM_GETADDR (RTM_BASE+6) + +#define RTM_NEWROUTE (RTM_BASE+8) +#define RTM_DELROUTE (RTM_BASE+9) +#define RTM_GETROUTE (RTM_BASE+10) + +#define RTM_NEWNEIGH (RTM_BASE+12) +#define RTM_DELNEIGH (RTM_BASE+13) +#define RTM_GETNEIGH (RTM_BASE+14) + +#define RTM_NEWRULE (RTM_BASE+16) +#define RTM_DELRULE (RTM_BASE+17) +#define RTM_GETRULE (RTM_BASE+18) + +#define RTM_NEWQDISC (RTM_BASE+20) +#define RTM_DELQDISC (RTM_BASE+21) +#define RTM_GETQDISC (RTM_BASE+22) + +#define RTM_NEWTCLASS (RTM_BASE+24) +#define RTM_DELTCLASS (RTM_BASE+25) +#define RTM_GETTCLASS (RTM_BASE+26) + +#define RTM_NEWTFILTER (RTM_BASE+28) +#define RTM_DELTFILTER (RTM_BASE+29) +#define RTM_GETTFILTER (RTM_BASE+30) + +#define RTM_MAX (RTM_BASE+31) + +/* + Generic structure for encapsulation optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administation. + ****/ + +struct rtmsg +{ + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum +{ + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ +}; + +#define RTN_MAX RTN_XRESOLVE + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisments */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t +{ + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ + +/* Reserved table identifiers */ + +enum rt_class_t +{ + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255 +}; +#define RT_TABLE_MAX RT_TABLE_LOCAL + + + +/* Routing message attributes */ + +enum rtattr_type_t +{ + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, +}; + +#define RTA_MAX RTA_SESSION + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describres all necessary nexthop information, + * i.e. parameters of path to a destination via this nextop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop +{ + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo +{ + uint32_t rta_clntref; + uint32_t rta_lastuse; + int32_t rta_expires; + uint32_t rta_error; + uint32_t rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + uint32_t rta_id; + uint32_t rta_ts; + uint32_t rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum +{ + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING +}; + +#define RTAX_MAX RTAX_REORDERING + +struct rta_session +{ + uint8_t proto; + + union { + struct { + uint16_t sport; + uint16_t dport; + } ports; + + struct { + uint8_t type; + uint8_t code; + uint16_t ident; + } icmpt; + + uint32_t spi; + } u; +}; + + +/********************************************************* + * Interface address. + ****/ + +struct ifaddrmsg +{ + unsigned char ifa_family; + unsigned char ifa_prefixlen; /* The prefix length */ + unsigned char ifa_flags; /* Flags */ + unsigned char ifa_scope; /* See above */ + int ifa_index; /* Link index */ +}; + +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO +}; + +#define IFA_MAX IFA_CACHEINFO + +/* ifa_flags */ + +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + int32_t ifa_prefered; + int32_t ifa_valid; +}; + + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +/* + Important comment: + IFA_ADDRESS is prefix address, rather than local interface address. + It makes no difference for normally configured broadcast interfaces, + but for point-to-point IFA_ADDRESS is DESTINATION address, + local address is supplied in IFA_LOCAL attribute. + */ + +/************************************************************** + * Neighbour discovery. + ****/ + +struct ndmsg +{ + unsigned char ndm_family; + unsigned char ndm_pad1; + unsigned short ndm_pad2; + int ndm_ifindex; /* Link index */ + uint16_t ndm_state; + uint8_t ndm_flags; + uint8_t ndm_type; +}; + +enum +{ + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO +}; + +#define NDA_MAX NDA_CACHEINFO + +#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + + +struct nda_cacheinfo +{ + uint32_t ndm_confirmed; + uint32_t ndm_used; + uint32_t ndm_updated; + uint32_t ndm_refcnt; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg +{ + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +enum +{ + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS +}; + + +#define IFLA_MAX IFLA_WIRELESS + +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neiher of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg +{ + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + uint32_t tcm_handle; + uint32_t tcm_parent; + uint32_t tcm_info; +}; + +enum +{ + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, +}; + +#define TCA_MAX TCA_RATE + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + + +/* SUMMARY: maximal rtattr understood by kernel */ + +#define RTATTR_MAX RTA_MAX + +/* RTnetlink multicast groups */ + +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +/* End of information exported to user level */ + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/src/pluto/linux26/xfrm.h b/src/pluto/linux26/xfrm.h new file mode 100644 index 000000000..4269ae29b --- /dev/null +++ b/src/pluto/linux26/xfrm.h @@ -0,0 +1,233 @@ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include + +/* All of the structures in this file may not change size as they are + * passed into the kernel from userspace via netlink sockets. + */ + +/* Structure to encapsulate addresses. I do not want to use + * "standard" structure. My apologies. + */ +typedef union +{ + uint32_t a4; + uint32_t a6[4]; +} xfrm_address_t; + +/* Ident of a specific xfrm_state. It is used on input to lookup + * the state by (spi,daddr,ah/esp) or to store information about + * spi, protocol and tunnel address on output. + */ +struct xfrm_id +{ + xfrm_address_t daddr; + uint32_t spi; + uint8_t proto; +}; + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ + +struct xfrm_selector +{ + xfrm_address_t daddr; + xfrm_address_t saddr; + uint16_t dport; + uint16_t dport_mask; + uint16_t sport; + uint16_t sport_mask; + uint16_t family; + uint8_t prefixlen_d; + uint8_t prefixlen_s; + uint8_t proto; + int ifindex; + uid_t user; +}; + +#define XFRM_INF (~(uint64_t)0) + +struct xfrm_lifetime_cfg +{ + uint64_t soft_byte_limit; + uint64_t hard_byte_limit; + uint64_t soft_packet_limit; + uint64_t hard_packet_limit; + uint64_t soft_add_expires_seconds; + uint64_t hard_add_expires_seconds; + uint64_t soft_use_expires_seconds; + uint64_t hard_use_expires_seconds; +}; + +struct xfrm_lifetime_cur +{ + uint64_t bytes; + uint64_t packets; + uint64_t add_time; + uint64_t use_time; +}; + +struct xfrm_replay_state +{ + uint32_t oseq; + uint32_t seq; + uint32_t bitmap; +}; + +struct xfrm_algo { + char alg_name[64]; + int alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_stats { + uint32_t replay_window; + uint32_t replay; + uint32_t integrity_failed; +}; + +enum +{ + XFRM_POLICY_IN = 0, + XFRM_POLICY_OUT = 1, + XFRM_POLICY_FWD = 2, + XFRM_POLICY_MAX = 3 +}; + +enum +{ + XFRM_SHARE_ANY, /* No limitations */ + XFRM_SHARE_SESSION, /* For this session only */ + XFRM_SHARE_USER, /* For this user only */ + XFRM_SHARE_UNIQUE /* Use once */ +}; + +/* Netlink configuration messages. */ +#define XFRM_MSG_BASE 0x10 + +#define XFRM_MSG_NEWSA (XFRM_MSG_BASE + 0) +#define XFRM_MSG_DELSA (XFRM_MSG_BASE + 1) +#define XFRM_MSG_GETSA (XFRM_MSG_BASE + 2) + +#define XFRM_MSG_NEWPOLICY (XFRM_MSG_BASE + 3) +#define XFRM_MSG_DELPOLICY (XFRM_MSG_BASE + 4) +#define XFRM_MSG_GETPOLICY (XFRM_MSG_BASE + 5) + +#define XFRM_MSG_ALLOCSPI (XFRM_MSG_BASE + 6) +#define XFRM_MSG_ACQUIRE (XFRM_MSG_BASE + 7) +#define XFRM_MSG_EXPIRE (XFRM_MSG_BASE + 8) + +#define XFRM_MSG_UPDPOLICY (XFRM_MSG_BASE + 9) +#define XFRM_MSG_UPDSA (XFRM_MSG_BASE + 10) + +#define XFRM_MSG_POLEXPIRE (XFRM_MSG_BASE + 11) + +#define XFRM_MSG_MAX (XFRM_MSG_POLEXPIRE+1) + +struct xfrm_user_tmpl { + struct xfrm_id id; + uint16_t family; + xfrm_address_t saddr; + uint32_t reqid; + uint8_t mode; + uint8_t share; + uint8_t optional; + uint32_t aalgos; + uint32_t ealgos; + uint32_t calgos; +}; + +struct xfrm_encap_tmpl { + uint16_t encap_type; + uint16_t encap_sport; + uint16_t encap_dport; + xfrm_address_t encap_oa; +}; + +/* Netlink message attributes. */ +enum xfrm_attr_type_t { + XFRMA_UNSPEC, + XFRMA_ALG_AUTH, /* struct xfrm_algo */ + XFRMA_ALG_CRYPT, /* struct xfrm_algo */ + XFRMA_ALG_COMP, /* struct xfrm_algo */ + XFRMA_ENCAP, /* struct xfrm_algo + struct xfrm_encap_tmpl */ + XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */ + +#define XFRMA_MAX XFRMA_TMPL +}; + +struct xfrm_usersa_info { + struct xfrm_selector sel; + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + struct xfrm_stats stats; + uint32_t seq; + uint32_t reqid; + uint16_t family; + uint8_t mode; /* 0=transport,1=tunnel */ + uint8_t replay_window; + uint8_t flags; +#define XFRM_STATE_NOECN 1 +}; + +struct xfrm_usersa_id { + xfrm_address_t daddr; + uint32_t spi; + uint16_t family; + uint8_t proto; +}; + +struct xfrm_userspi_info { + struct xfrm_usersa_info info; + uint32_t min; + uint32_t max; +}; + +struct xfrm_userpolicy_info { + struct xfrm_selector sel; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + uint32_t priority; + uint32_t index; + uint8_t dir; + uint8_t action; +#define XFRM_POLICY_ALLOW 0 +#define XFRM_POLICY_BLOCK 1 + uint8_t flags; +#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ + uint8_t share; +}; + +struct xfrm_userpolicy_id { + struct xfrm_selector sel; + uint32_t index; + uint8_t dir; +}; + +struct xfrm_user_acquire { + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_selector sel; + struct xfrm_userpolicy_info policy; + uint32_t aalgos; + uint32_t ealgos; + uint32_t calgos; + uint32_t seq; +}; + +struct xfrm_user_expire { + struct xfrm_usersa_info state; + uint8_t hard; +}; + +struct xfrm_user_polexpire { + struct xfrm_userpolicy_info pol; + uint8_t hard; +}; + +#define XFRMGRP_ACQUIRE 1 +#define XFRMGRP_EXPIRE 2 + +#endif /* _LINUX_XFRM_H */ diff --git a/src/pluto/log.c b/src/pluto/log.c new file mode 100644 index 000000000..36997122c --- /dev/null +++ b/src/pluto/log.c @@ -0,0 +1,841 @@ +/* error logging functions + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: log.c,v 1.8 2006/04/29 18:16:02 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* used only if MSG_NOSIGNAL not defined */ +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "server.h" +#include "state.h" +#include "connections.h" +#include "kernel.h" +#include "whack.h" /* needs connections.h */ +#include "timer.h" + +/* close one per-peer log */ +static void perpeer_logclose(struct connection *c); /* forward */ + + +bool + log_to_stderr = TRUE, /* should log go to stderr? */ + log_to_syslog = TRUE, /* should log go to syslog? */ + log_to_perpeer= FALSE; /* should log go to per-IP file? */ + +bool + logged_txt_warning = FALSE; /* should we complain about finding KEY? */ + +/* should we complain when we find no local id */ +bool + logged_myid_fqdn_txt_warning = FALSE, + logged_myid_ip_txt_warning = FALSE, + logged_myid_fqdn_key_warning = FALSE, + logged_myid_ip_key_warning = FALSE; + +/* may include trailing / */ +const char *base_perpeer_logdir = PERPEERLOGDIR; +static int perpeer_count = 0; + +/* from sys/queue.h */ +static CIRCLEQ_HEAD(,connection) perpeer_list; + + +/* Context for logging. + * + * Global variables: must be carefully adjusted at transaction boundaries! + * If the context provides a whack file descriptor, messages + * should be copied to it -- see whack_log() + */ +int whack_log_fd = NULL_FD; /* only set during whack_handle() */ +struct state *cur_state = NULL; /* current state, for diagnostics */ +struct connection *cur_connection = NULL; /* current connection, for diagnostics */ +const ip_address *cur_from = NULL; /* source of current current message */ +u_int16_t cur_from_port; /* host order */ + +void +init_log(const char *program) +{ + if (log_to_stderr) + setbuf(stderr, NULL); + if (log_to_syslog) + openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); + + CIRCLEQ_INIT(&perpeer_list); +} + +void +close_peerlog(void) +{ + /* end of circular queue is given by pointer to "HEAD" + * BUT if the queue is not initialized, this won't be true + * so we must guard by test perpeer_list.cqh_first != NULL + */ + if (perpeer_list.cqh_first != NULL) + while (perpeer_list.cqh_first != (void *)&perpeer_list) + perpeer_logclose(perpeer_list.cqh_first); +} + +void +close_log(void) +{ + if (log_to_syslog) + closelog(); + + close_peerlog(); +} + +/* Sanitize character string in situ: turns dangerous characters into \OOO. + * With a bit of work, we could use simpler reps for \\, \r, etc., + * but this is only to protect against something that shouldn't be used. + * Truncate resulting string to what fits in buffer. + */ +static size_t +sanitize(char *buf, size_t size) +{ +# define UGLY_WIDTH 4 /* width for ugly character: \OOO */ + size_t len; + size_t added = 0; + char *p; + + passert(size >= UGLY_WIDTH); /* need room to swing cat */ + + /* find right side of string to be sanitized and count + * number of columns to be added. Stop on end of string + * or lack of room for more result. + */ + for (p = buf; *p != '\0' && &p[added] < &buf[size - UGLY_WIDTH]; ) + { + unsigned char c = *p++; + + if (c == '\\' || !isprint(c)) + added += UGLY_WIDTH - 1; + } + + /* at this point, p points after last original character to be + * included. added is how many characters are added to sanitize. + * so p[added] will point after last sanitized character. + */ + + p[added] = '\0'; + len = &p[added] - buf; + + /* scan backwards, copying characters to their new home + * and inserting the expansions for ugly characters. + * It is finished when no more shifting is required. + * This is a predecrement loop. + */ + while (added != 0) + { + char fmtd[UGLY_WIDTH + 1]; + unsigned char c; + + while ((c = *--p) != '\\' && isprint(c)) + p[added] = c; + added -= UGLY_WIDTH - 1; + snprintf(fmtd, sizeof(fmtd), "\\%03o", c); + memcpy(p + added, fmtd, UGLY_WIDTH); + } + return len; +# undef UGLY_WIDTH +} + +/* format a string for the log, with suitable prefixes. + * A format starting with ~ indicates that this is a reprocessing + * of the message, so prefixing and quoting is suppressed. + */ +static void +fmt_log(char *buf, size_t buf_len, const char *fmt, va_list ap) +{ + bool reproc = *fmt == '~'; + size_t ps; + struct connection *c = cur_state != NULL ? cur_state->st_connection + : cur_connection; + + buf[0] = '\0'; + if (reproc) + fmt++; /* ~ at start of format suppresses this prefix */ + else if (c != NULL) + { + /* start with name of connection */ + char *const be = buf + buf_len; + char *bp = buf; + + snprintf(bp, be - bp, "\"%s\"", c->name); + bp += strlen(bp); + + /* if it fits, put in any connection instance information */ + if (be - bp > CONN_INST_BUF) + { + fmt_conn_instance(c, bp); + bp += strlen(bp); + } + + if (cur_state != NULL) + { + /* state number */ + snprintf(bp, be - bp, " #%lu", cur_state->st_serialno); + bp += strlen(bp); + } + snprintf(bp, be - bp, ": "); + } + else if (cur_from != NULL) + { + /* peer's IP address */ + /* Note: must not use ip_str() because our caller might! */ + char ab[ADDRTOT_BUF]; + + (void) addrtot(cur_from, 0, ab, sizeof(ab)); + snprintf(buf, buf_len, "packet from %s:%u: " + , ab, (unsigned)cur_from_port); + } + + ps = strlen(buf); + vsnprintf(buf + ps, buf_len - ps, fmt, ap); + if (!reproc) + (void)sanitize(buf, buf_len); +} + +static void +perpeer_logclose(struct connection *c) +{ + /* only free/close things if we had used them! */ + if (c->log_file != NULL) + { + passert(perpeer_count > 0); + + CIRCLEQ_REMOVE(&perpeer_list, c, log_link); + perpeer_count--; + fclose(c->log_file); + c->log_file=NULL; + } +} + +void +perpeer_logfree(struct connection *c) +{ + perpeer_logclose(c); + if (c->log_file_name != NULL) + { + pfree(c->log_file_name); + c->log_file_name = NULL; + c->log_file_err = FALSE; + } +} + +/* open the per-peer log */ +static void +open_peerlog(struct connection *c) +{ + syslog(LOG_INFO, "opening log file for conn %s", c->name); + + if (c->log_file_name == NULL) + { + char peername[ADDRTOT_BUF], dname[ADDRTOT_BUF]; + int peernamelen, lf_len; + + addrtot(&c->spd.that.host_addr, 'Q', peername, sizeof(peername)); + peernamelen = strlen(peername); + + /* copy IP address, turning : and . into / */ + { + char c, *p, *q; + + p = peername; + q = dname; + do { + c = *p++; + if (c == '.' || c == ':') + c = '/'; + *q++ = c; + } while (c != '\0'); + } + + lf_len = peernamelen * 2 + + strlen(base_perpeer_logdir) + + sizeof("//.log") + + 1; + c->log_file_name = alloc_bytes(lf_len, "per-peer log file name"); + + fprintf(stderr, "base dir |%s| dname |%s| peername |%s|" + , base_perpeer_logdir, dname, peername); + snprintf(c->log_file_name, lf_len, "%s/%s/%s.log" + , base_perpeer_logdir, dname, peername); + + syslog(LOG_DEBUG, "conn %s logfile is %s", c->name, c->log_file_name); + } + + /* now open the file, creating directories if necessary */ + + { /* create the directory */ + char *dname; + int bpl_len = strlen(base_perpeer_logdir); + char *slashloc; + + dname = clone_str(c->log_file_name, "temp copy of file name"); + dname = dirname(dname); + + if (access(dname, W_OK) != 0) + { + if (errno != ENOENT) + { + if (c->log_file_err) + { + syslog(LOG_CRIT, "can not write to %s: %s" + , dname, strerror(errno)); + c->log_file_err = TRUE; + pfree(dname); + return; + } + } + + /* directory does not exist, walk path creating dirs */ + /* start at base_perpeer_logdir */ + slashloc = dname + bpl_len; + slashloc++; /* since, by construction there is a slash + right there */ + + while (*slashloc != '\0') + { + char saveslash; + + /* look for next slash */ + while (*slashloc != '\0' && *slashloc != '/') slashloc++; + + saveslash = *slashloc; + + *slashloc = '\0'; + + if (mkdir(dname, 0750) != 0 && errno != EEXIST) + { + syslog(LOG_CRIT, "can not create dir %s: %s" + , dname, strerror(errno)); + c->log_file_err = TRUE; + pfree(dname); + return; + } + syslog(LOG_DEBUG, "created new directory %s", dname); + *slashloc = saveslash; + slashloc++; + } + } + + pfree(dname); + } + + c->log_file = fopen(c->log_file_name, "a"); + if (c->log_file == NULL) + { + if (c->log_file_err) + { + syslog(LOG_CRIT, "logging system can not open %s: %s" + , c->log_file_name, strerror(errno)); + c->log_file_err = TRUE; + } + return; + } + + /* look for a connection to close! */ + while (perpeer_count >= MAX_PEERLOG_COUNT) + { + /* can not be NULL because perpeer_count > 0 */ + passert(perpeer_list.cqh_last != (void *)&perpeer_list); + + perpeer_logclose(perpeer_list.cqh_last); + } + + /* insert this into the list */ + CIRCLEQ_INSERT_HEAD(&perpeer_list, c, log_link); + passert(c->log_file != NULL); + perpeer_count++; +} + +/* log a line to cur_connection's log */ +static void +peerlog(const char *prefix, const char *m) +{ + if (cur_connection == NULL) + { + /* we can not log it in this case. Oh well. */ + return; + } + + if (cur_connection->log_file == NULL) + { + open_peerlog(cur_connection); + } + + /* despite our attempts above, we may not be able to open the file. */ + if (cur_connection->log_file != NULL) + { + char datebuf[32]; + time_t n; + struct tm *t; + + time(&n); + t = localtime(&n); + + strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %T", t); + fprintf(cur_connection->log_file, "%s %s%s\n", datebuf, prefix, m); + + /* now move it to the front of the list */ + CIRCLEQ_REMOVE(&perpeer_list, cur_connection, log_link); + CIRCLEQ_INSERT_HEAD(&perpeer_list, cur_connection, log_link); + } +} + +void +plog(const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + fmt_log(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "%s\n", m); + if (log_to_syslog) + syslog(LOG_WARNING, "%s", m); + if (log_to_perpeer) + peerlog("", m); + + whack_log(RC_LOG, "~%s", m); +} + +void +loglog(int mess_no, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + fmt_log(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "%s\n", m); + if (log_to_syslog) + syslog(LOG_WARNING, "%s", m); + if (log_to_perpeer) + peerlog("", m); + + whack_log(mess_no, "~%s", m); +} + +void +log_errno_routine(int e, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + fmt_log(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "ERROR: %s. Errno %d: %s\n", m, e, strerror(e)); + if (log_to_syslog) + syslog(LOG_ERR, "ERROR: %s. Errno %d: %s", m, e, strerror(e)); + if (log_to_perpeer) + { + peerlog(strerror(e), m); + } + + whack_log(RC_LOG_SERIOUS + , "~ERROR: %s. Errno %d: %s", m, e, strerror(e)); +} + +void +exit_log(const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + fmt_log(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "FATAL ERROR: %s\n", m); + if (log_to_syslog) + syslog(LOG_ERR, "FATAL ERROR: %s", m); + if (log_to_perpeer) + peerlog("FATAL ERROR: ", m); + + whack_log(RC_LOG_SERIOUS, "~FATAL ERROR: %s", m); + + exit_pluto(1); +} + +void +exit_log_errno_routine(int e, const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + fmt_log(m, sizeof(m), message, args); + va_end(args); + + if (log_to_stderr) + fprintf(stderr, "FATAL ERROR: %s. Errno %d: %s\n", m, e, strerror(e)); + if (log_to_syslog) + syslog(LOG_ERR, "FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e)); + if (log_to_perpeer) + peerlog(strerror(e), m); + + whack_log(RC_LOG_SERIOUS + , "~FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e)); + + exit_pluto(1); +} + +/* emit message to whack. + * form is "ddd statename text" where + * - ddd is a decimal status code (RC_*) as described in whack.h + * - text is a human-readable annotation + */ +#ifdef DEBUG +static volatile sig_atomic_t dying_breath = FALSE; +#endif + +void +whack_log(int mess_no, const char *message, ...) +{ + int wfd = whack_log_fd != NULL_FD ? whack_log_fd + : cur_state != NULL ? cur_state->st_whack_sock + : NULL_FD; + + if (wfd != NULL_FD +#ifdef DEBUG + || dying_breath +#endif + ) + { + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + int prelen = snprintf(m, sizeof(m), "%03d ", mess_no); + + passert(prelen >= 0); + + va_start(args, message); + fmt_log(m+prelen, sizeof(m)-prelen, message, args); + va_end(args); + +#if DEBUG + if (dying_breath) + { + /* status output copied to log */ + if (log_to_stderr) + fprintf(stderr, "%s\n", m + prelen); + if (log_to_syslog) + syslog(LOG_WARNING, "%s", m + prelen); + if (log_to_perpeer) + peerlog("", m); + } +#endif + + if (wfd != NULL_FD) + { + /* write to whack socket, but suppress possible SIGPIPE */ + size_t len = strlen(m); +#ifdef MSG_NOSIGNAL /* depends on version of glibc??? */ + m[len] = '\n'; /* don't need NUL, do need NL */ + (void) send(wfd, m, len + 1, MSG_NOSIGNAL); +#else /* !MSG_NOSIGNAL */ + int r; + struct sigaction act + , oldact; + + m[len] = '\n'; /* don't need NUL, do need NL */ + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; /* no nothing */ + r = sigaction(SIGPIPE, &act, &oldact); + passert(r == 0); + + (void) write(wfd, m, len + 1); + + r = sigaction(SIGPIPE, &oldact, NULL); + passert(r == 0); +#endif /* !MSG_NOSIGNAL */ + } + } +} + +/* Build up a diagnostic in a static buffer. + * Although this would be a generally useful function, it is very + * hard to come up with a discipline that prevents different uses + * from interfering. It is intended that by limiting it to building + * diagnostics, we will avoid this problem. + * Juggling is performed to allow an argument to be a previous + * result: the new string may safely depend on the old one. This + * restriction is not checked in any way: violators will produce + * confusing results (without crashing!). + */ +char diag_space[sizeof(diag_space)]; + +err_t +builddiag(const char *fmt, ...) +{ + static char diag_space[LOG_WIDTH]; /* longer messages will be truncated */ + char t[sizeof(diag_space)]; /* build result here first */ + va_list args; + + va_start(args, fmt); + t[0] = '\0'; /* in case nothing terminates string */ + vsnprintf(t, sizeof(t), fmt, args); + va_end(args); + strcpy(diag_space, t); + return diag_space; +} + +/* Debugging message support */ + +#ifdef DEBUG + +void +switch_fail(int n, const char *file_str, unsigned long line_no) +{ + char buf[30]; + + snprintf(buf, sizeof(buf), "case %d unexpected", n); + passert_fail(buf, file_str, line_no); +} + +void +passert_fail(const char *pred_str, const char *file_str, unsigned long line_no) +{ + /* we will get a possibly unplanned prefix. Hope it works */ + loglog(RC_LOG_SERIOUS, "ASSERTION FAILED at %s:%lu: %s", file_str, line_no, pred_str); + if (!dying_breath) + { + dying_breath = TRUE; + show_status(TRUE, NULL); + } + abort(); /* exiting correctly doesn't always work */ +} + +void +pexpect_log(const char *pred_str, const char *file_str, unsigned long line_no) +{ + /* we will get a possibly unplanned prefix. Hope it works */ + loglog(RC_LOG_SERIOUS, "EXPECTATION FAILED at %s:%lu: %s", file_str, line_no, pred_str); +} + +lset_t + base_debugging = DBG_NONE, /* default to reporting nothing */ + cur_debugging = DBG_NONE; + +void +extra_debugging(const struct connection *c) +{ + if(c == NULL) + { + reset_debugging(); + return; + } + + if (c!= NULL && c->extra_debugging != 0) + { + plog("enabling for connection: %s" + , bitnamesof(debug_bit_names, c->extra_debugging & ~cur_debugging)); + cur_debugging |= c->extra_debugging; + } +} + +/* log a debugging message (prefixed by "| ") */ + +void +DBG_log(const char *message, ...) +{ + va_list args; + char m[LOG_WIDTH]; /* longer messages will be truncated */ + + va_start(args, message); + vsnprintf(m, sizeof(m), message, args); + va_end(args); + + (void)sanitize(m, sizeof(m)); + + if (log_to_stderr) + fprintf(stderr, "| %s\n", m); + if (log_to_syslog) + syslog(LOG_DEBUG, "| %s", m); + if (log_to_perpeer) + peerlog("| ", m); +} + +/* dump raw bytes in hex to stderr (for lack of any better destination) */ + +void +DBG_dump(const char *label, const void *p, size_t len) +{ +# define DUMP_LABEL_WIDTH 20 /* arbitrary modest boundary */ +# define DUMP_WIDTH (4 * (1 + 4 * 3) + 1) + char buf[DUMP_LABEL_WIDTH + DUMP_WIDTH]; + char *bp; + const unsigned char *cp = p; + + bp = buf; + + if (label != NULL && label[0] != '\0') + { + /* Handle the label. Care must be taken to avoid buffer overrun. */ + size_t llen = strlen(label); + + if (llen + 1 > sizeof(buf)) + { + DBG_log("%s", label); + } + else + { + strcpy(buf, label); + if (buf[llen-1] == '\n') + { + buf[llen-1] = '\0'; /* get rid of newline */ + DBG_log("%s", buf); + } + else if (llen < DUMP_LABEL_WIDTH) + { + bp = buf + llen; + } + else + { + DBG_log("%s", buf); + } + } + } + + do { + int i, j; + + for (i = 0; len!=0 && i!=4; i++) + { + *bp++ = ' '; + for (j = 0; len!=0 && j!=4; len--, j++) + { + static const char hexdig[] = "0123456789abcdef"; + + *bp++ = ' '; + *bp++ = hexdig[(*cp >> 4) & 0xF]; + *bp++ = hexdig[*cp & 0xF]; + cp++; + } + } + *bp = '\0'; + DBG_log("%s", buf); + bp = buf; + } while (len != 0); +# undef DUMP_LABEL_WIDTH +# undef DUMP_WIDTH +} + +#endif /* DEBUG */ + +void +show_status(bool all, const char *name) +{ + if (all) + { + show_ifaces_status(); + show_myid_status(); + show_debug_status(); + whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ + } + show_connections_status(all, name); + show_states_status(all, name); +#ifdef KLIPS + show_shunt_status(); +#endif +} + +/* ip_str: a simple to use variant of addrtot. + * It stores its result in a static buffer. + * This means that newer calls overwrite the storage of older calls. + * Note: this is not used in any of the logging functions, so their + * callers may use it. + */ +const char * +ip_str(const ip_address *src) +{ + static char buf[ADDRTOT_BUF]; + + addrtot(src, 0, buf, sizeof(buf)); + return buf; +} + +/* + * a routine that attempts to schedule itself daily. + * + */ + +void +daily_log_reset(void) +{ + /* now perform actions */ + logged_txt_warning = FALSE; + + logged_myid_fqdn_txt_warning = FALSE; + logged_myid_ip_txt_warning = FALSE; + logged_myid_fqdn_key_warning = FALSE; + logged_myid_ip_key_warning = FALSE; +} + +void +daily_log_event(void) +{ + struct tm *ltime; + time_t n, interval; + + /* attempt to schedule oneself to midnight, local time + * do this by getting seconds in the day, and delaying + * by 86400 - hour*3600+minutes*60+seconds. + */ + time(&n); + ltime = localtime(&n); + interval = (24 * 60 * 60) + - (ltime->tm_sec + + ltime->tm_min * 60 + + ltime->tm_hour * 3600); + + event_schedule(EVENT_LOG_DAILY, interval, NULL); + + daily_log_reset(); +} + +/* + * Local Variables: + * c-basic-offset:4 + * c-style: pluto + * End: + */ diff --git a/src/pluto/log.h b/src/pluto/log.h new file mode 100644 index 000000000..a4eae9d1c --- /dev/null +++ b/src/pluto/log.h @@ -0,0 +1,236 @@ +/* logging definitions + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: log.h,v 1.4 2005/07/11 18:33:45 as Exp $ + */ + +#include + +#define LOG_WIDTH 1024 /* roof of number of chars in log line */ + +#ifndef PERPEERLOGDIR +#define PERPEERLOGDIR "/var/log/pluto/peer" +#endif + +/* our versions of assert: log result */ + +#ifdef DEBUG + +extern void passert_fail(const char *pred_str + , const char *file_str, unsigned long line_no) NEVER_RETURNS; + +extern void pexpect_log(const char *pred_str + , const char *file_str, unsigned long line_no); + +# define impossible() passert_fail("impossible", __FILE__, __LINE__) + +extern void switch_fail(int n + , const char *file_str, unsigned long line_no) NEVER_RETURNS; + +# define bad_case(n) switch_fail((int) n, __FILE__, __LINE__) + +# define passert(pred) { \ + if (!(pred)) \ + passert_fail(#pred, __FILE__, __LINE__); \ + } + +# define pexpect(pred) { \ + if (!(pred)) \ + pexpect_log(#pred, __FILE__, __LINE__); \ + } + +/* assert that an err_t is NULL; evaluate exactly once */ +# define happy(x) { \ + err_t ugh = x; \ + if (ugh != NULL) \ + passert_fail(ugh, __FILE__, __LINE__); \ + } + +#else /*!DEBUG*/ + +# define impossible() abort() +# define bad_case(n) abort() +# define passert(pred) { } /* do nothing */ +# define happy(x) { (void) x; } /* evaluate non-judgementally */ + +#endif /*!DEBUG*/ + + +extern bool + log_to_stderr, /* should log go to stderr? */ + log_to_syslog, /* should log go to syslog? */ + log_to_perpeer; /* should log go to per-IP file? */ + +extern const char *base_perpeer_logdir; + +/* maximum number of files to keep open for per-peer log files */ +#define MAX_PEERLOG_COUNT 16 + +/* Context for logging. + * + * Global variables: must be carefully adjusted at transaction boundaries! + * All are to be left in RESET condition and will be checked. + * There are several pairs of routines to set and reset them. + * If the context provides a whack file descriptor, messages + * should be copied to it -- see whack_log() + */ +extern int whack_log_fd; /* only set during whack_handle() */ +extern struct state *cur_state; /* current state, for diagnostics */ +extern struct connection *cur_connection; /* current connection, for diagnostics */ +extern const ip_address *cur_from; /* source of current current message */ +extern u_int16_t cur_from_port; /* host order */ + +#ifdef DEBUG + + extern lset_t cur_debugging; /* current debugging level */ + + extern void extra_debugging(const struct connection *c); + +# define reset_debugging() { cur_debugging = base_debugging; } + +# define GLOBALS_ARE_RESET() (whack_log_fd == NULL_FD \ + && cur_state == NULL \ + && cur_connection == NULL \ + && cur_from == NULL \ + && cur_debugging == base_debugging) + +#else /*!DEBUG*/ + +# define extra_debugging(c) { } + +# define reset_debugging() { } + +# define GLOBALS_ARE_RESET() (whack_log_fd == NULL_FD \ + && cur_state == NULL \ + && cur_connection == NULL \ + && cur_from == NULL) + +#endif /*!DEBUG*/ + +#define reset_globals() { \ + whack_log_fd = NULL_FD; \ + cur_state = NULL; \ + cur_from = NULL; \ + reset_cur_connection(); \ + } + + +#define set_cur_connection(c) { \ + cur_connection = (c); \ + extra_debugging(c); \ + } + +#define reset_cur_connection() { \ + cur_connection = NULL; \ + reset_debugging(); \ + } + + +#define set_cur_state(s) { \ + cur_state = (s); \ + extra_debugging((s)->st_connection); \ + } + +#define reset_cur_state() { \ + cur_state = NULL; \ + reset_debugging(); \ + } + +extern void init_log(const char *program); +extern void close_log(void); +extern void plog(const char *message, ...) PRINTF_LIKE(1); +extern void exit_log(const char *message, ...) PRINTF_LIKE(1) NEVER_RETURNS; + +/* close of all per-peer logging */ +extern void close_peerlog(void); + +/* free all per-peer log resources */ +extern void perpeer_logfree(struct connection *c); + + + +/* the following routines do a dance to capture errno before it is changed + * A call must doubly parenthesize the argument list (no varargs macros). + * The first argument must be "e", the local variable that captures errno. + */ +#define log_errno(a) { int e = errno; log_errno_routine a; } +extern void log_errno_routine(int e, const char *message, ...) PRINTF_LIKE(2); +#define exit_log_errno(a) { int e = errno; exit_log_errno_routine a; } +extern void exit_log_errno_routine(int e, const char *message, ...) PRINTF_LIKE(2) NEVER_RETURNS NEVER_RETURNS; + +extern void whack_log(int mess_no, const char *message, ...) PRINTF_LIKE(2); + +/* Log to both main log and whack log + * Much like log, actually, except for specifying mess_no. + */ +extern void loglog(int mess_no, const char *message, ...) PRINTF_LIKE(2); + +/* show status, usually on whack log */ +extern void show_status(bool all, const char *name); + +/* Build up a diagnostic in a static buffer. + * Although this would be a generally useful function, it is very + * hard to come up with a discipline that prevents different uses + * from interfering. It is intended that by limiting it to building + * diagnostics, we will avoid this problem. + * Juggling is performed to allow an argument to be a previous + * result: the new string may safely depend on the old one. This + * restriction is not checked in any way: violators will produce + * confusing results (without crashing!). + */ +extern char diag_space[LOG_WIDTH]; /* output buffer, but can be occupied at call */ +extern err_t builddiag(const char *fmt, ...) PRINTF_LIKE(1); + +#ifdef DEBUG + +extern lset_t base_debugging; /* bits selecting what to report */ + +#define DBGP(cond) (cur_debugging & (cond)) +#define DBG(cond, action) { if (DBGP(cond)) { action ; } } + +extern void DBG_log(const char *message, ...) PRINTF_LIKE(1); +extern void DBG_dump(const char *label, const void *p, size_t len); +#define DBG_dump_chunk(label, ch) DBG_dump(label, (ch).ptr, (ch).len) + +#else /*!DEBUG*/ + +#define DBG(cond, action) { } /* do nothing */ + +#endif /*!DEBUG*/ + +#define DBG_cond_dump(cond, label, p, len) DBG(cond, DBG_dump(label, p, len)) +#define DBG_cond_dump_chunk(cond, label, ch) DBG(cond, DBG_dump_chunk(label, ch)) + + +/* ip_str: a simple to use variant of addrtot. + * It stores its result in a static buffer. + * This means that newer calls overwrite the storage of older calls. + * Note: this is not used in any of the logging functions, so their + * callers may use it. + */ +extern const char *ip_str(const ip_address *src); + +/* + * call this routine to reset daily items. + */ +extern void daily_log_reset(void); +extern void daily_log_event(void); + +/* + * some events are to be logged only occasionally. + */ +extern bool logged_txt_warning; +extern bool logged_myid_ip_txt_warning; +extern bool logged_myid_ip_key_warning; +extern bool logged_myid_fqdn_txt_warning; +extern bool logged_myid_fqdn_key_warning; diff --git a/src/pluto/md2.c b/src/pluto/md2.c new file mode 100644 index 000000000..d6465477d --- /dev/null +++ b/src/pluto/md2.c @@ -0,0 +1,237 @@ +/* MD2C.C - RSA Data Security, Inc., MD2 message-digest algorithm + */ + +/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All + rights reserved. + + License to copy and use this software is granted for + non-commercial Internet Privacy-Enhanced Mail provided that it is + identified as the "RSA Data Security, Inc. MD2 Message Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#include "md2.h" + +#define HAVEMEMCOPY 1 /* use ISO C's memcpy and memset */ + +static void MD2Transform PROTO_LIST + ((unsigned char [16], unsigned char [16], const unsigned char [16])); + +#ifdef HAVEMEMCOPY +#include +#define MD2_memcpy memcpy +#define MD2_memset memset +#else +#ifdef HAVEBCOPY +#define MD2_memcpy(_a,_b,_c) memcpy((_a), (_b),(_c)) +#define MD2_memset(_a,_b,_c) memset((_a), '\0',(_c)) +#else +static void MD2_memcpy PROTO_LIST ((POINTER, CONST_POINTER, unsigned int)); +static void MD2_memset PROTO_LIST ((POINTER, int, unsigned int)); +#endif +#endif + +/* Permutation of 0..255 constructed from the digits of pi. It gives a + "random" nonlinear byte substitution operation. + */ +static unsigned char PI_SUBST[256] = { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 +}; + +static const unsigned char *PADDING[] = { + (const unsigned char *)"", + (const unsigned char *)"\001", + (const unsigned char *)"\002\002", + (const unsigned char *)"\003\003\003", + (const unsigned char *)"\004\004\004\004", + (const unsigned char *)"\005\005\005\005\005", + (const unsigned char *)"\006\006\006\006\006\006", + (const unsigned char *)"\007\007\007\007\007\007\007", + (const unsigned char *)"\010\010\010\010\010\010\010\010", + (const unsigned char *)"\011\011\011\011\011\011\011\011\011", + (const unsigned char *)"\012\012\012\012\012\012\012\012\012\012", + (const unsigned char *)"\013\013\013\013\013\013\013\013\013\013\013", + (const unsigned char *)"\014\014\014\014\014\014\014\014\014\014\014\014", + (const unsigned char *) + "\015\015\015\015\015\015\015\015\015\015\015\015\015", + (const unsigned char *) + "\016\016\016\016\016\016\016\016\016\016\016\016\016\016", + (const unsigned char *) + "\017\017\017\017\017\017\017\017\017\017\017\017\017\017\017", + (const unsigned char *) + "\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020" +}; + +/* MD2 initialization. Begins an MD2 operation, writing a new context. + */ +void MD2Init (context) +MD2_CTX *context; /* context */ +{ + context->count = 0; + MD2_memset ((POINTER)context->state, 0, sizeof (context->state)); + MD2_memset + ((POINTER)context->checksum, 0, sizeof (context->checksum)); +} + +/* MD2 block update operation. Continues an MD2 message-digest + operation, processing another message block, and updating the + context. + */ +void MD2Update (context, input, inputLen) +MD2_CTX *context; /* context */ +const unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Update number of bytes mod 16 */ + index = context->count; + context->count = (index + inputLen) & 0xf; + + partLen = 16 - index; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) { + MD2_memcpy + ((POINTER)&context->buffer[index], (CONST_POINTER)input, partLen); + MD2Transform (context->state, context->checksum, context->buffer); + + for (i = partLen; i + 15 < inputLen; i += 16) + MD2Transform (context->state, context->checksum, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD2_memcpy + ((POINTER)&context->buffer[index], (CONST_POINTER)&input[i], + inputLen-i); +} + +/* MD2 finalization. Ends an MD2 message-digest operation, writing the + message digest and zeroizing the context. + */ +void MD2Final (digest, context) + +unsigned char digest[16]; /* message digest */ +MD2_CTX *context; /* context */ +{ + unsigned int index, padLen; + + /* Pad out to multiple of 16. + */ + index = context->count; + padLen = 16 - index; + MD2Update (context, PADDING[padLen], padLen); + + /* Extend with checksum */ + MD2Update (context, context->checksum, 16); + + /* Store state in digest */ + MD2_memcpy ((POINTER)digest, (POINTER)context->state, 16); + + /* Zeroize sensitive information. + */ + MD2_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD2 basic transformation. Transforms state and updates checksum + based on block. + */ +static void MD2Transform (state, checksum, block) +unsigned char state[16]; +unsigned char checksum[16]; +const unsigned char block[16]; +{ + unsigned int i, j, t; + unsigned char x[48]; + + /* Form encryption block from state, block, state ^ block. + */ + MD2_memcpy ((POINTER)x, (CONST_POINTER)state, 16); + MD2_memcpy ((POINTER)x+16, (CONST_POINTER)block, 16); + for (i = 0; i < 16; i++) + x[i+32] = state[i] ^ block[i]; + + /* Encrypt block (18 rounds). + */ + t = 0; + for (i = 0; i < 18; i++) { + for (j = 0; j < 48; j++) + t = x[j] ^= PI_SUBST[t]; + t = (t + i) & 0xff; + } + + /* Save new state */ + MD2_memcpy ((POINTER)state, (POINTER)x, 16); + + /* Update checksum. + */ + t = checksum[15]; + for (i = 0; i < 16; i++) + t = checksum[i] ^= PI_SUBST[block[i] ^ t]; + + /* Zeroize sensitive information. + */ + MD2_memset ((POINTER)x, 0, sizeof (x)); +} + +#ifndef HAVEMEMCOPY +#ifndef HAVEBCOPY +/* Note: Replace "for loop" with standard memcpy if possible. + */ +static void MD2_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD2_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} +#endif +#endif + diff --git a/src/pluto/md2.h b/src/pluto/md2.h new file mode 100644 index 000000000..b3b48dd92 --- /dev/null +++ b/src/pluto/md2.h @@ -0,0 +1,72 @@ +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 1 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; +typedef const unsigned char *CONST_POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ + +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +#endif + +/* MD2.H - header file for MD2C.C + */ + +/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All + rights reserved. + + License to copy and use this software is granted for + non-commercial Internet Privacy-Enhanced Mail provided that it is + identified as the "RSA Data Security, Inc. MD2 Message Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +/* MD2 context. */ +typedef struct { + unsigned char state[16]; /* state */ + unsigned char checksum[16]; /* checksum */ + unsigned int count; /* number of bytes, modulo 16 */ + unsigned char buffer[16]; /* input buffer */ +} MD2_CTX; + +void MD2Init PROTO_LIST ((MD2_CTX *)); +void MD2Update PROTO_LIST + ((MD2_CTX *, const unsigned char *, unsigned int)); +void MD2Final PROTO_LIST ((unsigned char [16], MD2_CTX *)); + +#define _MD2_H_ diff --git a/src/pluto/md5.c b/src/pluto/md5.c new file mode 100644 index 000000000..5d75e38a4 --- /dev/null +++ b/src/pluto/md5.c @@ -0,0 +1,385 @@ +/* + * The rest of the code is derived from MD5C.C by RSADSI. Minor cosmetic + * changes to accomodate it in the kernel by ji. + * Minor changes to make 64 bit clean by Peter Onion (i.e. using u_int*_t). + */ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* + * Additions by JI + * + * HAVEMEMCOPY is defined if mem* routines are available + * + * HAVEHTON is defined if htons() and htonl() can be used + * for big/little endian conversions + * + */ + +#include +#include +#include /* for u_int*_t */ +#include /* sets BYTE_ORDER, LITTLE_ENDIAN, and BIG_ENDIAN */ + +#include "md5.h" + +#define HAVEMEMCOPY 1 /* use ISO C's memcpy and memset */ + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +#define MD5Transform _MD5Transform + +static void MD5Transform PROTO_LIST ((UINT4 [4], const unsigned char [64])); + +#if BYTE_ORDER == LITTLE_ENDIAN +#define Encode MD5_memcpy +#define Decode MD5_memcpy +#else +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +#endif + +#ifdef HAVEMEMCOPY +#include +#define MD5_memcpy memcpy +#define MD5_memset memset +#else +#ifdef HAVEBCOPY +#define MD5_memcpy(_a,_b,_c) memcpy((_a), (_b),(_c)) +#define MD5_memset(_a,_b,_c) memset((_a), '\0',(_c)) +#else +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); +#endif +#endif +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +const unsigned char *input; /* input block */ +UINT4 inputLen; /* length of input block */ +{ + UINT4 i; + unsigned int index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += (inputLen << 3)) < (inputLen << 3)) + context->count[1]++; + context->count[1] += (inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + MD5_memcpy((POINTER)&context->buffer[index], (CONSTPOINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER)&context->buffer[index], (CONSTPOINTER)&input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + if (digest != NULL) /* Bill Simpson's padding */ + { + /* store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); + } +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +const unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +#if BYTE_ORDER != LITTLE_ENDIAN + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +#endif + +#ifndef HAVEMEMCOPY +#ifndef HAVEBCOPY +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} +#endif +#endif + diff --git a/src/pluto/md5.h b/src/pluto/md5.h new file mode 100644 index 000000000..9b29bc46e --- /dev/null +++ b/src/pluto/md5.h @@ -0,0 +1,75 @@ +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 1 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; +typedef const unsigned char *CONSTPOINTER; + +/* UINT2 defines a two byte word */ +typedef u_int16_t UINT2; + +/* UINT4 defines a four byte word */ +typedef u_int32_t UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ + +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +#endif + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, const unsigned char *, UINT4)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + +#define _MD5_H_ diff --git a/src/pluto/modecfg.c b/src/pluto/modecfg.c new file mode 100644 index 000000000..ab44a113e --- /dev/null +++ b/src/pluto/modecfg.c @@ -0,0 +1,1078 @@ +/* Mode config related functions + * Copyright (C) 2001-2002 Colubris Networks + * Copyright (C) 2003 Sean Mathews - Nu Tech Software Solutions, inc. + * Copyright (C) 2003-2004 Xelerance Corporation + * Copyright (C) 2006-2007 Andreas Steffen - Hochschule fuer Technik Rapperswil + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: modecfg.c,v 1.6 2006/04/24 20:44:57 as Exp $ + * + * This code originally written by Colubris Networks, Inc. + * Extraction of patch and porting to 1.99 codebases by Xelerance Corporation + * Porting to 2.x by Sean Mathews + */ + +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "state.h" +#include "demux.h" +#include "timer.h" +#include "ipsec_doi.h" +#include "log.h" +#include "md5.h" +#include "sha1.h" +#include "crypto.h" +#include "modecfg.h" +#include "whack.h" +#include "xauth.h" + +#define MAX_XAUTH_TRIES 3 + +#define SUPPORTED_ATTR_SET ( LELEM(INTERNAL_IP4_ADDRESS) \ + | LELEM(INTERNAL_IP4_NETMASK) \ + | LELEM(INTERNAL_IP4_DNS) \ + | LELEM(INTERNAL_IP4_NBNS) \ + | LELEM(APPLICATION_VERSION) \ + ) + +#define SUPPORTED_UNITY_ATTR_SET ( LELEM(UNITY_BANNER - UNITY_BASE) ) + +#define UNITY_BANNER_STR "Welcome to strongSwan - the Linux VPN Solution!\n" + +/* + * Addresses assigned (usually via ModeCfg) to the Initiator + */ +typedef struct internal_addr internal_addr_t; + +struct internal_addr +{ + lset_t attr_set; + lset_t xauth_attr_set; + lset_t unity_attr_set; + + /* ModeCfg variables */ + ip_address ipaddr; + ip_address dns[2]; + ip_address wins[2]; + + char *unity_banner; + + /* XAUTH variables */ + u_int16_t xauth_type; + xauth_t xauth_secret; + bool xauth_status; +}; + +/* + * Initialize an internal_addr struct + */ +static void +init_internal_addr(internal_addr_t *ia) +{ + ia->attr_set = LEMPTY; + ia->xauth_attr_set = LEMPTY; + ia->xauth_secret.user_name = empty_chunk; + ia->xauth_secret.user_password = empty_chunk; + ia->xauth_type = XAUTH_TYPE_GENERIC; + ia->xauth_status = XAUTH_STATUS_FAIL; + ia->unity_attr_set = LEMPTY; + ia->unity_banner = NULL; + + anyaddr(AF_INET, &ia->ipaddr); + anyaddr(AF_INET, &ia->dns[0]); + anyaddr(AF_INET, &ia->dns[1]); + anyaddr(AF_INET, &ia->wins[0]); + anyaddr(AF_INET, &ia->wins[1]); +} + +/* + * get internal IP address for a connection + */ +static void +get_internal_addr(struct connection *c, internal_addr_t *ia) +{ + if (isanyaddr(&c->spd.that.host_srcip)) + { + /* not defined in connection - fetch it from LDAP */ + } + else + { + char srcip[ADDRTOT_BUF]; + + ia->ipaddr = c->spd.that.host_srcip; + + addrtot(&ia->ipaddr, 0, srcip, sizeof(srcip)); + plog("assigning virtual IP source address %s", srcip); + } + + if (!isanyaddr(&ia->ipaddr)) /* We got an IP address, send it */ + { + c->spd.that.client.addr = ia->ipaddr; + c->spd.that.client.maskbits = 32; + c->spd.that.has_client = TRUE; + + ia->attr_set = LELEM(INTERNAL_IP4_ADDRESS) + | LELEM(INTERNAL_IP4_NETMASK); + } + + if (!isanyaddr(&ia->dns[0])) /* We got DNS addresses, send them */ + ia->attr_set |= LELEM(INTERNAL_IP4_DNS); + + if (!isanyaddr(&ia->wins[0])) /* We got WINS addresses, send them */ + ia->attr_set |= LELEM(INTERNAL_IP4_NBNS); +} + +/* + * Set srcip and client subnet to internal IP address + */ +static bool +set_internal_addr(struct connection *c, internal_addr_t *ia) +{ + if (ia->attr_set & LELEM(INTERNAL_IP4_ADDRESS) + && !isanyaddr(&ia->ipaddr)) + { + if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 + || isanyaddr(&c->spd.this.host_srcip) + || sameaddr(&c->spd.this.host_srcip, &ia->ipaddr)) + { + char srcip[ADDRTOT_BUF]; + + addrtot(&ia->ipaddr, 0, srcip, sizeof(srcip)); + plog("setting virtual IP source address to %s", srcip); + } + else + { + char old_srcip[ADDRTOT_BUF]; + char new_srcip[ADDRTOT_BUF]; + + addrtot(&c->spd.this.host_srcip, 0, old_srcip, sizeof(old_srcip)); + addrtot(&ia->ipaddr, 0, new_srcip, sizeof(new_srcip)); + plog("replacing virtual IP source address %s by %s" + , old_srcip, new_srcip); + } + + /* setting srcip */ + c->spd.this.host_srcip = ia->ipaddr; + + /* setting client subnet to srcip/32 */ + addrtosubnet(&ia->ipaddr, &c->spd.this.client); + setportof(0, &c->spd.this.client.addr); + c->spd.this.has_client = TRUE; + return TRUE; + } + return FALSE; +} + +/* + * Compute HASH of Mode Config. + */ +static size_t +modecfg_hash(u_char *dest, const u_char *start, const u_char *roof + , const struct state *st) +{ + struct hmac_ctx ctx; + + hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a); + hmac_update(&ctx, (const u_char *) &st->st_msgid, sizeof(st->st_msgid)); + hmac_update(&ctx, start, roof-start); + hmac_final(dest, &ctx); + + DBG(DBG_CRYPT, + DBG_log("ModeCfg HASH computed:"); + DBG_dump("", dest, ctx.hmac_digest_size) + ) + return ctx.hmac_digest_size; +} + + +/* + * Generate an IKE message containing ModeCfg information (eg: IP, DNS, WINS) + */ +static stf_status +modecfg_build_msg(struct state *st, pb_stream *rbody + , u_int16_t msg_type + , internal_addr_t *ia + , u_int16_t ap_id) +{ + u_char *r_hash_start, *r_hashval; + + START_HASH_PAYLOAD(*rbody, ISAKMP_NEXT_ATTR); + + /* ATTR out */ + { + struct isakmp_mode_attr attrh; + struct isakmp_attribute attr; + pb_stream strattr,attrval; + int attr_type; + int dns_idx, wins_idx; + bool dont_advance; + bool is_xauth_attr_set = ia->xauth_attr_set != LEMPTY; + bool is_unity_attr_set = ia->unity_attr_set != LEMPTY; + lset_t attr_set = ia->attr_set; + + attrh.isama_np = ISAKMP_NEXT_NONE; + attrh.isama_type = msg_type; + attrh.isama_identifier = ap_id; + + if (!out_struct(&attrh, &isakmp_attr_desc, rbody, &strattr)) + return STF_INTERNAL_ERROR; + + attr_type = 0; + dns_idx = 0; + wins_idx = 0; + + while (attr_set != LEMPTY || is_xauth_attr_set || is_unity_attr_set) + { + if (attr_set == LEMPTY) + { + if (is_xauth_attr_set) + { + attr_set = ia->xauth_attr_set; + attr_type = XAUTH_BASE; + is_xauth_attr_set = FALSE; + } + else + { + attr_set = ia->unity_attr_set; + attr_type = UNITY_BASE; + is_unity_attr_set = FALSE; + } + } + + dont_advance = FALSE; + + if (attr_set & 1) + { + const u_char *byte_ptr; + u_int len; + + /* ISAKMP attr out */ + if (attr_type == XAUTH_TYPE) + { + attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TV; + attr.isaat_lv = ia->xauth_type; + } + else if (attr_type == XAUTH_STATUS) + { + attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TV; + attr.isaat_lv = ia->xauth_status; + } + else + { + attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV; + } + out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, &attrval); + + switch (attr_type) + { + case INTERNAL_IP4_ADDRESS: + if (!isanyaddr(&ia->ipaddr)) + { + len = addrbytesptr(&ia->ipaddr, &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_addr"); + } + break; + case INTERNAL_IP4_NETMASK: + { + u_int mask; +#if 0 + char mask[4],bits[8]={0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe}; + int t,m=st->st_connection->that.host_addr.maskbit; + for (t=0; t<4; t++) + { + if (m < 8) + mask[t] = bits[m]; + else + mask[t] = 0xff; + m -= 8; + } +#endif + if (st->st_connection->spd.this.client.maskbits == 0) + mask = 0; + else + mask = 0xffffffff * 1; + out_raw(&mask, 4, &attrval, "IP4_mask"); + } + break; + case INTERNAL_IP4_SUBNET: + { + char mask[4]; + char bits[8] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe}; + int t; + int m = st->st_connection->spd.this.client.maskbits; + + for (t = 0; t < 4; t++) + { + if (m < 8) + mask[t] = bits[m]; + else + mask[t] = 0xff; + m -= 8; + if (m < 0) + m = 0; + } + len = addrbytesptr(&st->st_connection->spd.this.client.addr, &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_subnet"); + out_raw(mask, sizeof(mask), &attrval, "IP4_submsk"); + } + break; + case INTERNAL_IP4_DNS: + if (!isanyaddr(&ia->dns[dns_idx])) + { + len = addrbytesptr(&ia->dns[dns_idx++], &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_dns"); + } + if (dns_idx < 2 && !isanyaddr(&ia->dns[dns_idx])) + { + dont_advance = TRUE; + } + break; + case INTERNAL_IP4_NBNS: + if (!isanyaddr(&ia->wins[wins_idx])) + { + len = addrbytesptr(&ia->wins[wins_idx++], &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_wins"); + } + if (wins_idx < 2 && !isanyaddr(&ia->wins[wins_idx])) + { + dont_advance = TRUE; + } + break; + case XAUTH_TYPE: + break; + case XAUTH_USER_NAME: + if (ia->xauth_secret.user_name.ptr != NULL) + { + out_raw(ia->xauth_secret.user_name.ptr + , ia->xauth_secret.user_name.len + , &attrval, "xauth_user_name"); + } + break; + case XAUTH_USER_PASSWORD: + if (ia->xauth_secret.user_password.ptr != NULL) + { + out_raw(ia->xauth_secret.user_password.ptr + , ia->xauth_secret.user_password.len + , &attrval, "xauth_user_password"); + } + break; + case XAUTH_STATUS: + break; + case UNITY_BANNER: + if (ia->unity_banner != NULL) + { + out_raw(ia->unity_banner + , strlen(ia->unity_banner) + , &attrval, "UNITY_BANNER"); + } + break; + default: + plog("attempt to send unsupported mode cfg attribute %s." + , enum_show(&modecfg_attr_names, attr_type)); + break; + } + close_output_pbs(&attrval); + } + if (!dont_advance) + { + attr_type++; + attr_set >>= 1; + } + } + close_message(&strattr); + } + + modecfg_hash(r_hashval, r_hash_start, rbody->cur, st); + close_message(rbody); + encrypt_message(rbody, st); + return STF_OK; +} + +/* + * Send ModeCfg message + */ +static stf_status +modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia) +{ + pb_stream msg; + pb_stream rbody; + char buf[BUF_LEN]; + + /* set up attr */ + init_pbs(&msg, buf, sizeof(buf), "ModeCfg msg buffer"); + + /* this is the beginning of a new exchange */ + st->st_msgid = generate_msgid(st); + init_phase2_iv(st, &st->st_msgid); + + /* HDR out */ + { + struct isakmp_hdr hdr; + + zero(&hdr); /* default to 0 */ + hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; + hdr.isa_np = ISAKMP_NEXT_HASH; + hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG; + hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; + memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); + memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); + hdr.isa_msgid = st->st_msgid; + + if (!out_struct(&hdr, &isakmp_hdr_desc, &msg, &rbody)) + { + return STF_INTERNAL_ERROR; + } + } + + /* ATTR out */ + modecfg_build_msg(st, &rbody + , isama_type + , ia + , 0 /* XXX isama_id */ + ); + + freeanychunk(st->st_tpacket); + clonetochunk(st->st_tpacket, msg.start, pbs_offset(&msg), "ModeCfg msg"); + + /* Transmit */ + send_packet(st, "ModeCfg msg"); + + if (st->st_event->ev_type != EVENT_RETRANSMIT) + { + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); + } + return STF_OK; +} + +/* + * Parse a ModeCfg attribute payload + */ +static stf_status +modecfg_parse_attributes(pb_stream *attrs, internal_addr_t *ia) +{ + struct isakmp_attribute attr; + pb_stream strattr; + + while (pbs_left(attrs) >= sizeof(struct isakmp_attribute)) + { + u_int16_t attr_type; + u_int16_t attr_len; + + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc, attrs, &strattr)) + { + return STF_FAIL; + } + attr_type = attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK; + attr_len = attr.isaat_lv; + + switch (attr_type) + { + case INTERNAL_IP4_ADDRESS: + if (attr_len == 4) + { + initaddr((char *)(strattr.cur), 4, AF_INET, &ia->ipaddr); + } + /* fall through to set attribute flag */ + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + case INTERNAL_ADDRESS_EXPIRY: + case INTERNAL_IP4_DHCP: + case INTERNAL_IP6_ADDRESS: + case INTERNAL_IP6_NETMASK: + case INTERNAL_IP6_DNS: + case INTERNAL_IP6_NBNS: + case INTERNAL_IP6_DHCP: + case SUPPORTED_ATTRIBUTES: + case INTERNAL_IP6_SUBNET: + ia->attr_set |= LELEM(attr_type); + break; + case APPLICATION_VERSION: + if (attr_len > 0) + { + DBG(DBG_PARSING, + DBG_log(" '%.*s'", attr_len, strattr.cur) + ) + } + ia->attr_set |= LELEM(attr_type); + break; + case XAUTH_TYPE: + ia->xauth_type = attr.isaat_lv; + ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE); + break; + case XAUTH_USER_NAME: + setchunk(ia->xauth_secret.user_name, strattr.cur, attr_len); + ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE); + break; + case XAUTH_USER_PASSWORD: + setchunk(ia->xauth_secret.user_password, strattr.cur, attr_len); + ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE); + break; + case XAUTH_STATUS: + ia->xauth_status = attr.isaat_lv; + ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE); + break; + case XAUTH_MESSAGE: + if (attr_len > 0) + { + DBG(DBG_PARSING, + DBG_log(" '%.*s'", attr_len, strattr.cur) + ) + } + /* fall through to set attribute flag */ + case XAUTH_PASSCODE: + case XAUTH_CHALLENGE: + case XAUTH_DOMAIN: + case XAUTH_NEXT_PIN: + case XAUTH_ANSWER: + ia->xauth_attr_set |= LELEM(attr_type - XAUTH_BASE); + break; + case UNITY_DDNS_HOSTNAME: + if (attr_len > 0) + { + DBG(DBG_PARSING, + DBG_log(" '%.*s'", attr_len, strattr.cur) + ) + } + /* fall through to set attribute flag */ + case UNITY_BANNER: + case UNITY_SAVE_PASSWD: + case UNITY_DEF_DOMAIN: + case UNITY_SPLITDNS_NAME: + case UNITY_SPLIT_INCLUDE: + case UNITY_NATT_PORT: + case UNITY_LOCAL_LAN: + case UNITY_PFS: + case UNITY_FW_TYPE: + case UNITY_BACKUP_SERVERS: + ia->unity_attr_set |= LELEM(attr_type - UNITY_BASE); + break; + default: + plog("unsupported ModeCfg attribute %s received." + , enum_show(&modecfg_attr_names, attr_type)); + break; + } + } + return STF_OK; +} + +/* + * Parse a ModeCfg message + */ +static stf_status +modecfg_parse_msg(struct msg_digest *md, int isama_type, u_int16_t *isama_id + , internal_addr_t *ia) +{ + struct state *const st = md->st; + struct payload_digest *p; + stf_status stat; + + st->st_msgid = md->hdr.isa_msgid; + + CHECK_QUICK_HASH(md, modecfg_hash(hash_val + , hash_pbs->roof + , md->message_pbs.roof, st) + , "MODECFG-HASH", "ISAKMP_CFG_MSG"); + + /* process the ModeCfg payloads received */ + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + internal_addr_t ia_candidate; + + init_internal_addr(&ia_candidate); + + if (p->payload.attribute.isama_type == isama_type) + { + *isama_id = p->payload.attribute.isama_identifier; + + stat = modecfg_parse_attributes(&p->pbs, &ia_candidate); + if (stat == STF_OK) + { + /* return with a valid set of attributes */ + *ia = ia_candidate; + return STF_OK; + } + } + else + { + plog("expected %s, got %s instead (ignored)" + , enum_name(&attr_msg_type_names, isama_type) + , enum_name(&attr_msg_type_names, p->payload.attribute.isama_type)); + + stat = modecfg_parse_attributes(&p->pbs, &ia_candidate); + } + if (stat != STF_OK) + return stat; + } + return STF_IGNORE; +} + +/* + * Send ModeCfg request message from client to server in pull mode + */ +stf_status +modecfg_send_request(struct state *st) +{ + stf_status stat; + internal_addr_t ia; + + init_internal_addr(&ia); + + ia.attr_set = LELEM(INTERNAL_IP4_ADDRESS) + | LELEM(INTERNAL_IP4_NETMASK); + + plog("sending ModeCfg request"); + st->st_state = STATE_MODE_CFG_I1; + stat = modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia); + if (stat == STF_OK) + st->st_modecfg.started = TRUE; + return stat; +} + +/* STATE_MODE_CFG_R0: + * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP) + * + * used in ModeCfg pull mode, on the server (responder) + */ +stf_status +modecfg_inR0(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + bool want_unity_banner; + stf_status stat, stat_build; + + stat = modecfg_parse_msg(md, ISAKMP_CFG_REQUEST, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + want_unity_banner = (ia.unity_attr_set & LELEM(UNITY_BANNER - UNITY_BASE)) != LEMPTY; + + init_internal_addr(&ia); + get_internal_addr(st->st_connection, &ia); + + if (want_unity_banner) + { + ia.unity_banner = UNITY_BANNER_STR; + ia.unity_attr_set |= LELEM(UNITY_BANNER - UNITY_BASE); + } + + plog("sending ModeCfg reply"); + + stat_build = modecfg_build_msg(st, &md->rbody + , ISAKMP_CFG_REPLY + , &ia + , isama_id); + if (stat_build != STF_OK) + return stat_build; + + st->st_msgid = 0; + return STF_OK; +} + +/* STATE_MODE_CFG_I1: + * HDR*, HASH, ATTR(REPLY=IP) + * + * used in ModeCfg pull mode, on the client (initiator) + */ +stf_status +modecfg_inI1(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat; + + plog("parsing ModeCfg reply"); + + stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + st->st_modecfg.vars_set = set_internal_addr(st->st_connection, &ia); + st->st_msgid = 0; + return STF_OK; +} + + +/* + * Send ModeCfg set message from server to client in push mode + */ +stf_status +modecfg_send_set(struct state *st) +{ + stf_status stat; + internal_addr_t ia; + + init_internal_addr(&ia); + get_internal_addr(st->st_connection, &ia); + +#ifdef CISCO_QUIRKS + ia.unity_banner = UNITY_BANNER_STR; + ia.unity_attr_set |= LELEM(UNITY_BANNER - UNITY_BASE); +#endif + + plog("sending ModeCfg set"); + st->st_state = STATE_MODE_CFG_R3; + stat = modecfg_send_msg(st, ISAKMP_CFG_SET, &ia); + if (stat == STF_OK) + st->st_modecfg.started = TRUE; + return stat; +} + +/* STATE_MODE_CFG_I0: + * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + * + * used in ModeCfg push mode, on the client (initiator). + */ +stf_status +modecfg_inI0(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + lset_t attr_set, unity_attr_set; + stf_status stat, stat_build; + + plog("parsing ModeCfg set"); + + stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + st->st_modecfg.vars_set = set_internal_addr(st->st_connection, &ia); + + /* prepare ModeCfg ack which sends zero length attributes */ + attr_set = ia.attr_set; + unity_attr_set = ia.unity_attr_set; + init_internal_addr(&ia); + ia.attr_set = attr_set & SUPPORTED_ATTR_SET; + ia.unity_attr_set = unity_attr_set & SUPPORTED_UNITY_ATTR_SET; + + plog("sending ModeCfg ack"); + + stat_build = modecfg_build_msg(st, &md->rbody + , ISAKMP_CFG_ACK + , &ia + , isama_id); + if (stat_build != STF_OK) + return stat_build; + + st->st_msgid = 0; + return STF_OK; +} + +/* STATE_MODE_CFG_R3: + * HDR*, HASH, ATTR(ACK,OK) + * + * used in ModeCfg push mode, on the server (responder) + */ +stf_status +modecfg_inR3(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat; + + plog("parsing ModeCfg ack"); + + stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + st->st_msgid = 0; + return STF_OK; +} + +/* + * Send XAUTH credentials request (username + password) + */ +stf_status +xauth_send_request(struct state *st) +{ + stf_status stat; + internal_addr_t ia; + + init_internal_addr(&ia); + ia.xauth_attr_set = LELEM(XAUTH_USER_NAME - XAUTH_BASE) + | LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE); + + plog("sending XAUTH request"); + st->st_state = STATE_XAUTH_R1; + stat = modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia); + if (stat == STF_OK) + st->st_xauth.started = TRUE; + return stat; +} + +/* STATE_XAUTH_I0: + * HDR*, HASH, ATTR(REQ) --> HDR*, HASH, ATTR(REPLY=USERNAME/PASSWORD) + * + * used on the XAUTH client (initiator) + */ +stf_status +xauth_inI0(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat, stat_build; + bool xauth_type_present; + + plog("parsing XAUTH request"); + + stat = modecfg_parse_msg(md, ISAKMP_CFG_REQUEST, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + /* check XAUTH attributes */ + xauth_type_present = (ia.xauth_attr_set & LELEM(XAUTH_TYPE - XAUTH_BASE)) != LEMPTY; + + if (xauth_type_present && ia.xauth_type != XAUTH_TYPE_GENERIC) + { + plog("xauth type %s is not supported", enum_name(&xauth_type_names, ia.xauth_type)); + stat = STF_FAIL; + } + else if ((ia.xauth_attr_set & LELEM(XAUTH_USER_NAME - XAUTH_BASE)) == LEMPTY) + { + plog("user name attribute is missing in XAUTH request"); + stat = STF_FAIL; + } + else if ((ia.xauth_attr_set & LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE)) == LEMPTY) + { + plog("user password attribute is missing in XAUTH request"); + stat = STF_FAIL; + } + + /* prepare XAUTH reply */ + init_internal_addr(&ia); + + if (stat == STF_OK) + { + /* get user credentials using a plugin function */ + if (!xauth_module.get_secret(&ia.xauth_secret)) + { + plog("xauth user credentials not found"); + stat = STF_FAIL; + } + } + if (stat == STF_OK) + { + DBG(DBG_CONTROL, + DBG_log("my xauth user name is '%.*s'" + , ia.xauth_secret.user_name.len + , ia.xauth_secret.user_name.ptr) + ) + DBG(DBG_PRIVATE, + DBG_log("my xauth user password is '%.*s'" + , ia.xauth_secret.user_password.len + , ia.xauth_secret.user_password.ptr) + ) + ia.xauth_attr_set = LELEM(XAUTH_USER_NAME - XAUTH_BASE) + | LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE); + if (xauth_type_present) + ia.xauth_attr_set |= LELEM(XAUTH_TYPE - XAUTH_BASE); + } + else + { + ia.xauth_attr_set = LELEM(XAUTH_STATUS - XAUTH_BASE); + ia.xauth_status = XAUTH_STATUS_FAIL; + } + + plog("sending XAUTH reply"); + + stat_build = modecfg_build_msg(st, &md->rbody + , ISAKMP_CFG_REPLY + , &ia + , isama_id); + if (stat_build != STF_OK) + return stat_build; + + if (stat == STF_OK) + { + st->st_xauth.started = TRUE; + st->st_msgid = 0; + return STF_OK; + } + else + { + /* send XAUTH reply msg and then delete ISAKMP SA */ + freeanychunk(st->st_tpacket); + clonetochunk(st->st_tpacket, md->reply.start + , pbs_offset(&md->reply), "XAUTH reply msg"); + send_packet(st, "XAUTH reply msg"); + delete_state(st); + return STF_IGNORE; + } +} + +/* STATE_XAUTH_R1: + * HDR*, HASH, ATTR(REPLY=USERNAME/PASSWORD) --> HDR*, HASH, ATTR(STATUS) + * + * used on the XAUTH server (responder) + */ +stf_status +xauth_inR1(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat, stat_build; + + plog("parsing XAUTH reply"); + + stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + /* did the client return an XAUTH FAIL status? */ + if ((ia.xauth_attr_set & LELEM(XAUTH_STATUS - XAUTH_BASE)) != LEMPTY) + { + plog("received FAIL status in XAUTH reply"); + + /* client is not able to do XAUTH, delete ISAKMP SA */ + delete_state(st); + return STF_IGNORE; + } + + /* check XAUTH reply */ + if ((ia.xauth_attr_set & LELEM(XAUTH_USER_NAME - XAUTH_BASE)) == LEMPTY) + { + plog("user name attribute is missing in XAUTH reply"); + st->st_xauth.status = FALSE; + } + else if ((ia.xauth_attr_set & LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE)) == LEMPTY) + { + plog("user password attribute is missing in XAUTH reply"); + st->st_xauth.status = FALSE; + } + else + { + DBG(DBG_CONTROL, + DBG_log("peer xauth user name is '%.*s'" + , ia.xauth_secret.user_name.len + , ia.xauth_secret.user_name.ptr) + ) + DBG(DBG_PRIVATE, + DBG_log("peer xauth user password is '%.*s'" + , ia.xauth_secret.user_password.len + , ia.xauth_secret.user_password.ptr) + ) + /* verify the user credentials using a plugn function */ + st->st_xauth.status = xauth_module.verify_secret(&ia.xauth_secret); + plog("extended authentication %s", st->st_xauth.status? "was successful":"failed"); + } + + /* prepare XAUTH set which sends the authentication status */ + init_internal_addr(&ia); + ia.xauth_attr_set = LELEM(XAUTH_STATUS - XAUTH_BASE); + ia.xauth_status = (st->st_xauth.status)? XAUTH_STATUS_OK : XAUTH_STATUS_FAIL; + + plog("sending XAUTH status:"); + + stat_build = modecfg_send_msg(st, ISAKMP_CFG_SET, &ia); + if (stat_build != STF_OK) + return stat_build; + return STF_OK; +} + +/* STATE_XAUTH_I1: + * HDR*, HASH, ATTR(STATUS) --> HDR*, HASH, ATTR(ACK) + * + * used on the XAUTH client (initiator) + */ +stf_status +xauth_inI1(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat, stat_build; + + plog("parsing XAUTH status"); + stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia); + if (stat != STF_OK) + { + /* notification payload - not exactly the right choice, but okay */ + md->note = ATTRIBUTES_NOT_SUPPORTED; + return stat; + } + + st->st_xauth.status = ia.xauth_status; + plog("extended authentication %s", st->st_xauth.status? "was successful":"failed"); + + plog("sending XAUTH ack"); + init_internal_addr(&ia); + stat_build = modecfg_build_msg(st, &md->rbody + , ISAKMP_CFG_ACK + , &ia + , isama_id); + if (stat_build != STF_OK) + return stat_build; + + if (st->st_xauth.status) + { + st->st_msgid = 0; + return STF_OK; + } + else + { + /* send XAUTH ack msg and then delete ISAKMP SA */ + freeanychunk(st->st_tpacket); + clonetochunk(st->st_tpacket, md->reply.start + , pbs_offset(&md->reply), "XAUTH ack msg"); + send_packet(st, "XAUTH ack msg"); + delete_state(st); + return STF_IGNORE; + } +} + +/* STATE_XAUTH_R2: + * HDR*, ATTR(STATUS), HASH --> Done + * + * used on the XAUTH server (responder) + */ +stf_status +xauth_inR2(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat; + + plog("parsing XAUTH ack"); + + stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia); + if (stat != STF_OK) + return stat; + + st->st_msgid = 0; + if (st->st_xauth.status) + { + return STF_OK; + } + else + { + delete_state(st); + return STF_IGNORE; + } +} diff --git a/src/pluto/modecfg.h b/src/pluto/modecfg.h new file mode 100644 index 000000000..68b7ef446 --- /dev/null +++ b/src/pluto/modecfg.h @@ -0,0 +1,47 @@ +/* Mode Config related functions + * Copyright (C) 2001-2002 Colubris Networks + * Copyright (C) 2003-2004 Xelerance Corporation + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: modecfg.h,v 1.1 2005/01/06 22:10:15 as Exp $ + */ + +#ifndef _MODECFG_H +#define _MODECFG_H + +struct state; +struct msg_digest; + +/* ModeConfig pull mode start function */ +extern stf_status modecfg_send_request(struct state *st); + +/* ModeConfig pull mode state transition functions */ +extern stf_status modecfg_inR0(struct msg_digest *md); +extern stf_status modecfg_inI1(struct msg_digest *md); + +/* ModeConfig push mode start function */ +extern stf_status modecfg_send_set(struct state *st); + +/* ModeConfig push mode state transition functions */ +extern stf_status modecfg_inI0(struct msg_digest *md); +extern stf_status modecfg_inR3(struct msg_digest *md); + +/* XAUTH start function */ +extern stf_status xauth_send_request(struct state *st); + +/* XAUTH state transition funcgtions */ +extern stf_status xauth_inI0(struct msg_digest *md); +extern stf_status xauth_inR1(struct msg_digest *md); +extern stf_status xauth_inI1(struct msg_digest *md); +extern stf_status xauth_inR2(struct msg_digest *md); + +#endif /* _MODECFG_H */ diff --git a/src/pluto/mp_defs.c b/src/pluto/mp_defs.c new file mode 100644 index 000000000..7ad896751 --- /dev/null +++ b/src/pluto/mp_defs.c @@ -0,0 +1,70 @@ +/* some multiprecision utilities + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: mp_defs.c,v 1.1 2006/01/05 12:37:11 as Exp $ + */ + +#include + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "log.h" + +/* Convert MP_INT to network form (binary octets, big-endian). + * We do the malloc; caller must eventually do free. + */ +chunk_t +mpz_to_n(const MP_INT *mp, size_t bytes) +{ + chunk_t r; + MP_INT temp1, temp2; + int i; + + r.len = bytes; + r.ptr = alloc_bytes(r.len, "host representation of large integer"); + + mpz_init(&temp1); + mpz_init(&temp2); + + mpz_set(&temp1, mp); + + for (i = r.len-1; i >= 0; i--) + { + r.ptr[i] = mpz_mdivmod_ui(&temp2, NULL, &temp1, 1 << BITS_PER_BYTE); + mpz_set(&temp1, &temp2); + } + + passert(mpz_sgn(&temp1) == 0); /* we must have done all the bits */ + mpz_clear(&temp1); + mpz_clear(&temp2); + + return r; +} + +/* Convert network form (binary bytes, big-endian) to MP_INT. + * The *mp must not be previously mpz_inited. + */ +void +n_to_mpz(MP_INT *mp, const u_char *nbytes, size_t nlen) +{ + size_t i; + + mpz_init_set_ui(mp, 0); + + for (i = 0; i != nlen; i++) + { + mpz_mul_ui(mp, mp, 1 << BITS_PER_BYTE); + mpz_add_ui(mp, mp, nbytes[i]); + } +} diff --git a/src/pluto/mp_defs.h b/src/pluto/mp_defs.h new file mode 100644 index 000000000..744a028d1 --- /dev/null +++ b/src/pluto/mp_defs.h @@ -0,0 +1,36 @@ +/* some multiprecision utilities + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: mp_defs.h,v 1.2 2006/01/06 11:40:45 as Exp $ + */ + +#ifndef _MP_DEFS_H +#define _MP_DEFS_H + +#include + +#include "defs.h" + +extern void n_to_mpz(MP_INT *mp, const u_char *nbytes, size_t nlen); +extern chunk_t mpz_to_n(const MP_INT *mp, size_t bytes); + +/* var := mod(base ** exp, mod), ensuring var is mpz_inited */ +#define mpz_init_powm(flag, var, base, exp, mod) { \ + if (!(flag)) \ + mpz_init(&(var)); \ + (flag) = TRUE; \ + mpz_powm(&(var), &(base), &(exp), (mod)); \ + } + +#endif /* _MP_DEFS_H */ diff --git a/src/pluto/nat_traversal.c b/src/pluto/nat_traversal.c new file mode 100644 index 000000000..4a52cc107 --- /dev/null +++ b/src/pluto/nat_traversal.c @@ -0,0 +1,866 @@ +/* FreeS/WAN NAT-Traversal + * Copyright (C) 2002-2005 Mathieu Lafon - Arkoon Network Security + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: nat_traversal.c,v 1.8 2005/01/06 22:36:58 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* used only if MSG_NOSIGNAL not defined */ +#include + +#include +#include +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "server.h" +#include "state.h" +#include "connections.h" +#include "packet.h" +#include "demux.h" +#include "kernel.h" +#include "whack.h" +#include "timer.h" +#include "cookie.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" +#include "vendor.h" +#include "ike_alg.h" +#include "nat_traversal.h" + +/* #define FORCE_NAT_TRAVERSAL */ +#define NAT_D_DEBUG +#define NAT_T_SUPPORT_LAST_DRAFTS + +#ifndef SOL_UDP +#define SOL_UDP 17 +#endif + +#ifndef UDP_ESPINUDP +#define UDP_ESPINUDP 100 +#endif + +#define DEFAULT_KEEP_ALIVE_PERIOD 20 + +#ifdef _IKE_ALG_H +/* Alg patch: hash_digest_len -> hash_digest_size */ +#define hash_digest_len hash_digest_size +#endif + +bool nat_traversal_enabled = FALSE; +bool nat_traversal_support_non_ike = FALSE; +bool nat_traversal_support_port_floating = FALSE; + +static unsigned int _kap = 0; +static unsigned int _ka_evt = 0; +static bool _force_ka = 0; + +static const char *natt_version = "0.6c"; + +void init_nat_traversal (bool activate, unsigned int keep_alive_period, + bool fka, bool spf) +{ + nat_traversal_enabled = activate; + nat_traversal_support_non_ike = activate; +#ifdef NAT_T_SUPPORT_LAST_DRAFTS + nat_traversal_support_port_floating = activate ? spf : FALSE; +#endif + _force_ka = fka; + _kap = keep_alive_period ? keep_alive_period : DEFAULT_KEEP_ALIVE_PERIOD; + plog(" including NAT-Traversal patch (Version %s)%s%s%s" + , natt_version, activate ? "" : " [disabled]" + , activate & fka ? " [Force KeepAlive]" : "" + , activate & !spf ? " [Port Floating disabled]" : ""); +} + +static void disable_nat_traversal (int type) +{ + if (type == ESPINUDP_WITH_NON_IKE) + nat_traversal_support_non_ike = FALSE; + else + nat_traversal_support_port_floating = FALSE; + + if (!nat_traversal_support_non_ike && + !nat_traversal_support_port_floating) + nat_traversal_enabled = FALSE; +} + +static void _natd_hash(const struct hash_desc *hasher, char *hash, + u_int8_t *icookie, u_int8_t *rcookie, + const ip_address *ip, u_int16_t port) +{ + union hash_ctx ctx; + + if (is_zero_cookie(icookie)) + DBG_log("_natd_hash: Warning, icookie is zero !!"); + if (is_zero_cookie(rcookie)) + DBG_log("_natd_hash: Warning, rcookie is zero !!"); + + /** + * draft-ietf-ipsec-nat-t-ike-01.txt + * + * HASH = HASH(CKY-I | CKY-R | IP | Port) + * + * All values in network order + */ + hasher->hash_init(&ctx); + hasher->hash_update(&ctx, icookie, COOKIE_SIZE); + hasher->hash_update(&ctx, rcookie, COOKIE_SIZE); + switch (addrtypeof(ip)) { + case AF_INET: + hasher->hash_update(&ctx, (const u_char *)&ip->u.v4.sin_addr.s_addr + , sizeof(ip->u.v4.sin_addr.s_addr)); + break; + case AF_INET6: + hasher->hash_update(&ctx, (const u_char *)&ip->u.v6.sin6_addr.s6_addr + , sizeof(ip->u.v6.sin6_addr.s6_addr)); + break; + } + hasher->hash_update(&ctx, (const u_char *)&port, sizeof(u_int16_t)); + hasher->hash_final(hash, &ctx); +#ifdef NAT_D_DEBUG + DBG(DBG_NATT, + DBG_log("_natd_hash: hasher=%p(%d)", hasher, (int)hasher->hash_digest_len); + DBG_dump("_natd_hash: icookie=", icookie, COOKIE_SIZE); + DBG_dump("_natd_hash: rcookie=", rcookie, COOKIE_SIZE); + switch (addrtypeof(ip)) { + case AF_INET: + DBG_dump("_natd_hash: ip=", &ip->u.v4.sin_addr.s_addr + , sizeof(ip->u.v4.sin_addr.s_addr)); + break; + } + DBG_log("_natd_hash: port=%d", port); + DBG_dump("_natd_hash: hash=", hash, hasher->hash_digest_len); + ); +#endif +} + +/* Add NAT-Traversal VIDs (supported ones) + * used when we are Initiator + */ +bool nat_traversal_add_vid(u_int8_t np, pb_stream *outs) +{ + bool r = TRUE; + + if (nat_traversal_support_port_floating) + { + u_int8_t last_np = nat_traversal_support_non_ike ? + ISAKMP_NEXT_VID : np; + + if (r) + r = out_vendorid(ISAKMP_NEXT_VID, outs, VID_NATT_RFC); + if (r) + r = out_vendorid(ISAKMP_NEXT_VID, outs, VID_NATT_IETF_03); + if (r) + r = out_vendorid(ISAKMP_NEXT_VID, outs, VID_NATT_IETF_02); + if (r) + r = out_vendorid(last_np, outs, VID_NATT_IETF_02_N); + } + if (nat_traversal_support_non_ike) + { + if (r) + r = out_vendorid(np, outs, VID_NATT_IETF_00); + } + return r; +} + +u_int32_t nat_traversal_vid_to_method(unsigned short nat_t_vid) +{ + switch (nat_t_vid) + { + case VID_NATT_IETF_00: + return LELEM(NAT_TRAVERSAL_IETF_00_01); + case VID_NATT_IETF_02: + case VID_NATT_IETF_02_N: + case VID_NATT_IETF_03: + return LELEM(NAT_TRAVERSAL_IETF_02_03); + case VID_NATT_RFC: + return LELEM(NAT_TRAVERSAL_RFC); + } + return 0; +} + +void nat_traversal_natd_lookup(struct msg_digest *md) +{ + char hash[MAX_DIGEST_LEN]; + struct payload_digest *p; + struct state *st = md->st; + int i; + + if (!st || !md->iface || !st->st_oakley.hasher) + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d" + , __FILE__, __LINE__); + return; + } + + /** Count NAT-D **/ + for (p = md->chain[ISAKMP_NEXT_NATD_RFC], i=0; p != NULL; p = p->next, i++); + + /* + * We need at least 2 NAT-D (1 for us, many for peer) + */ + if (i < 2) + { + loglog(RC_LOG_SERIOUS, + "NAT-Traversal: Only %d NAT-D - Aborting NAT-Traversal negociation", i); + st->nat_traversal = 0; + return; + } + + /* + * First one with my IP & port + */ + p = md->chain[ISAKMP_NEXT_NATD_RFC]; + _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, st->st_rcookie, + &(md->iface->addr), ntohs(st->st_connection->spd.this.host_port)); + + if (!(pbs_left(&p->pbs) == st->st_oakley.hasher->hash_digest_len && + memcmp(p->pbs.cur, hash, st->st_oakley.hasher->hash_digest_len) == 0)) + { +#ifdef NAT_D_DEBUG + DBG(DBG_NATT, + DBG_log("NAT_TRAVERSAL_NAT_BHND_ME"); + DBG_dump("expected NAT-D:", hash + , st->st_oakley.hasher->hash_digest_len); + DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs)); + ) +#endif + st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME); + } + + /* + * The others with sender IP & port + */ + _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, st->st_rcookie, + &(md->sender), ntohs(md->sender_port)); + for (p = p->next, i=0 ; p != NULL; p = p->next) + { + if (pbs_left(&p->pbs) == st->st_oakley.hasher->hash_digest_len && + memcmp(p->pbs.cur, hash, st->st_oakley.hasher->hash_digest_len) == 0) + { + i++; + } + } + if (!i) + { +#ifdef NAT_D_DEBUG + DBG(DBG_NATT, + DBG_log("NAT_TRAVERSAL_NAT_BHND_PEER"); + DBG_dump("expected NAT-D:", hash + , st->st_oakley.hasher->hash_digest_len); + p = md->chain[ISAKMP_NEXT_NATD_RFC]; + for (p = p->next, i=0 ; p != NULL; p = p->next) + { + DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs)); + } + ) +#endif + st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); + } +#ifdef FORCE_NAT_TRAVERSAL + st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); + st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME); +#endif +} + +bool nat_traversal_add_natd(u_int8_t np, pb_stream *outs, + struct msg_digest *md) +{ + char hash[MAX_DIGEST_LEN]; + struct state *st = md->st; + + if (!st || !st->st_oakley.hasher) + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d" + , __FILE__, __LINE__); + return FALSE; + } + + DBG(DBG_EMITTING, + DBG_log("sending NATD payloads") + ) + + /* + * First one with sender IP & port + */ + _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, + is_zero_cookie(st->st_rcookie) ? md->hdr.isa_rcookie : st->st_rcookie, + &(md->sender), +#ifdef FORCE_NAT_TRAVERSAL + 0 +#else + ntohs(md->sender_port) +#endif + ); + if (!out_generic_raw((st->nat_traversal & NAT_T_WITH_RFC_VALUES + ? ISAKMP_NEXT_NATD_RFC : ISAKMP_NEXT_NATD_DRAFTS), &isakmp_nat_d, outs, + hash, st->st_oakley.hasher->hash_digest_len, "NAT-D")) + { + return FALSE; + } + + /* + * Second one with my IP & port + */ + _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, + is_zero_cookie(st->st_rcookie) ? md->hdr.isa_rcookie : st->st_rcookie, + &(md->iface->addr), +#ifdef FORCE_NAT_TRAVERSAL + 0 +#else + ntohs(st->st_connection->spd.this.host_port) +#endif + ); + return (out_generic_raw(np, &isakmp_nat_d, outs, + hash, st->st_oakley.hasher->hash_digest_len, "NAT-D")); +} + +/* + * nat_traversal_natoa_lookup() + * + * Look for NAT-OA in message + */ +void nat_traversal_natoa_lookup(struct msg_digest *md) +{ + struct payload_digest *p; + struct state *st = md->st; + int i; + ip_address ip; + + if (!st || !md->iface) + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d" + , __FILE__, __LINE__); + return; + } + + /* Initialize NAT-OA */ + anyaddr(AF_INET, &st->nat_oa); + + /* Count NAT-OA **/ + for (p = md->chain[ISAKMP_NEXT_NATOA_RFC], i=0; p != NULL; p = p->next, i++); + + DBG(DBG_NATT, + DBG_log("NAT-Traversal: received %d NAT-OA.", i) + ) + + if (i == 0) + return; + + if (!(st->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_PEER))) + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: received %d NAT-OA. " + "ignored because peer is not NATed", i); + return; + } + + if (i > 1) + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: received %d NAT-OA. " + "using first, ignoring others", i); + } + + /* Take first */ + p = md->chain[ISAKMP_NEXT_NATOA_RFC]; + + DBG(DBG_PARSING, + DBG_dump("NAT-OA:", p->pbs.start, pbs_room(&p->pbs)); + ); + + switch (p->payload.nat_oa.isanoa_idtype) + { + case ID_IPV4_ADDR: + if (pbs_left(&p->pbs) == sizeof(struct in_addr)) + { + initaddr(p->pbs.cur, pbs_left(&p->pbs), AF_INET, &ip); + } + else + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: received IPv4 NAT-OA " + "with invalid IP size (%d)", (int)pbs_left(&p->pbs)); + return; + } + break; + case ID_IPV6_ADDR: + if (pbs_left(&p->pbs) == sizeof(struct in6_addr)) + { + initaddr(p->pbs.cur, pbs_left(&p->pbs), AF_INET6, &ip); + } + else + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: received IPv6 NAT-OA " + "with invalid IP size (%d)", (int)pbs_left(&p->pbs)); + return; + } + break; + default: + loglog(RC_LOG_SERIOUS, "NAT-Traversal: " + "invalid ID Type (%d) in NAT-OA - ignored", + p->payload.nat_oa.isanoa_idtype); + return; + } + + DBG(DBG_NATT, + { + char ip_t[ADDRTOT_BUF]; + addrtot(&ip, 0, ip_t, sizeof(ip_t)); + + DBG_log("received NAT-OA: %s", ip_t); + } + ) + + if (isanyaddr(&ip)) + loglog(RC_LOG_SERIOUS, "NAT-Traversal: received %%any NAT-OA..."); + else + st->nat_oa = ip; +} + +bool nat_traversal_add_natoa(u_int8_t np, pb_stream *outs, + struct state *st) +{ + struct isakmp_nat_oa natoa; + pb_stream pbs; + unsigned char ip_val[sizeof(struct in6_addr)]; + size_t ip_len = 0; + ip_address *ip; + + if ((!st) || (!st->st_connection)) + { + loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d" + , __FILE__, __LINE__); + return FALSE; + } + ip = &(st->st_connection->spd.this.host_addr); + + memset(&natoa, 0, sizeof(natoa)); + natoa.isanoa_np = np; + + switch (addrtypeof(ip)) + { + case AF_INET: + ip_len = sizeof(ip->u.v4.sin_addr.s_addr); + memcpy(ip_val, &ip->u.v4.sin_addr.s_addr, ip_len); + natoa.isanoa_idtype = ID_IPV4_ADDR; + break; + case AF_INET6: + ip_len = sizeof(ip->u.v6.sin6_addr.s6_addr); + memcpy(ip_val, &ip->u.v6.sin6_addr.s6_addr, ip_len); + natoa.isanoa_idtype = ID_IPV6_ADDR; + break; + default: + loglog(RC_LOG_SERIOUS, "NAT-Traversal: " + "invalid addrtypeof()=%d", addrtypeof(ip)); + return FALSE; + } + + if (!out_struct(&natoa, &isakmp_nat_oa, outs, &pbs)) + return FALSE; + + if (!out_raw(ip_val, ip_len, &pbs, "NAT-OA")) + return FALSE; + + DBG(DBG_NATT, + DBG_dump("NAT-OA (S):", ip_val, ip_len) + ) + + close_output_pbs(&pbs); + return TRUE; +} + +void nat_traversal_show_result (u_int32_t nt, u_int16_t sport) +{ + const char *mth = NULL, *rslt = NULL; + + switch (nt & NAT_TRAVERSAL_METHOD) + { + case LELEM(NAT_TRAVERSAL_IETF_00_01): + mth = natt_type_bitnames[0]; + break; + case LELEM(NAT_TRAVERSAL_IETF_02_03): + mth = natt_type_bitnames[1]; + break; + case LELEM(NAT_TRAVERSAL_RFC): + mth = natt_type_bitnames[2]; + break; + } + + switch (nt & NAT_T_DETECTED) + { + case 0: + rslt = "no NAT detected"; + break; + case LELEM(NAT_TRAVERSAL_NAT_BHND_ME): + rslt = "i am NATed"; + break; + case LELEM(NAT_TRAVERSAL_NAT_BHND_PEER): + rslt = "peer is NATed"; + break; + case LELEM(NAT_TRAVERSAL_NAT_BHND_ME) | LELEM(NAT_TRAVERSAL_NAT_BHND_PEER): + rslt = "both are NATed"; + break; + } + + loglog(RC_LOG_SERIOUS, + "NAT-Traversal: Result using %s: %s", + mth ? mth : "unknown method", + rslt ? rslt : "unknown result" + ); + + if ((nt & LELEM(NAT_TRAVERSAL_NAT_BHND_PEER)) + && (sport == IKE_UDP_PORT) + && ((nt & NAT_T_WITH_PORT_FLOATING)==0)) + { + loglog(RC_LOG_SERIOUS, + "Warning: peer is NATed but source port is still udp/%d. " + "Ipsec-passthrough NAT device suspected -- NAT-T may not work.", + IKE_UDP_PORT + ); + } +} + +int nat_traversal_espinudp_socket (int sk, u_int32_t type) +{ + int r = setsockopt(sk, SOL_UDP, UDP_ESPINUDP, &type, sizeof(type)); + + if (r < 0 && errno == ENOPROTOOPT) + { + loglog(RC_LOG_SERIOUS, + "NAT-Traversal: ESPINUDP(%d) not supported by kernel -- " + "NAT-T disabled", type); + disable_nat_traversal(type); + } + return r; +} + +void nat_traversal_new_ka_event (void) +{ + if (_ka_evt) + return; /* event already scheduled */ + + event_schedule(EVENT_NAT_T_KEEPALIVE, _kap, NULL); + _ka_evt = 1; +} + +static void nat_traversal_send_ka (struct state *st) +{ + static unsigned char ka_payload = 0xff; + chunk_t sav; + + DBG(DBG_NATT, + DBG_log("ka_event: send NAT-KA to %s:%d", + ip_str(&st->st_connection->spd.that.host_addr), + st->st_connection->spd.that.host_port); + ) + + /* save state chunk */ + setchunk(sav, st->st_tpacket.ptr, st->st_tpacket.len); + + /* send keep alive */ + setchunk(st->st_tpacket, &ka_payload, 1); + send_packet(st, "NAT-T Keep Alive"); + + /* restore state chunk */ + setchunk(st->st_tpacket, sav.ptr, sav.len); +} + +/** + * Find ISAKMP States with NAT-T and send keep-alive + */ +static void nat_traversal_ka_event_state (struct state *st, void *data) +{ + unsigned int *_kap_st = (unsigned int *)data; + const struct connection *c = st->st_connection; + + if (!c) + return; + + if ((st->st_state == STATE_MAIN_R3 || st->st_state == STATE_MAIN_I4) + && (st->nat_traversal & NAT_T_DETECTED) + && ((st->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME)) || _force_ka)) + { + /* + * - ISAKMP established + * - NAT-Traversal detected + * - NAT-KeepAlive needed (we are NATed) + */ + if (c->newest_isakmp_sa != st->st_serialno) + { + /* + * if newest is also valid, ignore this one, we will only use + * newest. + */ + struct state *st_newest; + + st_newest = state_with_serialno(c->newest_isakmp_sa); + if (st_newest + && (st_newest->st_state == STATE_MAIN_R3 || st_newest->st_state == STATE_MAIN_I4) + && (st_newest->nat_traversal & NAT_T_DETECTED) + && ((st_newest->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME)) || _force_ka)) + { + return; + } + } + set_cur_state(st); + nat_traversal_send_ka(st); + reset_cur_state(); + (*_kap_st)++; + } +} + +void nat_traversal_ka_event (void) +{ + unsigned int _kap_st = 0; + + _ka_evt = 0; /* ready to be reschedule */ + + for_each_state((void *)nat_traversal_ka_event_state, &_kap_st); + + /* if there are still states who needs Keep-Alive, schedule new event */ + if (_kap_st) + nat_traversal_new_ka_event(); +} + +struct _new_mapp_nfo { + ip_address addr; + u_int16_t sport, dport; +}; + +static void nat_traversal_find_new_mapp_state (struct state *st, void *data) +{ + struct connection *c = st->st_connection; + struct _new_mapp_nfo *nfo = (struct _new_mapp_nfo *)data; + + if (c != NULL + && sameaddr(&c->spd.that.host_addr, &(nfo->addr)) + && c->spd.that.host_port == nfo->sport) + { + + /* change host port */ + c->spd.that.host_port = nfo->dport; + + if (IS_IPSEC_SA_ESTABLISHED(st->st_state) + || IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(st->st_state)) + { + if (!update_ipsec_sa(st)) + { + /* + * If ipsec update failed, restore old port or we'll + * not be able to update anymore. + */ + c->spd.that.host_port = nfo->sport; + } + } + } +} + +static int nat_traversal_new_mapping(const ip_address *src, u_int16_t sport, + const ip_address *dst, u_int16_t dport) +{ + char srca[ADDRTOT_BUF], dsta[ADDRTOT_BUF]; + struct _new_mapp_nfo nfo; + + addrtot(src, 0, srca, ADDRTOT_BUF); + addrtot(dst, 0, dsta, ADDRTOT_BUF); + + if (!sameaddr(src, dst)) + { + loglog(RC_LOG_SERIOUS, "nat_traversal_new_mapping: " + "address change currently not supported [%s:%d,%s:%d]", + srca, sport, dsta, dport); + return -1; + } + + if (sport == dport) + { + /* no change */ + return 0; + } + + DBG_log("NAT-T: new mapping %s:%d/%d)", srca, sport, dport); + + nfo.addr = *src; + nfo.sport = sport; + nfo.dport = dport; + + for_each_state((void *)nat_traversal_find_new_mapp_state, &nfo); + + return 0; +} + +void nat_traversal_change_port_lookup(struct msg_digest *md, struct state *st) +{ + struct connection *c = st ? st->st_connection : NULL; + struct iface *i = NULL; + + if ((st == NULL) || (c == NULL)) + return; + + if (md) + { + /* + * If source port has changed, update (including other states and + * established kernel SA) + */ + if (c->spd.that.host_port != md->sender_port) + { + nat_traversal_new_mapping(&c->spd.that.host_addr, c->spd.that.host_port, + &c->spd.that.host_addr, md->sender_port); + } + + /* + * If interface type has changed, update local port (500/4500) + */ + if ((c->spd.this.host_port == NAT_T_IKE_FLOAT_PORT && !md->iface->ike_float) + || (c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT && md->iface->ike_float)) + { + c->spd.this.host_port = (md->iface->ike_float) + ? NAT_T_IKE_FLOAT_PORT : pluto_port; + + DBG(DBG_NATT, + DBG_log("NAT-T: updating local port to %d", c->spd.this.host_port); + ); + } + } + + /* + * If we're initiator and NAT-T (with port floating) is detected, we + * need to change port (MAIN_I3 or QUICK_I1) + */ + if ((st->st_state == STATE_MAIN_I3 || st->st_state == STATE_QUICK_I1) + && (st->nat_traversal & NAT_T_WITH_PORT_FLOATING) + && (st->nat_traversal & NAT_T_DETECTED) + && (c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT)) + { + DBG(DBG_NATT, + DBG_log("NAT-T: floating to port %d", NAT_T_IKE_FLOAT_PORT); + ) + c->spd.this.host_port = NAT_T_IKE_FLOAT_PORT; + c->spd.that.host_port = NAT_T_IKE_FLOAT_PORT; + /* + * Also update pending connections or they will be deleted if uniqueids + * option is set. + */ + update_pending(st, st); + } + + /* + * Find valid interface according to local port (500/4500) + */ + if ((c->spd.this.host_port == NAT_T_IKE_FLOAT_PORT && !c->interface->ike_float) + || (c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT && c->interface->ike_float)) + { + for (i = interfaces; i != NULL; i = i->next) + { + if (sameaddr(&c->interface->addr, &i->addr) + && i->ike_float != c->interface->ike_float) + { + DBG(DBG_NATT, + DBG_log("NAT-T: using interface %s:%d", i->rname, + i->ike_float ? NAT_T_IKE_FLOAT_PORT : pluto_port); + ) + c->interface = i; + break; + } + } + } +} + +struct _new_klips_mapp_nfo { + struct sadb_sa *sa; + ip_address src, dst; + u_int16_t sport, dport; +}; + +static void nat_t_new_klips_mapp (struct state *st, void *data) +{ + struct connection *c = st->st_connection; + struct _new_klips_mapp_nfo *nfo = (struct _new_klips_mapp_nfo *)data; + + if (c != NULL && st->st_esp.present + && sameaddr(&c->spd.that.host_addr, &(nfo->src)) + && st->st_esp.our_spi == nfo->sa->sadb_sa_spi) + { + nat_traversal_new_mapping(&c->spd.that.host_addr, c->spd.that.host_port, + &(nfo->dst), nfo->dport); + } +} + +void process_pfkey_nat_t_new_mapping( + struct sadb_msg *msg __attribute__ ((unused)), + struct sadb_ext *extensions[SADB_EXT_MAX + 1]) +{ + struct _new_klips_mapp_nfo nfo; + struct sadb_address *srcx = (void *) extensions[SADB_EXT_ADDRESS_SRC]; + struct sadb_address *dstx = (void *) extensions[SADB_EXT_ADDRESS_DST]; + struct sockaddr *srca, *dsta; + err_t ugh = NULL; + + nfo.sa = (void *) extensions[SADB_EXT_SA]; + + if (!nfo.sa || !srcx || !dstx) + { + plog("SADB_X_NAT_T_NEW_MAPPING message from KLIPS malformed: " + "got NULL params"); + return; + } + + srca = ((struct sockaddr *)(void *)&srcx[1]); + dsta = ((struct sockaddr *)(void *)&dstx[1]); + + if (srca->sa_family != AF_INET || dsta->sa_family != AF_INET) + { + ugh = "only AF_INET supported"; + } + else + { + char text_said[SATOT_BUF]; + char _srca[ADDRTOT_BUF], _dsta[ADDRTOT_BUF]; + ip_said said; + + initaddr((const void *) &((const struct sockaddr_in *)srca)->sin_addr, + sizeof(((const struct sockaddr_in *)srca)->sin_addr), + srca->sa_family, &(nfo.src)); + nfo.sport = ntohs(((const struct sockaddr_in *)srca)->sin_port); + initaddr((const void *) &((const struct sockaddr_in *)dsta)->sin_addr, + sizeof(((const struct sockaddr_in *)dsta)->sin_addr), + dsta->sa_family, &(nfo.dst)); + nfo.dport = ntohs(((const struct sockaddr_in *)dsta)->sin_port); + + DBG(DBG_NATT, + initsaid(&nfo.src, nfo.sa->sadb_sa_spi, SA_ESP, &said); + satot(&said, 0, text_said, SATOT_BUF); + addrtot(&nfo.src, 0, _srca, ADDRTOT_BUF); + addrtot(&nfo.dst, 0, _dsta, ADDRTOT_BUF); + DBG_log("new klips mapping %s %s:%d %s:%d", + text_said, _srca, nfo.sport, _dsta, nfo.dport); + ) + + for_each_state((void *)nat_t_new_klips_mapp, &nfo); + } + + if (ugh != NULL) + plog("SADB_X_NAT_T_NEW_MAPPING message from KLIPS malformed: %s", ugh); +} + diff --git a/src/pluto/nat_traversal.h b/src/pluto/nat_traversal.h new file mode 100644 index 000000000..71222c54c --- /dev/null +++ b/src/pluto/nat_traversal.h @@ -0,0 +1,154 @@ +/* FreeS/WAN NAT-Traversal + * Copyright (C) 2002-2003 Mathieu Lafon - Arkoon Network Security + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: nat_traversal.h,v 1.4 2004/07/27 21:11:30 as Exp $ + */ + +#ifndef _NAT_TRAVERSAL_H +#define _NAT_TRAVERSAL_H + +#include "packet.h" + +#define NAT_TRAVERSAL_IETF_00_01 1 +#define NAT_TRAVERSAL_IETF_02_03 2 +#define NAT_TRAVERSAL_RFC 3 + +#define NAT_TRAVERSAL_NAT_BHND_ME 30 +#define NAT_TRAVERSAL_NAT_BHND_PEER 31 + +#define NAT_TRAVERSAL_METHOD (0xffffffff - LELEM(30) - LELEM(31)) + +/** + * NAT-Traversal methods which need NAT-D + */ +#define NAT_T_WITH_NATD \ + ( LELEM(NAT_TRAVERSAL_IETF_00_01) | LELEM(NAT_TRAVERSAL_IETF_02_03) | \ + LELEM(NAT_TRAVERSAL_RFC) ) +/** + * NAT-Traversal methods which need NAT-OA + */ +#define NAT_T_WITH_NATOA \ + ( LELEM(NAT_TRAVERSAL_IETF_00_01) | LELEM(NAT_TRAVERSAL_IETF_02_03) | \ + LELEM(NAT_TRAVERSAL_RFC) ) +/** + * NAT-Traversal methods which use NAT-KeepAlive + */ +#define NAT_T_WITH_KA \ + ( LELEM(NAT_TRAVERSAL_IETF_00_01) | LELEM(NAT_TRAVERSAL_IETF_02_03) | \ + LELEM(NAT_TRAVERSAL_RFC) ) +/** + * NAT-Traversal methods which use floating port + */ +#define NAT_T_WITH_PORT_FLOATING \ + ( LELEM(NAT_TRAVERSAL_IETF_02_03) | LELEM(NAT_TRAVERSAL_RFC) ) + +/** + * NAT-Traversal methods which use officials values (RFC) + */ +#define NAT_T_WITH_RFC_VALUES \ + ( LELEM(NAT_TRAVERSAL_RFC) ) + +/** + * NAT-Traversal detected + */ +#define NAT_T_DETECTED \ + ( LELEM(NAT_TRAVERSAL_NAT_BHND_ME) | LELEM(NAT_TRAVERSAL_NAT_BHND_PEER) ) + +/** + * NAT-T Port Floating + */ +#define NAT_T_IKE_FLOAT_PORT 4500 + +void init_nat_traversal (bool activate, unsigned int keep_alive_period, + bool fka, bool spf); + +extern bool nat_traversal_enabled; +extern bool nat_traversal_support_non_ike; +extern bool nat_traversal_support_port_floating; + +/** + * NAT-D + */ +void nat_traversal_natd_lookup(struct msg_digest *md); +#ifndef PB_STREAM_UNDEFINED +bool nat_traversal_add_natd(u_int8_t np, pb_stream *outs, + struct msg_digest *md); +#endif + +/** + * NAT-OA + */ +void nat_traversal_natoa_lookup(struct msg_digest *md); +#ifndef PB_STREAM_UNDEFINED +bool nat_traversal_add_natoa(u_int8_t np, pb_stream *outs, + struct state *st); +#endif + +/** + * NAT-keep_alive + */ +void nat_traversal_new_ka_event (void); +void nat_traversal_ka_event (void); + +void nat_traversal_show_result (u_int32_t nt, u_int16_t sport); + +int nat_traversal_espinudp_socket (int sk, u_int32_t type); + +/** + * Vendor ID + */ +#ifndef PB_STREAM_UNDEFINED +bool nat_traversal_add_vid(u_int8_t np, pb_stream *outs); +#endif +u_int32_t nat_traversal_vid_to_method(unsigned short nat_t_vid); + +void nat_traversal_change_port_lookup(struct msg_digest *md, struct state *st); + +/** + * New NAT mapping + */ +#ifdef __PFKEY_V2_H +void process_pfkey_nat_t_new_mapping( + struct sadb_msg *, + struct sadb_ext *[SADB_EXT_MAX + 1]); +#endif + +/** + * IKE port floating + */ +bool +nat_traversal_port_float(struct state *st, struct msg_digest *md, bool in); + +/** + * Encapsulation mode macro (see demux.c) + */ +#define NAT_T_ENCAPSULATION_MODE(st,nat_t_policy) ( \ + ((st)->nat_traversal & NAT_T_DETECTED) \ + ? ( ((nat_t_policy) & POLICY_TUNNEL) \ + ? ( ((st)->nat_traversal & NAT_T_WITH_RFC_VALUES) \ + ? (ENCAPSULATION_MODE_UDP_TUNNEL_RFC) \ + : (ENCAPSULATION_MODE_UDP_TUNNEL_DRAFTS) \ + ) \ + : ( ((st)->nat_traversal & NAT_T_WITH_RFC_VALUES) \ + ? (ENCAPSULATION_MODE_UDP_TRANSPORT_RFC) \ + : (ENCAPSULATION_MODE_UDP_TRANSPORT_DRAFTS) \ + ) \ + ) \ + : ( ((st)->st_policy & POLICY_TUNNEL) \ + ? (ENCAPSULATION_MODE_TUNNEL) \ + : (ENCAPSULATION_MODE_TRANSPORT) \ + ) \ + ) + +#endif /* _NAT_TRAVERSAL_H */ + diff --git a/src/pluto/ocsp.c b/src/pluto/ocsp.c new file mode 100644 index 000000000..a338be446 --- /dev/null +++ b/src/pluto/ocsp.c @@ -0,0 +1,1568 @@ +/* Support of the Online Certificate Status Protocol (OCSP) + * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen + * Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "x509.h" +#include "crl.h" +#include "ca.h" +#include "rnd.h" +#include "asn1.h" +#include "certs.h" +#include "smartcard.h" +#include "oid.h" +#include "whack.h" +#include "pkcs1.h" +#include "keys.h" +#include "fetch.h" +#include "ocsp.h" + +#define NONCE_LENGTH 16 + +static const char *const cert_status_names[] = { + "good", + "revoked", + "unknown", + "undefined" +}; + + +static const char *const response_status_names[] = { + "successful", + "malformed request", + "internal error", + "try later", + "signature required", + "unauthorized" +}; + +/* response container */ +typedef struct response response_t; + +struct response { + chunk_t tbs; + chunk_t responder_id_name; + chunk_t responder_id_key; + time_t produced_at; + chunk_t responses; + chunk_t nonce; + int algorithm; + chunk_t signature; +}; + +const response_t empty_response = { + { NULL, 0 } , /* tbs */ + { NULL, 0 } , /* responder_id_name */ + { NULL, 0 } , /* responder_id_key */ + UNDEFINED_TIME, /* produced_at */ + { NULL, 0 } , /* single_response */ + { NULL, 0 } , /* nonce */ + OID_UNKNOWN , /* signature_algorithm */ + { NULL, 0 } /* signature */ +}; + +/* single response container */ +typedef struct single_response single_response_t; + +struct single_response { + single_response_t *next; + int hash_algorithm; + chunk_t issuer_name_hash; + chunk_t issuer_key_hash; + chunk_t serialNumber; + cert_status_t status; + time_t revocationTime; + crl_reason_t revocationReason; + time_t thisUpdate; + time_t nextUpdate; +}; + +const single_response_t empty_single_response = { + NULL , /* *next */ + OID_UNKNOWN , /* hash_algorithm */ + { NULL, 0 } , /* issuer_name_hash */ + { NULL, 0 } , /* issuer_key_hash */ + { NULL, 0 } , /* serial_number */ + CERT_UNDEFINED , /* status */ + UNDEFINED_TIME , /* revocationTime */ + REASON_UNSPECIFIED, /* revocationReason */ + UNDEFINED_TIME , /* this_update */ + UNDEFINED_TIME /* next_update */ +}; + + +/* list of single requests */ +typedef struct request_list request_list_t; +struct request_list { + chunk_t request; + request_list_t *next; +}; + +/* some OCSP specific prefabricated ASN.1 constants */ + +static u_char ASN1_nonce_oid_str[] = { + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02 +}; + +static const chunk_t ASN1_nonce_oid = strchunk(ASN1_nonce_oid_str); + +static u_char ASN1_response_oid_str[] = { + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04 +}; + +static const chunk_t ASN1_response_oid = strchunk(ASN1_response_oid_str); + +static u_char ASN1_response_content_str[] = { + 0x04, 0x0D, + 0x30, 0x0B, + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 +}; + +static const chunk_t ASN1_response_content = strchunk(ASN1_response_content_str); + +/* default OCSP uri */ +static chunk_t ocsp_default_uri; + +/* ocsp cache: pointer to first element */ +static ocsp_location_t *ocsp_cache = NULL; + +/* static temporary storage for ocsp requestor information */ +static x509cert_t *ocsp_requestor_cert = NULL; + +static smartcard_t *ocsp_requestor_sc = NULL; + +static const struct RSA_private_key *ocsp_requestor_pri = NULL; + +/* asn.1 definitions for parsing */ + +static const asn1Object_t ocspResponseObjects[] = { + { 0, "OCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "responseStatus", ASN1_ENUMERATED, ASN1_BODY }, /* 1 */ + { 1, "responseBytesContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 2 */ + { 2, "responseBytes", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ + { 3, "responseType", ASN1_OID, ASN1_BODY }, /* 4 */ + { 3, "response", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 6 */ +}; + +#define OCSP_RESPONSE_STATUS 1 +#define OCSP_RESPONSE_TYPE 4 +#define OCSP_RESPONSE 5 +#define OCSP_RESPONSE_ROOF 7 + +static const asn1Object_t basicResponseObjects[] = { + { 0, "BasicOCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "tbsResponseData", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ + { 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE | + ASN1_DEF }, /* 2 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 2, "responderIdContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */ + { 3, "responderIdByName", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ + { 2, "responderIdContext", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 7 */ + { 3, "responderIdByKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 8 */ + { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ + { 2, "producedAt", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */ + { 2, "responses", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ + { 2, "responseExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 12 */ + { 3, "responseExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 13 */ + { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 14 */ + { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 15 */ + { 5, "critical", ASN1_BOOLEAN, ASN1_BODY | + ASN1_DEF }, /* 16 */ + { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 17 */ + { 4, "end loop", ASN1_EOC, ASN1_END }, /* 18 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ + { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 20 */ + { 1, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 21 */ + { 1, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 22 */ + { 2, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 23 */ + { 3, "certificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 24 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 26 */ +}; + +#define BASIC_RESPONSE_TBS_DATA 1 +#define BASIC_RESPONSE_VERSION 3 +#define BASIC_RESPONSE_ID_BY_NAME 5 +#define BASIC_RESPONSE_ID_BY_KEY 8 +#define BASIC_RESPONSE_PRODUCED_AT 10 +#define BASIC_RESPONSE_RESPONSES 11 +#define BASIC_RESPONSE_EXT_ID 15 +#define BASIC_RESPONSE_CRITICAL 16 +#define BASIC_RESPONSE_EXT_VALUE 17 +#define BASIC_RESPONSE_ALGORITHM 20 +#define BASIC_RESPONSE_SIGNATURE 21 +#define BASIC_RESPONSE_CERTIFICATE 24 +#define BASIC_RESPONSE_ROOF 27 + +static const asn1Object_t responsesObjects[] = { + { 0, "responses", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 1, "singleResponse", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 0, "end loop", ASN1_EOC, ASN1_END } /* 2 */ +}; + +#define RESPONSES_SINGLE_RESPONSE 1 +#define RESPONSES_ROOF 3 + +static const asn1Object_t singleResponseObjects[] = { + { 0, "singleResponse", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ + { 1, "certID", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ + { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 2, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ + { 2, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */ + { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "certStatusGood", ASN1_CONTEXT_S_0, ASN1_OPT }, /* 6 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 1, "certStatusRevoked", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 8 */ + { 2, "revocationTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 9 */ + { 2, "revocationReason", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 10 */ + { 3, "crlReason", ASN1_ENUMERATED, ASN1_BODY }, /* 11 */ + { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 13 */ + { 1, "certStatusUnknown", ASN1_CONTEXT_S_2, ASN1_OPT }, /* 14 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 15 */ + { 1, "thisUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */ + { 1, "nextUpdateContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 17 */ + { 2, "nextUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ + { 1, "singleExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 20 */ + { 2, "singleExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 21 */ + { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ + { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 23 */ + { 4, "critical", ASN1_BOOLEAN, ASN1_BODY | + ASN1_DEF }, /* 24 */ + { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 25 */ + { 2, "end loop", ASN1_EOC, ASN1_END }, /* 26 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 27 */ +}; + +#define SINGLE_RESPONSE_ALGORITHM 2 +#define SINGLE_RESPONSE_ISSUER_NAME_HASH 3 +#define SINGLE_RESPONSE_ISSUER_KEY_HASH 4 +#define SINGLE_RESPONSE_SERIAL_NUMBER 5 +#define SINGLE_RESPONSE_CERT_STATUS_GOOD 6 +#define SINGLE_RESPONSE_CERT_STATUS_REVOKED 8 +#define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME 9 +#define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON 11 +#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN 14 +#define SINGLE_RESPONSE_THIS_UPDATE 16 +#define SINGLE_RESPONSE_NEXT_UPDATE 18 +#define SINGLE_RESPONSE_EXT_ID 23 +#define SINGLE_RESPONSE_CRITICAL 24 +#define SINGLE_RESPONSE_EXT_VALUE 25 +#define SINGLE_RESPONSE_ROOF 28 + +/* build an ocsp location from certificate information + * without unsharing its contents + */ +static bool +build_ocsp_location(const x509cert_t *cert, ocsp_location_t *location) +{ + static u_char digest[SHA1_DIGEST_SIZE]; /* temporary storage */ + + location->uri = cert->accessLocation; + + if (location->uri.ptr == NULL) + { + ca_info_t *ca = get_ca_info(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID); + if (ca != NULL && ca->ocspuri != NULL) + setchunk(location->uri, ca->ocspuri, strlen(ca->ocspuri)) + else + /* abort if no ocsp location uri is defined */ + return FALSE; + } + + setchunk(location->authNameID, digest, SHA1_DIGEST_SIZE); + compute_digest(cert->issuer, OID_SHA1, &location->authNameID); + + location->next = NULL; + location->issuer = cert->issuer; + location->authKeyID = cert->authKeyID; + location->authKeySerialNumber = cert->authKeySerialNumber; + + if (cert->authKeyID.ptr == NULL) + { + x509cert_t *authcert = get_authcert(cert->issuer + , cert->authKeySerialNumber, cert->authKeyID, AUTH_CA); + + if (authcert != NULL) + { + location->authKeyID = authcert->subjectKeyID; + location->authKeySerialNumber = authcert->serialNumber; + } + } + + location->nonce = empty_chunk; + location->certinfo = NULL; + + return TRUE; +} + +/* + * compare two ocsp locations for equality + */ +static bool +same_ocsp_location(const ocsp_location_t *a, const ocsp_location_t *b) +{ + return ((a->authKeyID.ptr != NULL) + ? same_keyid(a->authKeyID, b->authKeyID) + : (same_dn(a->issuer, b->issuer) + && same_serial(a->authKeySerialNumber, b->authKeySerialNumber))) + && same_chunk(a->uri, b->uri); +} + +/* + * find an existing ocsp location in a chained list + */ +ocsp_location_t* +get_ocsp_location(const ocsp_location_t * loc, ocsp_location_t *chain) +{ + + while (chain != NULL) + { + if (same_ocsp_location(loc, chain)) + return chain; + chain = chain->next; + } + return NULL; +} + +/* retrieves the status of a cert from the ocsp cache + * returns CERT_UNDEFINED if no status is found + */ +static cert_status_t +get_ocsp_status(const ocsp_location_t *loc, chunk_t serialNumber + ,time_t *nextUpdate, time_t *revocationTime, crl_reason_t *revocationReason) +{ + ocsp_certinfo_t *certinfo, **certinfop; + int cmp = -1; + + /* find location */ + ocsp_location_t *location = get_ocsp_location(loc, ocsp_cache); + + if (location == NULL) + return CERT_UNDEFINED; + + /* traverse list of certinfos in increasing order */ + certinfop = &location->certinfo; + certinfo = *certinfop; + + while (certinfo != NULL) + { + cmp = cmp_chunk(serialNumber, certinfo->serialNumber); + if (cmp <= 0) + break; + certinfop = &certinfo->next; + certinfo = *certinfop; + } + + if (cmp == 0) + { + *nextUpdate = certinfo->nextUpdate; + *revocationTime = certinfo->revocationTime; + *revocationReason = certinfo->revocationReason; + return certinfo->status; + } + + return CERT_UNDEFINED; +} + +/* + * verify the ocsp status of a certificate + */ +cert_status_t +verify_by_ocsp(const x509cert_t *cert, time_t *until +, time_t *revocationDate, crl_reason_t *revocationReason) +{ + cert_status_t status; + ocsp_location_t location; + time_t nextUpdate = 0; + + *revocationDate = UNDEFINED_TIME; + *revocationReason = REASON_UNSPECIFIED; + + /* is an ocsp location defined? */ + if (!build_ocsp_location(cert, &location)) + return CERT_UNDEFINED; + + lock_ocsp_cache("verify_by_ocsp"); + status = get_ocsp_status(&location, cert->serialNumber, &nextUpdate + , revocationDate, revocationReason); + unlock_ocsp_cache("verify_by_ocsp"); + + if (status == CERT_UNDEFINED || nextUpdate < time(NULL)) + { + plog("ocsp status is stale or not in cache"); + add_ocsp_fetch_request(&location, cert->serialNumber); + + /* inititate fetching of ocsp status */ + wake_fetch_thread("verify_by_ocsp"); + } + *until = nextUpdate; + return status; +} + +/* + * check if an ocsp status is about to expire + */ +void +check_ocsp(void) +{ + ocsp_location_t *location; + + lock_ocsp_cache("check_ocsp"); + location = ocsp_cache; + + while (location != NULL) + { + char buf[BUF_LEN]; + bool first = TRUE; + ocsp_certinfo_t *certinfo = location->certinfo; + + while (certinfo != NULL) + { + if (!certinfo->once) + { + time_t time_left = certinfo->nextUpdate - time(NULL); + + DBG(DBG_CONTROL, + if (first) + { + dntoa(buf, BUF_LEN, location->issuer); + DBG_log("issuer: '%s'", buf); + if (location->authKeyID.ptr != NULL) + { + datatot(location->authKeyID.ptr, location->authKeyID.len + , ':', buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + first = FALSE; + } + datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len + , ':', buf, BUF_LEN); + DBG_log("serial: %s, %ld seconds left", buf, time_left) + ) + + if (time_left < 2*crl_check_interval) + add_ocsp_fetch_request(location, certinfo->serialNumber); + } + certinfo = certinfo->next; + } + location = location->next; + } + unlock_ocsp_cache("check_ocsp"); +} + +/* + * frees the allocated memory of a certinfo struct + */ +static void +free_certinfo(ocsp_certinfo_t *certinfo) +{ + freeanychunk(certinfo->serialNumber); + pfree(certinfo); +} + +/* + * frees all certinfos in a chained list + */ +static void +free_certinfos(ocsp_certinfo_t *chain) +{ + ocsp_certinfo_t *certinfo; + + while (chain != NULL) + { + certinfo = chain; + chain = chain->next; + free_certinfo(certinfo); + } +} + +/* + * frees the memory allocated to an ocsp location including all certinfos + */ +static void +free_ocsp_location(ocsp_location_t* location) +{ + freeanychunk(location->issuer); + freeanychunk(location->authNameID); + freeanychunk(location->authKeyID); + freeanychunk(location->authKeySerialNumber); + freeanychunk(location->uri); + free_certinfos(location->certinfo); + pfree(location); +} + +/* + * free a chained list of ocsp locations + */ +void +free_ocsp_locations(ocsp_location_t **chain) +{ + while (*chain != NULL) + { + ocsp_location_t *location = *chain; + *chain = location->next; + free_ocsp_location(location); + } +} + +/* + * free the ocsp cache + */ +void +free_ocsp_cache(void) +{ + lock_ocsp_cache("free_ocsp_cache"); + free_ocsp_locations(&ocsp_cache); + unlock_ocsp_cache("free_ocsp_cache"); +} + +/* + * frees the ocsp cache and global variables + */ +void +free_ocsp(void) +{ + pfreeany(ocsp_default_uri.ptr); + free_ocsp_cache(); +} + +/* + * list a chained list of ocsp_locations + */ +void +list_ocsp_locations(ocsp_location_t *location, bool requests, bool utc +, bool strict) +{ + bool first = TRUE; + + while (location != NULL) + { + ocsp_certinfo_t *certinfo = location->certinfo; + + if (certinfo != NULL) + { + u_char buf[BUF_LEN]; + + if (first) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of OCSP %s:", requests? + "fetch requests":"responses"); + first = FALSE; + } + whack_log(RC_COMMENT, " "); + if (location->issuer.ptr != NULL) + { + dntoa(buf, BUF_LEN, location->issuer); + whack_log(RC_COMMENT, " issuer: '%s'", buf); + } + whack_log(RC_COMMENT, " uri: '%.*s'", (int)location->uri.len + , location->uri.ptr); + if (location->authNameID.ptr != NULL) + { + datatot(location->authNameID.ptr, location->authNameID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authname: %s", buf); + } + if (location->authKeyID.ptr != NULL) + { + datatot(location->authKeyID.ptr, location->authKeyID.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " authkey: %s", buf); + } + if (location->authKeySerialNumber.ptr != NULL) + { + datatot(location->authKeySerialNumber.ptr + , location->authKeySerialNumber.len, ':', buf, BUF_LEN); + whack_log(RC_COMMENT, " aserial: %s", buf); + } + while (certinfo != NULL) + { + char thisUpdate[TIMETOA_BUF]; + + strcpy(thisUpdate, timetoa(&certinfo->thisUpdate, utc)); + + if (requests) + { + whack_log(RC_COMMENT, "%s, trials: %d", thisUpdate + , certinfo->trials); + } + else if (certinfo->once) + { + whack_log(RC_COMMENT, "%s, onetime use%s", thisUpdate + , (certinfo->nextUpdate < time(NULL))? " (expired)": ""); + } + else + { + whack_log(RC_COMMENT, "%s, until %s %s", thisUpdate + , timetoa(&certinfo->nextUpdate, utc) + , check_expiry(certinfo->nextUpdate, OCSP_WARNING_INTERVAL, strict)); + } + datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len, ':' + , buf, BUF_LEN); + whack_log(RC_COMMENT, " serial: %s, %s", buf + , cert_status_names[certinfo->status]); + certinfo = certinfo->next; + } + } + location = location->next; + } +} + +/* + * list the ocsp cache + */ +void +list_ocsp_cache(bool utc, bool strict) +{ + lock_ocsp_cache("list_ocsp_cache"); + list_ocsp_locations(ocsp_cache, FALSE, utc, strict); + unlock_ocsp_cache("list_ocsp_cache"); +} + +static bool +get_ocsp_requestor_cert(ocsp_location_t *location) +{ + x509cert_t *cert = NULL; + + /* initialize temporary static storage */ + ocsp_requestor_cert = NULL; + ocsp_requestor_sc = NULL; + ocsp_requestor_pri = NULL; + + for (;;) + { + char buf[BUF_LEN]; + + /* looking for a certificate from the same issuer */ + cert = get_x509cert(location->issuer, location->authKeySerialNumber + ,location->authKeyID, cert); + if (cert == NULL) + break; + + DBG(DBG_CONTROL, + dntoa(buf, BUF_LEN, cert->subject); + DBG_log("candidate: '%s'", buf); + ) + + if (cert->smartcard) + { + /* look for a matching private key on a smartcard */ + smartcard_t *sc = scx_get(cert); + + if (sc != NULL) + { + DBG(DBG_CONTROL, + DBG_log("matching smartcard found") + ) + if (sc->valid) + { + ocsp_requestor_cert = cert; + ocsp_requestor_sc = sc; + return TRUE; + } + plog("unable to sign ocsp request without PIN"); + } + } + else + { + /* look for a matching private key in the chained list */ + const struct RSA_private_key *pri = get_x509_private_key(cert); + + if (pri != NULL) + { + DBG(DBG_CONTROL, + DBG_log("matching private key found") + ) + ocsp_requestor_cert = cert; + ocsp_requestor_pri = pri; + return TRUE; + } + } + } + return FALSE; +} + +static chunk_t +generate_signature(chunk_t digest, smartcard_t *sc + , const RSA_private_key_t *pri) +{ + chunk_t sigdata; + u_char *pos; + size_t siglen = 0; + + if (sc != NULL) + { + /* RSA signature is done on smartcard */ + + if (!scx_establish_context(sc) || !scx_login(sc)) + { + scx_release_context(sc); + return empty_chunk; + } + + siglen = scx_get_keylength(sc); + + if (siglen == 0) + { + plog("failed to get keylength from smartcard"); + scx_release_context(sc); + return empty_chunk; + } + + DBG(DBG_CONTROL | DBG_CRYPT, + DBG_log("signing hash with RSA key from smartcard (slot: %d, id: %s)" + , (int)sc->slot, sc->id) + ) + + pos = build_asn1_object(&sigdata, ASN1_BIT_STRING, 1 + siglen); + *pos++ = 0x00; + scx_sign_hash(sc, digest.ptr, digest.len, pos, siglen); + if (!pkcs11_keep_state) + scx_release_context(sc); + } + else + { + /* RSA signature is done in software */ + siglen = pri->pub.k; + pos = build_asn1_object(&sigdata, ASN1_BIT_STRING, 1 + siglen); + *pos++ = 0x00; + sign_hash(pri, digest.ptr, digest.len, pos, siglen); + } + return sigdata; +} + +/* + * build signature into ocsp request + * gets built only if a request cert with + * a corresponding private key is found + */ +static chunk_t +build_signature(chunk_t tbsRequest) +{ + chunk_t sigdata, certs; + chunk_t digest_info; + + u_char digest_buf[MAX_DIGEST_LEN]; + chunk_t digest_raw = { digest_buf, MAX_DIGEST_LEN }; + + if (!compute_digest(tbsRequest, OID_SHA1, &digest_raw)) + return empty_chunk; + + /* according to PKCS#1 v2.1 digest must be packaged into + * an ASN.1 structure for encryption + */ + digest_info = asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_sha1_id + , asn1_simple_object(ASN1_OCTET_STRING, digest_raw)); + + /* generate the RSA signature */ + sigdata = generate_signature(digest_info + , ocsp_requestor_sc + , ocsp_requestor_pri); + freeanychunk(digest_info); + + /* has the RSA signature generation been successful? */ + if (sigdata.ptr == NULL) + return empty_chunk; + + /* include our certificate */ + certs = asn1_wrap(ASN1_CONTEXT_C_0, "m" + , asn1_simple_object(ASN1_SEQUENCE + , ocsp_requestor_cert->certificate + ) + ); + + /* build signature comprising algorithm, signature and cert */ + return asn1_wrap(ASN1_CONTEXT_C_0, "m" + , asn1_wrap(ASN1_SEQUENCE, "cmm" + , ASN1_sha1WithRSA_id + , sigdata + , certs + ) + ); +} + +/* build request (into requestList) + * no singleRequestExtensions used + */ +static chunk_t +build_request(ocsp_location_t *location, ocsp_certinfo_t *certinfo) +{ + chunk_t reqCert = asn1_wrap(ASN1_SEQUENCE, "cmmm" + , ASN1_sha1_id + , asn1_simple_object(ASN1_OCTET_STRING, location->authNameID) + , asn1_simple_object(ASN1_OCTET_STRING, location->authKeyID) + , asn1_simple_object(ASN1_INTEGER, certinfo->serialNumber)); + + return asn1_wrap(ASN1_SEQUENCE, "m", reqCert); +} + +/* + * build requestList (into TBSRequest) + */ +static chunk_t +build_request_list(ocsp_location_t *location) +{ + chunk_t requestList; + request_list_t *reqs = NULL; + ocsp_certinfo_t *certinfo = location->certinfo; + u_char *pos; + + size_t datalen = 0; + + /* build content */ + while (certinfo != NULL) + { + /* build request for every certificate in list + * and store them in a chained list + */ + request_list_t *req = alloc_thing(request_list_t, "ocsp request"); + + req->request = build_request(location, certinfo); + req->next = reqs; + reqs = req; + + datalen += req->request.len; + certinfo = certinfo->next; + } + + pos = build_asn1_object(&requestList, ASN1_SEQUENCE + , datalen); + + /* copy all in chained list, free list afterwards */ + while (reqs != NULL) + { + request_list_t *req = reqs; + + mv_chunk(&pos, req->request); + reqs = reqs->next; + pfree(req); + } + + return requestList; +} + +/* + * build requestorName (into TBSRequest) + */ +static chunk_t +build_requestor_name(void) +{ + return asn1_wrap(ASN1_CONTEXT_C_1, "m" + , asn1_simple_object(ASN1_CONTEXT_C_4 + , ocsp_requestor_cert->subject)); +} + +/* + * build nonce extension (into requestExtensions) + */ +static chunk_t +build_nonce_extension(ocsp_location_t *location) +{ + /* generate a random nonce */ + location->nonce.ptr = alloc_bytes(NONCE_LENGTH, "ocsp nonce"), + location->nonce.len = NONCE_LENGTH; + get_rnd_bytes(location->nonce.ptr, NONCE_LENGTH); + + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_nonce_oid + , asn1_simple_object(ASN1_OCTET_STRING, location->nonce)); +} + +/* + * build requestExtensions (into TBSRequest) + */ +static chunk_t +build_request_ext(ocsp_location_t *location) +{ + return asn1_wrap(ASN1_CONTEXT_C_2, "m" + , asn1_wrap(ASN1_SEQUENCE, "mm" + , build_nonce_extension(location) + , asn1_wrap(ASN1_SEQUENCE, "cc" + , ASN1_response_oid + , ASN1_response_content + ) + ) + ); +} + +/* + * build TBSRequest (into OCSPRequest) + */ +static chunk_t +build_tbs_request(ocsp_location_t *location, bool has_requestor_cert) +{ + /* version is skipped since the default is ok */ + return asn1_wrap(ASN1_SEQUENCE, "mmm" + , (has_requestor_cert) + ? build_requestor_name() + : empty_chunk + , build_request_list(location) + , build_request_ext(location)); +} + +/* assembles an ocsp request to given location + * and sets nonce field in location to the sent nonce + */ +chunk_t +build_ocsp_request(ocsp_location_t *location) +{ + bool has_requestor_cert; + chunk_t tbsRequest, signature; + char buf[BUF_LEN]; + + DBG(DBG_CONTROL, + DBG_log("assembling ocsp request"); + dntoa(buf, BUF_LEN, location->issuer); + DBG_log("issuer: '%s'", buf); + if (location->authKeyID.ptr != NULL) + { + datatot(location->authKeyID.ptr, location->authKeyID.len, ':' + , buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + ) + lock_certs_and_keys("build_ocsp_request"); + + /* looks for requestor cert and matching private key */ + has_requestor_cert = get_ocsp_requestor_cert(location); + + /* build content */ + tbsRequest = build_tbs_request(location, has_requestor_cert); + + /* sign tbsReuqest */ + signature = (has_requestor_cert)? build_signature(tbsRequest) + : empty_chunk; + + unlock_certs_and_keys("build_ocsp_request"); + + return asn1_wrap(ASN1_SEQUENCE, "mm" + , tbsRequest + , signature); +} + +/* + * check if the OCSP response has a valid signature + */ +static bool +valid_ocsp_response(response_t *res) +{ + int pathlen; + x509cert_t *authcert; + + lock_authcert_list("valid_ocsp_response"); + + authcert = get_authcert(res->responder_id_name, empty_chunk + , res->responder_id_key, AUTH_OCSP | AUTH_CA); + + if (authcert == NULL) + { + plog("no matching ocsp signer cert found"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("ocsp signer cert found") + ) + + if (!check_signature(res->tbs, res->signature, res->algorithm + , res->algorithm, authcert)) + { + plog("signature of ocsp response is invalid"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("signature of ocsp response is valid") + ) + + + for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) + { + u_char buf[BUF_LEN]; + err_t ugh = NULL; + time_t until; + + x509cert_t *cert = authcert; + + DBG(DBG_CONTROL, + dntoa(buf, BUF_LEN, cert->subject); + DBG_log("subject: '%s'",buf); + dntoa(buf, BUF_LEN, cert->issuer); + DBG_log("issuer: '%s'",buf); + if (cert->authKeyID.ptr != NULL) + { + datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':' + , buf, BUF_LEN); + DBG_log("authkey: %s", buf); + } + ) + + ugh = check_validity(authcert, &until); + + if (ugh != NULL) + { + plog("%s", ugh); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + + DBG(DBG_CONTROL, + DBG_log("certificate is valid") + ) + + authcert = get_authcert(cert->issuer, cert->authKeySerialNumber + , cert->authKeyID, AUTH_CA); + + if (authcert == NULL) + { + plog("issuer cacert not found"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("issuer cacert found") + ) + + if (!check_signature(cert->tbsCertificate, cert->signature + , cert->algorithm, cert->algorithm, authcert)) + { + plog("certificate signature is invalid"); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; + } + DBG(DBG_CONTROL, + DBG_log("certificate signature is valid") + ) + + /* check if cert is self-signed */ + if (same_dn(cert->issuer, cert->subject)) + { + DBG(DBG_CONTROL, + DBG_log("reached self-signed root ca") + ) + unlock_authcert_list("valid_ocsp_response"); + return TRUE; + } + } + plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN); + unlock_authcert_list("valid_ocsp_response"); + return FALSE; +} + +/* + * parse a basic OCSP response + */ +static bool +parse_basic_ocsp_response(chunk_t blob, int level0, response_t *res) +{ + u_int level, version; + u_int extn_oid = OID_UNKNOWN; + u_char buf[BUF_LEN]; + asn1_ctx_t ctx; + bool critical; + chunk_t object; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < BASIC_RESPONSE_ROOF) + { + if (!extract_object(basicResponseObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + switch (objectID) + { + case BASIC_RESPONSE_TBS_DATA: + res->tbs = object; + break; + case BASIC_RESPONSE_VERSION: + version = (object.len)? (1 + (u_int)*object.ptr) : 1; + if (version != OCSP_BASIC_RESPONSE_VERSION) + { + plog("wrong ocsp basic response version (version= %i)", version); + return FALSE; + } + break; + case BASIC_RESPONSE_ID_BY_NAME: + res->responder_id_name = object; + DBG(DBG_PARSING, + dntoa(buf, BUF_LEN, object); + DBG_log(" '%s'",buf) + ) + break; + case BASIC_RESPONSE_ID_BY_KEY: + res->responder_id_key = object; + break; + case BASIC_RESPONSE_PRODUCED_AT: + res->produced_at = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case BASIC_RESPONSE_RESPONSES: + res->responses = object; + break; + case BASIC_RESPONSE_EXT_ID: + extn_oid = known_oid(object); + break; + case BASIC_RESPONSE_CRITICAL: + critical = object.len && *object.ptr; + DBG(DBG_PARSING, + DBG_log(" %s",(critical)?"TRUE":"FALSE"); + ) + break; + case BASIC_RESPONSE_EXT_VALUE: + if (extn_oid == OID_NONCE) + res->nonce = object; + break; + case BASIC_RESPONSE_ALGORITHM: + res->algorithm = parse_algorithmIdentifier(object, level+1, NULL); + break; + case BASIC_RESPONSE_SIGNATURE: + res->signature = object; + break; + case BASIC_RESPONSE_CERTIFICATE: + { + chunk_t blob; + x509cert_t *cert = alloc_thing(x509cert_t, "ocspcert"); + + clonetochunk(blob, object.ptr, object.len, "ocspcert blob"); + *cert = empty_x509cert; + + if (parse_x509cert(blob, level+1, cert) + && cert->isOcspSigner + && trust_authcert_candidate(cert, NULL)) + { + add_authcert(cert, AUTH_OCSP); + } + else + { + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log("embedded ocsp certificate rejected") + ) + free_x509cert(cert); + } + } + break; + } + objectID++; + } + return TRUE; +} + + +/* + * parse an ocsp response and return the result as a response_t struct + */ +static response_status +parse_ocsp_response(chunk_t blob, response_t * res) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + response_status rStatus = STATUS_INTERNALERROR; + u_int ocspResponseType = OID_UNKNOWN; + + asn1_init(&ctx, blob, 0, FALSE, DBG_RAW); + + while (objectID < OCSP_RESPONSE_ROOF) + { + if (!extract_object(ocspResponseObjects, &objectID, &object, &level, &ctx)) + return STATUS_INTERNALERROR; + + switch (objectID) { + case OCSP_RESPONSE_STATUS: + rStatus = (response_status) *object.ptr; + + switch (rStatus) + { + case STATUS_SUCCESSFUL: + break; + case STATUS_MALFORMEDREQUEST: + case STATUS_INTERNALERROR: + case STATUS_TRYLATER: + case STATUS_SIGREQUIRED: + case STATUS_UNAUTHORIZED: + plog("ocsp response: server said '%s'" + , response_status_names[rStatus]); + return rStatus; + default: + return STATUS_INTERNALERROR; + } + break; + case OCSP_RESPONSE_TYPE: + ocspResponseType = known_oid(object); + break; + case OCSP_RESPONSE: + { + switch (ocspResponseType) { + case OID_BASIC: + if (!parse_basic_ocsp_response(object, level+1, res)) + return STATUS_INTERNALERROR; + break; + default: + DBG(DBG_CONTROL, + DBG_log("ocsp response is not of type BASIC"); + DBG_dump_chunk("ocsp response OID: ", object); + ) + return STATUS_INTERNALERROR; + } + } + break; + } + objectID++; + } + return rStatus; +} + +/* + * parse a basic OCSP response + */ +static bool +parse_ocsp_single_response(chunk_t blob, int level0, single_response_t *sres) +{ + u_int level, extn_oid; + asn1_ctx_t ctx; + bool critical; + chunk_t object; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < SINGLE_RESPONSE_ROOF) + { + if (!extract_object(singleResponseObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + switch (objectID) + { + case SINGLE_RESPONSE_ALGORITHM: + sres->hash_algorithm = parse_algorithmIdentifier(object, level+1, NULL); + break; + case SINGLE_RESPONSE_ISSUER_NAME_HASH: + sres->issuer_name_hash = object; + break; + case SINGLE_RESPONSE_ISSUER_KEY_HASH: + sres->issuer_key_hash = object; + break; + case SINGLE_RESPONSE_SERIAL_NUMBER: + sres->serialNumber = object; + break; + case SINGLE_RESPONSE_CERT_STATUS_GOOD: + sres->status = CERT_GOOD; + break; + case SINGLE_RESPONSE_CERT_STATUS_REVOKED: + sres->status = CERT_REVOKED; + break; + case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME: + sres->revocationTime = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON: + sres->revocationReason = (object.len == 1) + ? *object.ptr : REASON_UNSPECIFIED; + break; + case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN: + sres->status = CERT_UNKNOWN; + break; + case SINGLE_RESPONSE_THIS_UPDATE: + sres->thisUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case SINGLE_RESPONSE_NEXT_UPDATE: + sres->nextUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME); + break; + case SINGLE_RESPONSE_EXT_ID: + extn_oid = known_oid(object); + break; + case SINGLE_RESPONSE_CRITICAL: + critical = object.len && *object.ptr; + DBG(DBG_PARSING, + DBG_log(" %s",(critical)?"TRUE":"FALSE"); + ) + case SINGLE_RESPONSE_EXT_VALUE: + break; + } + objectID++; + } + return TRUE; +} + +/* + * add an ocsp location to a chained list + */ +ocsp_location_t* +add_ocsp_location(const ocsp_location_t *loc, ocsp_location_t **chain) +{ + ocsp_location_t *location = alloc_thing(ocsp_location_t, "ocsp location"); + + /* unshare location fields */ + clonetochunk(location->issuer + , loc->issuer.ptr, loc->issuer.len + , "ocsp issuer"); + + clonetochunk(location->authNameID + , loc->authNameID.ptr, loc->authNameID.len + , "ocsp authNameID"); + + if (loc->authKeyID.ptr == NULL) + location->authKeyID = empty_chunk; + else + clonetochunk(location->authKeyID + , loc->authKeyID.ptr, loc->authKeyID.len + , "ocsp authKeyID"); + + if (loc->authKeySerialNumber.ptr == NULL) + location->authKeySerialNumber = empty_chunk; + else + clonetochunk(location->authKeySerialNumber + , loc->authKeySerialNumber.ptr, loc->authKeySerialNumber.len + , "ocsp authKeySerialNumber"); + + clonetochunk(location->uri + , loc->uri.ptr, loc->uri.len + , "ocsp uri"); + + location->certinfo = NULL; + + /* insert new ocsp location in front of chain */ + location->next = *chain; + *chain = location; + + DBG(DBG_CONTROL, + DBG_log("new ocsp location added") + ) + + return location; +} + +/* + * add a certinfo struct to a chained list + */ +void +add_certinfo(ocsp_location_t *loc, ocsp_certinfo_t *info, ocsp_location_t **chain + , bool request) +{ + ocsp_location_t *location; + ocsp_certinfo_t *certinfo, **certinfop; + char buf[BUF_LEN]; + time_t now; + int cmp = -1; + + location = get_ocsp_location(loc, *chain); + if (location == NULL) + location = add_ocsp_location(loc, chain); + + /* traverse list of certinfos in increasing order */ + certinfop = &location->certinfo; + certinfo = *certinfop; + + while (certinfo != NULL) + { + cmp = cmp_chunk(info->serialNumber, certinfo->serialNumber); + if (cmp <= 0) + break; + certinfop = &certinfo->next; + certinfo = *certinfop; + } + + if (cmp != 0) + { + /* add a new certinfo entry */ + ocsp_certinfo_t *cnew = alloc_thing(ocsp_certinfo_t, "ocsp certinfo"); + clonetochunk(cnew->serialNumber, info->serialNumber.ptr + , info->serialNumber.len, "serialNumber"); + cnew->next = certinfo; + *certinfop = cnew; + certinfo = cnew; + } + + DBG(DBG_CONTROL, + datatot(info->serialNumber.ptr, info->serialNumber.len, ':' + , buf, BUF_LEN); + DBG_log("ocsp %s for serial %s %s" + , request?"fetch request":"certinfo" + , buf + , (cmp == 0)? (request?"already exists":"updated"):"added") + ) + + time(&now); + + if (request) + { + certinfo->status = CERT_UNDEFINED; + + if (cmp != 0) + certinfo->thisUpdate = now; + + certinfo->nextUpdate = UNDEFINED_TIME; + } + else + { + certinfo->status = info->status; + certinfo->revocationTime = info->revocationTime; + certinfo->revocationReason = info->revocationReason; + + certinfo->thisUpdate = (info->thisUpdate != UNDEFINED_TIME)? + info->thisUpdate : now; + + certinfo->once = (info->nextUpdate == UNDEFINED_TIME); + + certinfo->nextUpdate = (certinfo->once)? + (now + OCSP_DEFAULT_VALID_TIME) : info->nextUpdate; + } +} + +/* + * process received ocsp single response and add it to ocsp cache + */ +static void +process_single_response(ocsp_location_t *location, single_response_t *sres) +{ + ocsp_certinfo_t *certinfo, **certinfop; + int cmp = -1; + + if (sres->hash_algorithm != OID_SHA1) + { + plog("only SHA-1 hash supported in OCSP single response"); + return; + } + if (!(same_chunk(sres->issuer_name_hash, location->authNameID) + && same_chunk(sres->issuer_key_hash, location->authKeyID))) + { + plog("ocsp single response has wrong issuer"); + return; + } + + /* traverse list of certinfos in increasing order */ + certinfop = &location->certinfo; + certinfo = *certinfop; + + while (certinfo != NULL) + { + cmp = cmp_chunk(sres->serialNumber, certinfo->serialNumber); + if (cmp <= 0) + break; + certinfop = &certinfo->next; + certinfo = *certinfop; + } + + if (cmp != 0) + { + plog("received unrequested cert status from ocsp server"); + return; + } + + /* unlink cert from ocsp fetch request list */ + *certinfop = certinfo->next; + + /* update certinfo using the single response information */ + certinfo->thisUpdate = sres->thisUpdate; + certinfo->nextUpdate = sres->nextUpdate; + certinfo->status = sres->status; + certinfo->revocationTime = sres->revocationTime; + certinfo->revocationReason = sres->revocationReason; + + /* add or update certinfo in ocsp cache */ + lock_ocsp_cache("process_single_response"); + add_certinfo(location, certinfo, &ocsp_cache, FALSE); + unlock_ocsp_cache("process_single_response"); + + /* free certinfo unlinked from ocsp fetch request list */ + free_certinfo(certinfo); + +} + +/* + * parse and verify ocsp response and update the ocsp cache + */ +void +parse_ocsp(ocsp_location_t *location, chunk_t blob) +{ + response_t res = empty_response; + + /* parse the ocsp response without looking at the single responses yet */ + response_status status = parse_ocsp_response(blob, &res); + + if (status != STATUS_SUCCESSFUL) + { + plog("error in ocsp response"); + return; + } + /* check if there was a nonce in the request */ + if (location->nonce.ptr != NULL && res.nonce.ptr == NULL) + { + plog("ocsp response contains no nonce, replay attack possible"); + } + /* check if the nonce is identical */ + if (res.nonce.ptr != NULL && !same_chunk(res.nonce, location->nonce)) + { + plog("invalid nonce in ocsp response"); + return; + } + /* check if the response is signed by a trusted key */ + if (!valid_ocsp_response(&res)) + { + plog("invalid ocsp response"); + return; + } + DBG(DBG_CONTROL, + DBG_log("valid ocsp response") + ) + + /* now parse the single responses one at a time */ + { + u_int level; + asn1_ctx_t ctx; + chunk_t object; + int objectID = 0; + + asn1_init(&ctx, res.responses, 0, FALSE, DBG_RAW); + + while (objectID < RESPONSES_ROOF) + { + if (!extract_object(responsesObjects, &objectID, &object, &level, &ctx)) + return; + + if (objectID == RESPONSES_SINGLE_RESPONSE) + { + single_response_t sres = empty_single_response; + + if (parse_ocsp_single_response(object, level+1, &sres)) + { + process_single_response(location, &sres); + } + } + objectID++; + } + } +} diff --git a/src/pluto/ocsp.h b/src/pluto/ocsp.h new file mode 100644 index 000000000..49e1026ec --- /dev/null +++ b/src/pluto/ocsp.h @@ -0,0 +1,85 @@ +/* Support of the Online Certificate Status Protocol (OCSP) Support + * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen + * Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + */ + +#include "constants.h" + +/* constants */ + +#define OCSP_BASIC_RESPONSE_VERSION 1 +#define OCSP_DEFAULT_VALID_TIME 120 /* validity of one-time response in seconds */ +#define OCSP_WARNING_INTERVAL 2 /* days */ + +/* OCSP response status */ + +typedef enum { + STATUS_SUCCESSFUL = 0, + STATUS_MALFORMEDREQUEST = 1, + STATUS_INTERNALERROR = 2, + STATUS_TRYLATER = 3, + STATUS_SIGREQUIRED = 5, + STATUS_UNAUTHORIZED= 6 +} response_status; + +/* OCSP access structures */ + +typedef struct ocsp_certinfo ocsp_certinfo_t; + +struct ocsp_certinfo { + ocsp_certinfo_t *next; + int trials; + chunk_t serialNumber; + cert_status_t status; + bool once; + crl_reason_t revocationReason; + time_t revocationTime; + time_t thisUpdate; + time_t nextUpdate; +}; + +typedef struct ocsp_location ocsp_location_t; + +struct ocsp_location { + ocsp_location_t *next; + chunk_t issuer; + chunk_t authNameID; + chunk_t authKeyID; + chunk_t authKeySerialNumber; + chunk_t uri; + chunk_t nonce; + ocsp_certinfo_t *certinfo; +}; + +extern ocsp_location_t* get_ocsp_location(const ocsp_location_t *loc + , ocsp_location_t *chain); +extern ocsp_location_t* add_ocsp_location(const ocsp_location_t *loc + , ocsp_location_t **chain); +extern void add_certinfo(ocsp_location_t *loc, ocsp_certinfo_t *info + , ocsp_location_t **chain, bool request); +extern void check_ocsp(void); +extern cert_status_t verify_by_ocsp(const x509cert_t *cert, time_t *until + , time_t *revocationTime, crl_reason_t *revocationReason); +extern bool ocsp_set_request_cert(char* path); +extern void ocsp_set_default_uri(char* uri); +extern void ocsp_cache_add_cert(const x509cert_t* cert); +extern chunk_t build_ocsp_request(ocsp_location_t* location); +extern void parse_ocsp(ocsp_location_t* location, chunk_t blob); +extern void list_ocsp_locations(ocsp_location_t *location, bool requests + , bool utc, bool strict); +extern void list_ocsp_cache(bool utc, bool strict); +extern void free_ocsp_locations(ocsp_location_t **chain); +extern void free_ocsp_cache(void); +extern void free_ocsp(void); +extern void ocsp_purge_cache(void); diff --git a/src/pluto/oid.c b/src/pluto/oid.c new file mode 100644 index 000000000..4b0632de2 --- /dev/null +++ b/src/pluto/oid.c @@ -0,0 +1,197 @@ +/* List of some useful object identifiers (OIDs) + * Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This file has been automatically generated by the script oid.pl + * Do not edit manually! + */ + +#include + +#include "oid.h" + +const oid_t oid_names[] = { + {0x02, 7, 1, "ITU-T Administration" }, /* 0 */ + { 0x82, 0, 1, "" }, /* 1 */ + { 0x06, 0, 1, "Germany ITU-T member" }, /* 2 */ + { 0x01, 0, 1, "Deutsche Telekom AG" }, /* 3 */ + { 0x0A, 0, 1, "" }, /* 4 */ + { 0x07, 0, 1, "" }, /* 5 */ + { 0x14, 0, 0, "ND" }, /* 6 */ + {0x09, 18, 1, "data" }, /* 7 */ + { 0x92, 0, 1, "" }, /* 8 */ + { 0x26, 0, 1, "" }, /* 9 */ + { 0x89, 0, 1, "" }, /* 10 */ + { 0x93, 0, 1, "" }, /* 11 */ + { 0xF2, 0, 1, "" }, /* 12 */ + { 0x2C, 0, 1, "" }, /* 13 */ + { 0x64, 0, 1, "pilot" }, /* 14 */ + { 0x01, 0, 1, "pilotAttributeType" }, /* 15 */ + { 0x01, 17, 0, "UID" }, /* 16 */ + { 0x19, 0, 0, "DC" }, /* 17 */ + {0x55, 51, 1, "X.500" }, /* 18 */ + { 0x04, 36, 1, "X.509" }, /* 19 */ + { 0x03, 21, 0, "CN" }, /* 20 */ + { 0x04, 22, 0, "S" }, /* 21 */ + { 0x05, 23, 0, "SN" }, /* 22 */ + { 0x06, 24, 0, "C" }, /* 23 */ + { 0x07, 25, 0, "L" }, /* 24 */ + { 0x08, 26, 0, "ST" }, /* 25 */ + { 0x0A, 27, 0, "O" }, /* 26 */ + { 0x0B, 28, 0, "OU" }, /* 27 */ + { 0x0C, 29, 0, "T" }, /* 28 */ + { 0x0D, 30, 0, "D" }, /* 29 */ + { 0x24, 31, 0, "userCertificate" }, /* 30 */ + { 0x29, 32, 0, "N" }, /* 31 */ + { 0x2A, 33, 0, "G" }, /* 32 */ + { 0x2B, 34, 0, "I" }, /* 33 */ + { 0x2D, 35, 0, "ID" }, /* 34 */ + { 0x48, 0, 0, "role" }, /* 35 */ + { 0x1D, 0, 1, "id-ce" }, /* 36 */ + { 0x09, 38, 0, "subjectDirectoryAttrs" }, /* 37 */ + { 0x0E, 39, 0, "subjectKeyIdentifier" }, /* 38 */ + { 0x0F, 40, 0, "keyUsage" }, /* 39 */ + { 0x10, 41, 0, "privateKeyUsagePeriod" }, /* 40 */ + { 0x11, 42, 0, "subjectAltName" }, /* 41 */ + { 0x12, 43, 0, "issuerAltName" }, /* 42 */ + { 0x13, 44, 0, "basicConstraints" }, /* 43 */ + { 0x15, 45, 0, "reasonCode" }, /* 44 */ + { 0x1F, 46, 0, "crlDistributionPoints" }, /* 45 */ + { 0x20, 47, 0, "certificatePolicies" }, /* 46 */ + { 0x23, 48, 0, "authorityKeyIdentifier" }, /* 47 */ + { 0x25, 49, 0, "extendedKeyUsage" }, /* 48 */ + { 0x37, 50, 0, "targetInformation" }, /* 49 */ + { 0x38, 0, 0, "noRevAvail" }, /* 50 */ + {0x2A, 88, 1, "" }, /* 51 */ + { 0x86, 0, 1, "" }, /* 52 */ + { 0x48, 0, 1, "" }, /* 53 */ + { 0x86, 0, 1, "" }, /* 54 */ + { 0xF7, 0, 1, "" }, /* 55 */ + { 0x0D, 0, 1, "RSADSI" }, /* 56 */ + { 0x01, 83, 1, "PKCS" }, /* 57 */ + { 0x01, 66, 1, "PKCS-1" }, /* 58 */ + { 0x01, 60, 0, "rsaEncryption" }, /* 59 */ + { 0x02, 61, 0, "md2WithRSAEncryption" }, /* 60 */ + { 0x04, 62, 0, "md5WithRSAEncryption" }, /* 61 */ + { 0x05, 63, 0, "sha-1WithRSAEncryption" }, /* 62 */ + { 0x0B, 64, 0, "sha256WithRSAEncryption"}, /* 63 */ + { 0x0C, 65, 0, "sha384WithRSAEncryption"}, /* 64 */ + { 0x0D, 0, 0, "sha512WithRSAEncryption"}, /* 65 */ + { 0x07, 73, 1, "PKCS-7" }, /* 66 */ + { 0x01, 68, 0, "data" }, /* 67 */ + { 0x02, 69, 0, "signedData" }, /* 68 */ + { 0x03, 70, 0, "envelopedData" }, /* 69 */ + { 0x04, 71, 0, "signedAndEnvelopedData" }, /* 70 */ + { 0x05, 72, 0, "digestedData" }, /* 71 */ + { 0x06, 0, 0, "encryptedData" }, /* 72 */ + { 0x09, 0, 1, "PKCS-9" }, /* 73 */ + { 0x01, 75, 0, "E" }, /* 74 */ + { 0x02, 76, 0, "unstructuredName" }, /* 75 */ + { 0x03, 77, 0, "contentType" }, /* 76 */ + { 0x04, 78, 0, "messageDigest" }, /* 77 */ + { 0x05, 79, 0, "signingTime" }, /* 78 */ + { 0x06, 80, 0, "counterSignature" }, /* 79 */ + { 0x07, 81, 0, "challengePassword" }, /* 80 */ + { 0x08, 82, 0, "unstructuredAddress" }, /* 81 */ + { 0x0E, 0, 0, "extensionRequest" }, /* 82 */ + { 0x02, 86, 1, "digestAlgorithm" }, /* 83 */ + { 0x02, 85, 0, "md2" }, /* 84 */ + { 0x05, 0, 0, "md5" }, /* 85 */ + { 0x03, 0, 1, "encryptionAlgorithm" }, /* 86 */ + { 0x07, 0, 0, "3des-ede-cbc" }, /* 87 */ + {0x2B, 149, 1, "" }, /* 88 */ + { 0x06, 136, 1, "dod" }, /* 89 */ + { 0x01, 0, 1, "internet" }, /* 90 */ + { 0x04, 105, 1, "private" }, /* 91 */ + { 0x01, 0, 1, "enterprise" }, /* 92 */ + { 0x82, 98, 1, "" }, /* 93 */ + { 0x37, 0, 1, "Microsoft" }, /* 94 */ + { 0x0A, 0, 1, "" }, /* 95 */ + { 0x03, 0, 1, "" }, /* 96 */ + { 0x03, 0, 0, "msSGC" }, /* 97 */ + { 0x89, 0, 1, "" }, /* 98 */ + { 0x31, 0, 1, "" }, /* 99 */ + { 0x01, 0, 1, "" }, /* 100 */ + { 0x01, 0, 1, "" }, /* 101 */ + { 0x02, 0, 1, "" }, /* 102 */ + { 0x02, 104, 0, "" }, /* 103 */ + { 0x4B, 0, 0, "TCGID" }, /* 104 */ + { 0x05, 0, 1, "security" }, /* 105 */ + { 0x05, 0, 1, "mechanisms" }, /* 106 */ + { 0x07, 0, 1, "id-pkix" }, /* 107 */ + { 0x01, 110, 1, "id-pe" }, /* 108 */ + { 0x01, 0, 0, "authorityInfoAccess" }, /* 109 */ + { 0x03, 120, 1, "id-kp" }, /* 110 */ + { 0x01, 112, 0, "serverAuth" }, /* 111 */ + { 0x02, 113, 0, "clientAuth" }, /* 112 */ + { 0x03, 114, 0, "codeSigning" }, /* 113 */ + { 0x04, 115, 0, "emailProtection" }, /* 114 */ + { 0x05, 116, 0, "ipsecEndSystem" }, /* 115 */ + { 0x06, 117, 0, "ipsecTunnel" }, /* 116 */ + { 0x07, 118, 0, "ipsecUser" }, /* 117 */ + { 0x08, 119, 0, "timeStamping" }, /* 118 */ + { 0x09, 0, 0, "ocspSigning" }, /* 119 */ + { 0x08, 122, 1, "id-otherNames" }, /* 120 */ + { 0x05, 0, 0, "xmppAddr" }, /* 121 */ + { 0x0A, 127, 1, "id-aca" }, /* 122 */ + { 0x01, 124, 0, "authenticationInfo" }, /* 123 */ + { 0x02, 125, 0, "accessIdentity" }, /* 124 */ + { 0x03, 126, 0, "chargingIdentity" }, /* 125 */ + { 0x04, 0, 0, "group" }, /* 126 */ + { 0x30, 0, 1, "id-ad" }, /* 127 */ + { 0x01, 0, 1, "ocsp" }, /* 128 */ + { 0x01, 130, 0, "basic" }, /* 129 */ + { 0x02, 131, 0, "nonce" }, /* 130 */ + { 0x03, 132, 0, "crl" }, /* 131 */ + { 0x04, 133, 0, "response" }, /* 132 */ + { 0x05, 134, 0, "noCheck" }, /* 133 */ + { 0x06, 135, 0, "archiveCutoff" }, /* 134 */ + { 0x07, 0, 0, "serviceLocator" }, /* 135 */ + { 0x0E, 142, 1, "oiw" }, /* 136 */ + { 0x03, 0, 1, "secsig" }, /* 137 */ + { 0x02, 0, 1, "algorithms" }, /* 138 */ + { 0x07, 140, 0, "des-cbc" }, /* 139 */ + { 0x1A, 141, 0, "sha-1" }, /* 140 */ + { 0x1D, 0, 0, "sha-1WithRSASignature" }, /* 141 */ + { 0x24, 0, 1, "TeleTrusT" }, /* 142 */ + { 0x03, 0, 1, "algorithm" }, /* 143 */ + { 0x03, 0, 1, "signatureAlgorithm" }, /* 144 */ + { 0x01, 0, 1, "rsaSignature" }, /* 145 */ + { 0x02, 147, 0, "rsaSigWithripemd160" }, /* 146 */ + { 0x03, 148, 0, "rsaSigWithripemd128" }, /* 147 */ + { 0x04, 0, 0, "rsaSigWithripemd256" }, /* 148 */ + {0x60, 0, 1, "" }, /* 149 */ + { 0x86, 0, 1, "" }, /* 150 */ + { 0x48, 0, 1, "" }, /* 151 */ + { 0x01, 0, 1, "organization" }, /* 152 */ + { 0x65, 160, 1, "gov" }, /* 153 */ + { 0x03, 0, 1, "csor" }, /* 154 */ + { 0x04, 0, 1, "nistalgorithm" }, /* 155 */ + { 0x02, 0, 1, "hashalgs" }, /* 156 */ + { 0x01, 158, 0, "id-SHA-256" }, /* 157 */ + { 0x02, 159, 0, "id-SHA-384" }, /* 158 */ + { 0x03, 0, 0, "id-SHA-512" }, /* 159 */ + { 0x86, 0, 1, "" }, /* 160 */ + { 0xf8, 0, 1, "" }, /* 161 */ + { 0x42, 174, 1, "netscape" }, /* 162 */ + { 0x01, 169, 1, "" }, /* 163 */ + { 0x01, 165, 0, "nsCertType" }, /* 164 */ + { 0x03, 166, 0, "nsRevocationUrl" }, /* 165 */ + { 0x04, 167, 0, "nsCaRevocationUrl" }, /* 166 */ + { 0x08, 168, 0, "nsCaPolicyUrl" }, /* 167 */ + { 0x0d, 0, 0, "nsComment" }, /* 168 */ + { 0x03, 172, 1, "directory" }, /* 169 */ + { 0x01, 0, 1, "" }, /* 170 */ + { 0x03, 0, 0, "employeeNumber" }, /* 171 */ + { 0x04, 0, 1, "policy" }, /* 172 */ + { 0x01, 0, 0, "nsSGC" }, /* 173 */ + { 0x45, 0, 1, "verisign" }, /* 174 */ + { 0x01, 0, 1, "pki" }, /* 175 */ + { 0x09, 0, 1, "attributes" }, /* 176 */ + { 0x02, 178, 0, "messageType" }, /* 177 */ + { 0x03, 179, 0, "pkiStatus" }, /* 178 */ + { 0x04, 180, 0, "failInfo" }, /* 179 */ + { 0x05, 181, 0, "senderNonce" }, /* 180 */ + { 0x06, 182, 0, "recipientNonce" }, /* 181 */ + { 0x07, 183, 0, "transID" }, /* 182 */ + { 0x08, 0, 0, "extensionReq" } /* 183 */ +}; diff --git a/src/pluto/oid.h b/src/pluto/oid.h new file mode 100644 index 000000000..ccdfb2954 --- /dev/null +++ b/src/pluto/oid.h @@ -0,0 +1,78 @@ +/* Object identifiers (OIDs) used by FreeS/WAN + * Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This file has been automatically generated by the script oid.pl + * Do not edit manually! + */ + +typedef struct { + u_char octet; + u_int next; + u_int down; + const u_char *name; +} oid_t; + +extern const oid_t oid_names[]; + +#define OID_UNKNOWN -1 +#define OID_ROLE 35 +#define OID_SUBJECT_KEY_ID 38 +#define OID_SUBJECT_ALT_NAME 41 +#define OID_BASIC_CONSTRAINTS 43 +#define OID_CRL_REASON_CODE 44 +#define OID_CRL_DISTRIBUTION_POINTS 45 +#define OID_AUTHORITY_KEY_ID 47 +#define OID_EXTENDED_KEY_USAGE 48 +#define OID_TARGET_INFORMATION 49 +#define OID_NO_REV_AVAIL 50 +#define OID_RSA_ENCRYPTION 59 +#define OID_MD2_WITH_RSA 60 +#define OID_MD5_WITH_RSA 61 +#define OID_SHA1_WITH_RSA 62 +#define OID_SHA256_WITH_RSA 63 +#define OID_SHA384_WITH_RSA 64 +#define OID_SHA512_WITH_RSA 65 +#define OID_PKCS7_DATA 67 +#define OID_PKCS7_SIGNED_DATA 68 +#define OID_PKCS7_ENVELOPED_DATA 69 +#define OID_PKCS7_SIGNED_ENVELOPED_DATA 70 +#define OID_PKCS7_DIGESTED_DATA 71 +#define OID_PKCS7_ENCRYPTED_DATA 72 +#define OID_PKCS9_EMAIL 74 +#define OID_PKCS9_CONTENT_TYPE 76 +#define OID_PKCS9_MESSAGE_DIGEST 77 +#define OID_PKCS9_SIGNING_TIME 78 +#define OID_MD2 84 +#define OID_MD5 85 +#define OID_3DES_EDE_CBC 87 +#define OID_AUTHORITY_INFO_ACCESS 109 +#define OID_OCSP_SIGNING 119 +#define OID_XMPP_ADDR 121 +#define OID_AUTHENTICATION_INFO 123 +#define OID_ACCESS_IDENTITY 124 +#define OID_CHARGING_IDENTITY 125 +#define OID_GROUP 126 +#define OID_OCSP 128 +#define OID_BASIC 129 +#define OID_NONCE 130 +#define OID_CRL 131 +#define OID_RESPONSE 132 +#define OID_NO_CHECK 133 +#define OID_ARCHIVE_CUTOFF 134 +#define OID_SERVICE_LOCATOR 135 +#define OID_DES_CBC 139 +#define OID_SHA1 140 +#define OID_SHA1_WITH_RSA_OIW 141 +#define OID_SHA256 157 +#define OID_SHA384 158 +#define OID_SHA512 159 +#define OID_NS_REVOCATION_URL 165 +#define OID_NS_CA_REVOCATION_URL 166 +#define OID_NS_CA_POLICY_URL 167 +#define OID_NS_COMMENT 168 +#define OID_PKI_MESSAGE_TYPE 177 +#define OID_PKI_STATUS 178 +#define OID_PKI_FAIL_INFO 179 +#define OID_PKI_SENDER_NONCE 180 +#define OID_PKI_RECIPIENT_NONCE 181 +#define OID_PKI_TRANS_ID 182 diff --git a/src/pluto/oid.pl b/src/pluto/oid.pl new file mode 100644 index 000000000..52ac8eae0 --- /dev/null +++ b/src/pluto/oid.pl @@ -0,0 +1,123 @@ +#!/usr/bin/perl +# Generates oid.h and oid.c out of oid.txt +# Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur +# +# This program 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. See . +# +# 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 General Public License +# for more details. +# + +$copyright="Copyright (C) 2003-2004 Andreas Steffen, Zuercher Hochschule Winterthur"; +$automatic="This file has been automatically generated by the script oid.pl"; +$warning="Do not edit manually!"; + +print "oid.pl generating oid.h and oid.c\n"; + +# Generate oid.h + +open(OID_H, ">oid.h") + or die "could not open 'oid.h': $!"; + +print OID_H "/* Object identifiers (OIDs) used by FreeS/WAN\n", + " * ", $copyright, "\n", + " * \n", + " * ", $automatic, "\n", + " * ", $warning, "\n", + " */\n\n", + "typedef struct {\n", + " u_char octet;\n", + " u_int next;\n", + " u_int down;\n", + " const u_char *name;\n", + "} oid_t;\n", + "\n", + "extern const oid_t oid_names[];\n", + "\n", + "#define OID_UNKNOWN -1\n"; + +# parse oid.txt + +open(SRC, ") +{ + $line =~ m/( *?)(0x\w{2})\s+(".*?")[ \t]*?([\w_]*?)\Z/; + + @order[$counter] = length($1); + @octet[$counter] = $2; + @name[$counter] = $3; + + if (length($1) > $max_order) + { + $max_order = length($1); + } + if (length($3) > $max_name) + { + $max_name = length($3); + } + if (length($4) > 0) + { + printf OID_H "#define %s%s%d\n", $4, "\t" x ((39-length($4))/8), $counter; + } + $counter++; +} + +close SRC; +close OID_H; + +# Generate oid.c + +open(OID_C, ">oid.c") + or die "could not open 'oid.c': $!"; + +print OID_C "/* List of some useful object identifiers (OIDs)\n", + " * ", $copyright, "\n", + " * \n", + " * ", $automatic, "\n", + " * ", $warning, "\n", + " */\n", + "\n", + "#include \n", + "\n", + "#include \"oid.h\"\n", + "\n", + "const oid_t oid_names[] = {\n"; + +for ($c = 0; $c < $counter; $c++) +{ + $next = 0; + + for ($d = $c+1; $d < $counter && @order[$d] >= @order[$c]; $d++) + { + if (@order[$d] == @order[$c]) + { + @next[$c] = $d; + last; + } + } + + printf OID_C " {%s%s,%s%3d, %d, %s%s}%s /* %3d */\n" + ,' ' x @order[$c] + , @octet[$c] + , ' ' x (1 + $max_order - @order[$c]) + , @next[$c] + , @order[$c+1] > @order[$c] + , @name[$c] + , ' ' x ($max_name - length(@name[$c])) + , $c != $counter-1 ? "," : " " + , $c; +} + +print OID_C "};\n" ; +close OID_C; diff --git a/src/pluto/oid.txt b/src/pluto/oid.txt new file mode 100644 index 000000000..e8750024e --- /dev/null +++ b/src/pluto/oid.txt @@ -0,0 +1,184 @@ +0x02 "ITU-T Administration" + 0x82 "" + 0x06 "Germany ITU-T member" + 0x01 "Deutsche Telekom AG" + 0x0A "" + 0x07 "" + 0x14 "ND" +0x09 "data" + 0x92 "" + 0x26 "" + 0x89 "" + 0x93 "" + 0xF2 "" + 0x2C "" + 0x64 "pilot" + 0x01 "pilotAttributeType" + 0x01 "UID" + 0x19 "DC" +0x55 "X.500" + 0x04 "X.509" + 0x03 "CN" + 0x04 "S" + 0x05 "SN" + 0x06 "C" + 0x07 "L" + 0x08 "ST" + 0x0A "O" + 0x0B "OU" + 0x0C "T" + 0x0D "D" + 0x24 "userCertificate" + 0x29 "N" + 0x2A "G" + 0x2B "I" + 0x2D "ID" + 0x48 "role" OID_ROLE + 0x1D "id-ce" + 0x09 "subjectDirectoryAttrs" + 0x0E "subjectKeyIdentifier" OID_SUBJECT_KEY_ID + 0x0F "keyUsage" + 0x10 "privateKeyUsagePeriod" + 0x11 "subjectAltName" OID_SUBJECT_ALT_NAME + 0x12 "issuerAltName" + 0x13 "basicConstraints" OID_BASIC_CONSTRAINTS + 0x15 "reasonCode" OID_CRL_REASON_CODE + 0x1F "crlDistributionPoints" OID_CRL_DISTRIBUTION_POINTS + 0x20 "certificatePolicies" + 0x23 "authorityKeyIdentifier" OID_AUTHORITY_KEY_ID + 0x25 "extendedKeyUsage" OID_EXTENDED_KEY_USAGE + 0x37 "targetInformation" OID_TARGET_INFORMATION + 0x38 "noRevAvail" OID_NO_REV_AVAIL +0x2A "" + 0x86 "" + 0x48 "" + 0x86 "" + 0xF7 "" + 0x0D "RSADSI" + 0x01 "PKCS" + 0x01 "PKCS-1" + 0x01 "rsaEncryption" OID_RSA_ENCRYPTION + 0x02 "md2WithRSAEncryption" OID_MD2_WITH_RSA + 0x04 "md5WithRSAEncryption" OID_MD5_WITH_RSA + 0x05 "sha-1WithRSAEncryption" OID_SHA1_WITH_RSA + 0x0B "sha256WithRSAEncryption" OID_SHA256_WITH_RSA + 0x0C "sha384WithRSAEncryption" OID_SHA384_WITH_RSA + 0x0D "sha512WithRSAEncryption" OID_SHA512_WITH_RSA + 0x07 "PKCS-7" + 0x01 "data" OID_PKCS7_DATA + 0x02 "signedData" OID_PKCS7_SIGNED_DATA + 0x03 "envelopedData" OID_PKCS7_ENVELOPED_DATA + 0x04 "signedAndEnvelopedData" OID_PKCS7_SIGNED_ENVELOPED_DATA + 0x05 "digestedData" OID_PKCS7_DIGESTED_DATA + 0x06 "encryptedData" OID_PKCS7_ENCRYPTED_DATA + 0x09 "PKCS-9" + 0x01 "E" OID_PKCS9_EMAIL + 0x02 "unstructuredName" + 0x03 "contentType" OID_PKCS9_CONTENT_TYPE + 0x04 "messageDigest" OID_PKCS9_MESSAGE_DIGEST + 0x05 "signingTime" OID_PKCS9_SIGNING_TIME + 0x06 "counterSignature" + 0x07 "challengePassword" + 0x08 "unstructuredAddress" + 0x0E "extensionRequest" + 0x02 "digestAlgorithm" + 0x02 "md2" OID_MD2 + 0x05 "md5" OID_MD5 + 0x03 "encryptionAlgorithm" + 0x07 "3des-ede-cbc" OID_3DES_EDE_CBC +0x2B "" + 0x06 "dod" + 0x01 "internet" + 0x04 "private" + 0x01 "enterprise" + 0x82 "" + 0x37 "Microsoft" + 0x0A "" + 0x03 "" + 0x03 "msSGC" + 0x89 "" + 0x31 "" + 0x01 "" + 0x01 "" + 0x02 "" + 0x02 "" + 0x4B "TCGID" + 0x05 "security" + 0x05 "mechanisms" + 0x07 "id-pkix" + 0x01 "id-pe" + 0x01 "authorityInfoAccess" OID_AUTHORITY_INFO_ACCESS + 0x03 "id-kp" + 0x01 "serverAuth" + 0x02 "clientAuth" + 0x03 "codeSigning" + 0x04 "emailProtection" + 0x05 "ipsecEndSystem" + 0x06 "ipsecTunnel" + 0x07 "ipsecUser" + 0x08 "timeStamping" + 0x09 "ocspSigning" OID_OCSP_SIGNING + 0x08 "id-otherNames" + 0x05 "xmppAddr" OID_XMPP_ADDR + 0x0A "id-aca" + 0x01 "authenticationInfo" OID_AUTHENTICATION_INFO + 0x02 "accessIdentity" OID_ACCESS_IDENTITY + 0x03 "chargingIdentity" OID_CHARGING_IDENTITY + 0x04 "group" OID_GROUP + 0x30 "id-ad" + 0x01 "ocsp" OID_OCSP + 0x01 "basic" OID_BASIC + 0x02 "nonce" OID_NONCE + 0x03 "crl" OID_CRL + 0x04 "response" OID_RESPONSE + 0x05 "noCheck" OID_NO_CHECK + 0x06 "archiveCutoff" OID_ARCHIVE_CUTOFF + 0x07 "serviceLocator" OID_SERVICE_LOCATOR + 0x0E "oiw" + 0x03 "secsig" + 0x02 "algorithms" + 0x07 "des-cbc" OID_DES_CBC + 0x1A "sha-1" OID_SHA1 + 0x1D "sha-1WithRSASignature" OID_SHA1_WITH_RSA_OIW + 0x24 "TeleTrusT" + 0x03 "algorithm" + 0x03 "signatureAlgorithm" + 0x01 "rsaSignature" + 0x02 "rsaSigWithripemd160" + 0x03 "rsaSigWithripemd128" + 0x04 "rsaSigWithripemd256" +0x60 "" + 0x86 "" + 0x48 "" + 0x01 "organization" + 0x65 "gov" + 0x03 "csor" + 0x04 "nistalgorithm" + 0x02 "hashalgs" + 0x01 "id-SHA-256" OID_SHA256 + 0x02 "id-SHA-384" OID_SHA384 + 0x03 "id-SHA-512" OID_SHA512 + 0x86 "" + 0xf8 "" + 0x42 "netscape" + 0x01 "" + 0x01 "nsCertType" + 0x03 "nsRevocationUrl" OID_NS_REVOCATION_URL + 0x04 "nsCaRevocationUrl" OID_NS_CA_REVOCATION_URL + 0x08 "nsCaPolicyUrl" OID_NS_CA_POLICY_URL + 0x0d "nsComment" OID_NS_COMMENT + 0x03 "directory" + 0x01 "" + 0x03 "employeeNumber" + 0x04 "policy" + 0x01 "nsSGC" + 0x45 "verisign" + 0x01 "pki" + 0x09 "attributes" + 0x02 "messageType" OID_PKI_MESSAGE_TYPE + 0x03 "pkiStatus" OID_PKI_STATUS + 0x04 "failInfo" OID_PKI_FAIL_INFO + 0x05 "senderNonce" OID_PKI_SENDER_NONCE + 0x06 "recipientNonce" OID_PKI_RECIPIENT_NONCE + 0x07 "transID" OID_PKI_TRANS_ID + 0x08 "extensionReq" diff --git a/src/pluto/packet.c b/src/pluto/packet.c new file mode 100644 index 000000000..9f04c8bb2 --- /dev/null +++ b/src/pluto/packet.c @@ -0,0 +1,1244 @@ +/* parsing packets: formats and tools + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: packet.c,v 1.7 2005/01/06 22:39:04 as Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "packet.h" +#include "whack.h" /* for RC_LOG_SERIOUS */ + +/* ISAKMP Header: for all messages + * layout from RFC 2408 "ISAKMP" section 3.1 + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Initiator ! + * ! Cookie ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Responder ! + * ! Cookie ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Message ID ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +static field_desc isa_fields[] = { + { ft_raw, COOKIE_SIZE, "initiator cookie", NULL }, + { ft_raw, COOKIE_SIZE, "responder cookie", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_enum, 8/BITS_PER_BYTE, "ISAKMP version", &version_names }, + { ft_enum, 8/BITS_PER_BYTE, "exchange type", &exchange_names }, + { ft_set, 8/BITS_PER_BYTE, "flags", flag_bit_names }, + { ft_raw, 32/BITS_PER_BYTE, "message ID", NULL }, + { ft_len, 32/BITS_PER_BYTE, "length", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_hdr_desc = { "ISAKMP Message", isa_fields, sizeof(struct isakmp_hdr) }; + +/* Generic portion of all ISAKMP payloads. + * layout from RFC 2408 "ISAKMP" section 3.2 + * This describes the first 32-bit chunk of all payloads. + * The previous next payload depends on the actual payload type. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +static field_desc isag_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_generic_desc = { "ISAKMP Generic Payload", isag_fields, sizeof(struct isakmp_generic) }; + + +/* ISAKMP Data Attribute (generic representation within payloads) + * layout from RFC 2408 "ISAKMP" section 3.3 + * This is not a payload type. + * In TLV format, this is followed by a value field. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * !A! Attribute Type ! AF=0 Attribute Length ! + * !F! ! AF=1 Attribute Value ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . AF=0 Attribute Value . + * . AF=1 Not Transmitted . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* Oakley Attributes */ +static field_desc isaat_fields_oakley[] = { + { ft_af_enum, 16/BITS_PER_BYTE, "af+type", &oakley_attr_names }, + { ft_lv, 16/BITS_PER_BYTE, "length/value", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_oakley_attribute_desc = { + "ISAKMP Oakley attribute", + isaat_fields_oakley, sizeof(struct isakmp_attribute) }; + +/* IPsec DOI Attributes */ +static field_desc isaat_fields_ipsec[] = { + { ft_af_enum, 16/BITS_PER_BYTE, "af+type", &ipsec_attr_names }, + { ft_lv, 16/BITS_PER_BYTE, "length/value", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_ipsec_attribute_desc = { + "ISAKMP IPsec DOI attribute", + isaat_fields_ipsec, sizeof(struct isakmp_attribute) }; + +/* Mode Config Attributes */ +static field_desc isaat_fields_modecfg[] = { + { ft_af_loose_enum, 16/BITS_PER_BYTE, "ModeCfg attr type", &modecfg_attr_names }, + { ft_lv, 16/BITS_PER_BYTE, "length/value", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_modecfg_attribute_desc = { + "ISAKMP ModeCfg attribute", + isaat_fields_modecfg, sizeof(struct isakmp_attribute) }; + +/* ISAKMP Security Association Payload + * layout from RFC 2408 "ISAKMP" section 3.4 + * A variable length Situation follows. + * Previous next payload: ISAKMP_NEXT_SA + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Domain of Interpretation (DOI) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Situation ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isasa_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_sa_desc = { "ISAKMP Security Association Payload", isasa_fields, sizeof(struct isakmp_sa) }; + +static field_desc ipsec_sit_field[] = { + { ft_set, 32/BITS_PER_BYTE, "IPsec DOI SIT", &sit_bit_names }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc ipsec_sit_desc = { "IPsec DOI SIT", ipsec_sit_field, sizeof(u_int32_t) }; + +/* ISAKMP Proposal Payload + * layout from RFC 2408 "ISAKMP" section 3.5 + * A variable length SPI follows. + * Previous next payload: ISAKMP_NEXT_P + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Proposal # ! Protocol-Id ! SPI Size !# of Transforms! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! SPI (variable) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isap_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "proposal number", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "protocol ID", &protocol_names }, + { ft_nat, 8/BITS_PER_BYTE, "SPI size", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "number of transforms", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_proposal_desc = { "ISAKMP Proposal Payload", isap_fields, sizeof(struct isakmp_proposal) }; + +/* ISAKMP Transform Payload + * layout from RFC 2408 "ISAKMP" section 3.6 + * Variable length SA Attributes follow. + * Previous next payload: ISAKMP_NEXT_T + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Transform # ! Transform-Id ! RESERVED2 ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ SA Attributes ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* PROTO_ISAKMP */ +static field_desc isat_fields_isakmp[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "transform ID", &isakmp_transformid_names }, + { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_isakmp_transform_desc = { + "ISAKMP Transform Payload (ISAKMP)", + isat_fields_isakmp, sizeof(struct isakmp_transform) }; + +/* PROTO_IPSEC_AH */ +static field_desc isat_fields_ah[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "transform ID", &ah_transformid_names }, + { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_ah_transform_desc = { + "ISAKMP Transform Payload (AH)", + isat_fields_ah, sizeof(struct isakmp_transform) }; + +/* PROTO_IPSEC_ESP */ +static field_desc isat_fields_esp[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "transform ID", &esp_transformid_names }, + { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_esp_transform_desc = { + "ISAKMP Transform Payload (ESP)", + isat_fields_esp, sizeof(struct isakmp_transform) }; + +/* PROTO_IPCOMP */ +static field_desc isat_fields_ipcomp[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "transform number", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "transform ID", &ipcomp_transformid_names }, + { ft_mbz, 16/BITS_PER_BYTE, NULL, NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_ipcomp_transform_desc = { + "ISAKMP Transform Payload (COMP)", + isat_fields_ipcomp, sizeof(struct isakmp_transform) }; + + +/* ISAKMP Key Exchange Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.7 + * Variable Key Exchange Data follow the generic fields. + * Previous next payload: ISAKMP_NEXT_KE + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Key Exchange Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct_desc isakmp_keyex_desc = { "ISAKMP Key Exchange Payload", isag_fields, sizeof(struct isakmp_generic) }; + +/* ISAKMP Identification Payload + * layout from RFC 2408 "ISAKMP" section 3.8 + * See "struct identity" declared later. + * Variable length Identification Data follow. + * Previous next payload: ISAKMP_NEXT_ID + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ID Type ! DOI Specific ID Data ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Identification Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isaid_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "ID type", &ident_names }, /* ??? depends on DOI? */ + { ft_nat, 8/BITS_PER_BYTE, "DOI specific A", NULL }, /* ??? depends on DOI? */ + { ft_nat, 16/BITS_PER_BYTE, "DOI specific B", NULL }, /* ??? depends on DOI? */ + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_identification_desc = { "ISAKMP Identification Payload", isaid_fields, sizeof(struct isakmp_id) }; + +/* IPSEC Identification Payload Content + * layout from RFC 2407 "IPsec DOI" section 4.6.2 + * See struct isakmp_id declared earlier. + * Note: Hashing skips the ISAKMP generic payload header + * Variable length Identification Data follow. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ID Type ! Protocol ID ! Port ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ Identification Data ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isaiid_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "ID type", &ident_names }, + { ft_nat, 8/BITS_PER_BYTE, "Protocol ID", NULL }, /* ??? UDP/TCP or 0? */ + { ft_nat, 16/BITS_PER_BYTE, "port", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_ipsec_identification_desc = { "ISAKMP Identification Payload (IPsec DOI)", isaiid_fields, sizeof(struct isakmp_ipsec_id) }; + +/* ISAKMP Certificate Payload: oddball fixed field beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.9 + * Variable length Certificate Data follow the generic fields. + * Previous next payload: ISAKMP_NEXT_CERT. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Cert Encoding ! ! + * +-+-+-+-+-+-+-+-+ ! + * ~ Certificate Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isacert_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "cert encoding", &cert_type_names }, + { ft_end, 0, NULL, NULL } +}; + +/* Note: the size field of isakmp_ipsec_certificate_desc cannot be + * sizeof(struct isakmp_cert) because that will rounded up for padding. + */ + struct_desc isakmp_ipsec_certificate_desc = { "ISAKMP Certificate Payload", isacert_fields, ISAKMP_CERT_SIZE }; + +/* ISAKMP Certificate Request Payload: oddball field beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.10 + * Variable length Certificate Types and Certificate Authorities follow. + * Previous next payload: ISAKMP_NEXT_CR. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Cert. Type ! ! + * +-+-+-+-+-+-+-+-+ ! + * ~ Certificate Authority ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isacr_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "cert type", &cert_type_names }, + { ft_end, 0, NULL, NULL } +}; + +/* Note: the size field of isakmp_ipsec_cert_req_desc cannot be + * sizeof(struct isakmp_cr) because that will rounded up for padding. + */ +struct_desc isakmp_ipsec_cert_req_desc = { "ISAKMP Certificate RequestPayload", isacr_fields, ISAKMP_CR_SIZE }; + +/* ISAKMP Hash Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.11 + * Variable length Hash Data follow. + * Previous next payload: ISAKMP_NEXT_HASH. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Hash Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct_desc isakmp_hash_desc = { "ISAKMP Hash Payload", isag_fields, sizeof(struct isakmp_generic) }; + +/* ISAKMP Signature Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.12 + * Variable length Signature Data follow. + * Previous next payload: ISAKMP_NEXT_SIG. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Signature Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct_desc isakmp_signature_desc = { "ISAKMP Signature Payload", isag_fields, sizeof(struct isakmp_generic) }; + +/* ISAKMP Nonce Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.13 + * Variable length Nonce Data follow. + * Previous next payload: ISAKMP_NEXT_NONCE. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Nonce Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct_desc isakmp_nonce_desc = { "ISAKMP Nonce Payload", isag_fields, sizeof(struct isakmp_generic) }; + +/* ISAKMP Notification Payload + * layout from RFC 2408 "ISAKMP" section 3.14 + * This is followed by a variable length SPI + * and then possibly by variable length Notification Data. + * Previous next payload: ISAKMP_NEXT_N + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Domain of Interpretation (DOI) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Protocol-ID ! SPI Size ! Notify Message Type ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Security Parameter Index (SPI) ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Notification Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isan_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names }, + { ft_nat, 8/BITS_PER_BYTE, "protocol ID", NULL }, /* ??? really enum: ISAKMP, IPSEC, ESP, ... */ + { ft_nat, 8/BITS_PER_BYTE, "SPI size", NULL }, + { ft_enum, 16/BITS_PER_BYTE, "Notify Message Type", ¬ification_names }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_notification_desc = { "ISAKMP Notification Payload", isan_fields, sizeof(struct isakmp_notification) }; + +/* ISAKMP Delete Payload + * layout from RFC 2408 "ISAKMP" section 3.15 + * This is followed by a variable length SPI. + * Previous next payload: ISAKMP_NEXT_D + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Domain of Interpretation (DOI) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Protocol-Id ! SPI Size ! # of SPIs ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Security Parameter Index(es) (SPI) ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isad_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names }, + { ft_nat, 8/BITS_PER_BYTE, "protocol ID", NULL }, /* ??? really enum: ISAKMP, IPSEC */ + { ft_nat, 8/BITS_PER_BYTE, "SPI size", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "number of SPIs", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_delete_desc = { "ISAKMP Delete Payload", isad_fields, sizeof(struct isakmp_delete) }; + +/* ISAKMP Vendor ID Payload + * layout from RFC 2408 "ISAKMP" section 3.15 + * This is followed by a variable length VID. + * Previous next payload: ISAKMP_NEXT_VID + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Vendor ID (VID) ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct_desc isakmp_vendor_id_desc = { "ISAKMP Vendor ID Payload", isag_fields, sizeof(struct isakmp_generic) }; + +/* MODECFG */ +/* + * From draft-dukes-ike-mode-cfg +3.2. Attribute Payload + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload ! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Type ! RESERVED ! Identifier ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Attributes ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +static field_desc isaattr_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "Attr Msg Type", &attr_msg_type_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Identifier", NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_attr_desc = { "ISAKMP Mode Attribute", isaattr_fields, sizeof(struct isakmp_mode_attr) }; + +/* ISAKMP NAT-Traversal NAT-D + * layout from draft-ietf-ipsec-nat-t-ike-01.txt section 3.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! HASH of the address and port ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct_desc isakmp_nat_d = { "ISAKMP NAT-D Payload", isag_fields, sizeof(struct isakmp_generic) }; + +/* ISAKMP NAT-Traversal NAT-OA + * layout from draft-ietf-ipsec-nat-t-ike-01.txt section 4.2 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ID Type ! RESERVED ! RESERVED ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! IPv4 (4 octets) or IPv6 address (16 octets) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static field_desc isanat_oa_fields[] = { + { ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names }, + { ft_mbz, 8/BITS_PER_BYTE, NULL, NULL }, + { ft_len, 16/BITS_PER_BYTE, "length", NULL }, + { ft_enum, 8/BITS_PER_BYTE, "ID type", &ident_names }, + { ft_mbz, 24/BITS_PER_BYTE, NULL, NULL }, + { ft_end, 0, NULL, NULL } +}; + +struct_desc isakmp_nat_oa = { "ISAKMP NAT-OA Payload", isanat_oa_fields, sizeof(struct isakmp_nat_oa) }; + +/* descriptor for each payload type + * + * There is a slight problem in that some payloads differ, depending + * on the mode. Since this is table only used for top-level payloads, + * Proposal and Transform payloads need not be handled. + * That leaves only Identification payloads as a problem. + * We make all these entries NULL + */ +struct_desc *const payload_descs[ISAKMP_NEXT_ROOF] = { + NULL, /* 0 ISAKMP_NEXT_NONE (No other payload following) */ + &isakmp_sa_desc, /* 1 ISAKMP_NEXT_SA (Security Association) */ + NULL, /* 2 ISAKMP_NEXT_P (Proposal) */ + NULL, /* 3 ISAKMP_NEXT_T (Transform) */ + &isakmp_keyex_desc, /* 4 ISAKMP_NEXT_KE (Key Exchange) */ + NULL, /* 5 ISAKMP_NEXT_ID (Identification) */ + &isakmp_ipsec_certificate_desc, /* 6 ISAKMP_NEXT_CERT (Certificate) */ + &isakmp_ipsec_cert_req_desc, /* 7 ISAKMP_NEXT_CR (Certificate Request) */ + &isakmp_hash_desc, /* 8 ISAKMP_NEXT_HASH (Hash) */ + &isakmp_signature_desc, /* 9 ISAKMP_NEXT_SIG (Signature) */ + &isakmp_nonce_desc, /* 10 ISAKMP_NEXT_NONCE (Nonce) */ + &isakmp_notification_desc, /* 11 ISAKMP_NEXT_N (Notification) */ + &isakmp_delete_desc, /* 12 ISAKMP_NEXT_D (Delete) */ + &isakmp_vendor_id_desc, /* 13 ISAKMP_NEXT_VID (Vendor ID) */ + &isakmp_attr_desc, /* 14 ISAKMP_NEXT_ATTR (Mode Config) */ + NULL, /* 15 */ + NULL, /* 16 */ + NULL, /* 17 */ + NULL, /* 18 */ + NULL, /* 19 */ + &isakmp_nat_d, /* 20=130 ISAKMP_NEXT_NATD (NAT-D) */ + &isakmp_nat_oa, /* 20=131 ISAKMP_NEXT_NATOA (NAT-OA) */ +}; + +void +init_pbs(pb_stream *pbs, u_int8_t *start, size_t len, const char *name) +{ + pbs->container = NULL; + pbs->desc = NULL; + pbs->name = name; + pbs->start = pbs->cur = start; + pbs->roof = start + len; + pbs->lenfld = NULL; + pbs->lenfld_desc = NULL; +} + +#ifdef DEBUG + +/* print a host struct + * + * This code assumes that the network and host structure + * members have the same alignment and size! This requires + * that all padding be explicit. + */ +void +DBG_print_struct(const char *label, const void *struct_ptr +, struct_desc *sd, bool len_meaningful) +{ + bool immediate = FALSE; + const u_int8_t *inp = struct_ptr; + field_desc *fp; + + DBG_log("%s%s:", label, sd->name); + + for (fp = sd->fields; fp->field_type != ft_end; fp++) + { + int i = fp->size; + u_int32_t n = 0; + + switch (fp->field_type) + { + case ft_mbz: /* must be zero */ + inp += i; + break; + case ft_nat: /* natural number (may be 0) */ + case ft_len: /* length of this struct and any following crud */ + case ft_lv: /* length/value field of attribute */ + case ft_enum: /* value from an enumeration */ + case ft_loose_enum: /* value from an enumeration with only some names known */ + case ft_af_enum: /* Attribute Format + value from an enumeration */ + case ft_af_loose_enum: /* Attribute Format + value from an enumeration */ + case ft_set: /* bits representing set */ + switch (i) + { + case 8/BITS_PER_BYTE: + n = *(const u_int8_t *)inp; + break; + case 16/BITS_PER_BYTE: + n = *(const u_int16_t *)inp; + break; + case 32/BITS_PER_BYTE: + n = *(const u_int32_t *)inp; + break; + default: + bad_case(i); + } + switch (fp->field_type) + { + case ft_len: /* length of this struct and any following crud */ + case ft_lv: /* length/value field of attribute */ + if (!immediate && !len_meaningful) + break; + /* FALL THROUGH */ + case ft_nat: /* natural number (may be 0) */ + DBG_log(" %s: %lu", fp->name, (unsigned long)n); + break; + case ft_af_enum: /* Attribute Format + value from an enumeration */ + case ft_af_loose_enum: /* Attribute Format + value from an enumeration */ + if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV) + immediate = TRUE; + /* FALL THROUGH */ + case ft_enum: /* value from an enumeration */ + case ft_loose_enum: /* value from an enumeration with only some names known */ + DBG_log(" %s: %s", fp->name, enum_show(fp->desc, n)); + break; + case ft_set: /* bits representing set */ + DBG_log(" %s: %s", fp->name, bitnamesof(fp->desc, n)); + break; + default: + bad_case(fp->field_type); + } + inp += i; + break; + + case ft_raw: /* bytes to be left in network-order */ + { + char m[50]; /* arbitrary limit on name width in log */ + + snprintf(m, sizeof(m), " %s:", fp->name); + DBG_dump(m, inp, i); + inp += i; + } + break; + default: + bad_case(fp->field_type); + } + } +} + +static void +DBG_prefix_print_struct(const pb_stream *pbs +, const char *label, const void *struct_ptr +, struct_desc *sd, bool len_meaningful) +{ + /* print out a title, with a prefix of asterisks to show + * the nesting level. + */ + char space[40]; /* arbitrary limit on label+flock-of-* */ + size_t len = strlen(label); + + if (sizeof(space) <= len) + { + DBG_print_struct(label, struct_ptr, sd, len_meaningful); + } + else + { + const pb_stream *p = pbs; + char *pre = &space[sizeof(space) - (len + 1)]; + + strcpy(pre, label); + + /* put at least one * out */ + for (;;) + { + if (pre <= space) + break; + *--pre = '*'; + if (p == NULL) + break; + p = p->container; + } + DBG_print_struct(pre, struct_ptr, sd, len_meaningful); + } +} + +#endif + +/* "parse" a network struct into a host struct. + * + * This code assumes that the network and host structure + * members have the same alignment and size! This requires + * that all padding be explicit. + * + * If obj_pbs is supplied, a new pb_stream is created for the + * variable part of the structure (this depends on their + * being one length field in the structure). The cursor of this + * new PBS is set to after the parsed part of the struct. + * + * This routine returns TRUE iff it succeeds. + */ + +bool +in_struct(void *struct_ptr, struct_desc *sd +, pb_stream *ins, pb_stream *obj_pbs) +{ + err_t ugh = NULL; + u_int8_t *cur = ins->cur; + + if (ins->roof - cur < (ptrdiff_t)sd->size) + { + ugh = builddiag("not enough room in input packet for %s", sd->name); + } + else + { + u_int8_t *roof = cur + sd->size; /* may be changed by a length field */ + u_int8_t *outp = struct_ptr; + bool immediate = FALSE; + field_desc *fp; + + for (fp = sd->fields; ugh == NULL; fp++) + { + size_t i = fp->size; + + passert(ins->roof - cur >= (ptrdiff_t)i); + passert(cur - ins->cur <= (ptrdiff_t)(sd->size - i)); + passert(outp - (cur - ins->cur) == struct_ptr); + +#if 0 + DBG(DBG_PARSING, DBG_log("%d %s" + , (int) (cur - ins->cur), fp->name == NULL? "" : fp->name)); +#endif + switch (fp->field_type) + { + case ft_mbz: /* must be zero */ + for (; i != 0; i--) + { + if (*cur++ != 0) + { + ugh = builddiag("byte %d of %s must be zero, but is not" + , (int) (cur - ins->cur), sd->name); + break; + } + *outp++ = '\0'; /* probably redundant */ + } + break; + + case ft_nat: /* natural number (may be 0) */ + case ft_len: /* length of this struct and any following crud */ + case ft_lv: /* length/value field of attribute */ + case ft_enum: /* value from an enumeration */ + case ft_loose_enum: /* value from an enumeration with only some names known */ + case ft_af_enum: /* Attribute Format + value from an enumeration */ + case ft_af_loose_enum: /* Attribute Format + value from an enumeration */ + case ft_set: /* bits representing set */ + { + u_int32_t n = 0; + + for (; i != 0; i--) + n = (n << BITS_PER_BYTE) | *cur++; + + switch (fp->field_type) + { + case ft_len: /* length of this struct and any following crud */ + case ft_lv: /* length/value field of attribute */ + { + u_int32_t len = fp->field_type == ft_len? n + : immediate? sd->size : n + sd->size; + + if (len < sd->size) + { + ugh = builddiag("%s of %s is smaller than minimum" + , fp->name, sd->name); + } + else if (pbs_left(ins) < len) + { + ugh = builddiag("%s of %s is larger than can fit" + , fp->name, sd->name); + } + else + { + roof = ins->cur + len; + } + break; + } + case ft_af_loose_enum: /* Attribute Format + value from an enumeration */ + if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV) + immediate = TRUE; + break; + case ft_af_enum: /* Attribute Format + value from an enumeration */ + if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV) + immediate = TRUE; + /* FALL THROUGH */ + case ft_enum: /* value from an enumeration */ + if (enum_name(fp->desc, n) == NULL) + { + ugh = builddiag("%s of %s has an unknown value: %lu" + , fp->name, sd->name, (unsigned long)n); + } + /* FALL THROUGH */ + case ft_loose_enum: /* value from an enumeration with only some names known */ + break; + case ft_set: /* bits representing set */ + if (!testset(fp->desc, n)) + { + ugh = builddiag("bitset %s of %s has unknown member(s): %s" + , fp->name, sd->name, bitnamesof(fp->desc, n)); + } + break; + default: + break; + } + i = fp->size; + switch (i) + { + case 8/BITS_PER_BYTE: + *(u_int8_t *)outp = n; + break; + case 16/BITS_PER_BYTE: + *(u_int16_t *)outp = n; + break; + case 32/BITS_PER_BYTE: + *(u_int32_t *)outp = n; + break; + default: + bad_case(i); + } + outp += i; + break; + } + + case ft_raw: /* bytes to be left in network-order */ + for (; i != 0; i--) + { + *outp++ = *cur++; + } + break; + + case ft_end: /* end of field list */ + passert(cur == ins->cur + sd->size); + if (obj_pbs != NULL) + { + init_pbs(obj_pbs, ins->cur, roof - ins->cur, sd->name); + obj_pbs->container = ins; + obj_pbs->desc = sd; + obj_pbs->cur = cur; + } + ins->cur = roof; + DBG(DBG_PARSING + , DBG_prefix_print_struct(ins, "parse ", struct_ptr, sd, TRUE)); + return TRUE; + + default: + bad_case(fp->field_type); + } + } + } + + /* some failure got us here: report it */ + loglog(RC_LOG_SERIOUS, ugh); + return FALSE; +} + +bool +in_raw(void *bytes, size_t len, pb_stream *ins, const char *name) +{ + if (pbs_left(ins) < len) + { + loglog(RC_LOG_SERIOUS, "not enough bytes left to get %s from %s", name, ins->name); + return FALSE; + } + else + { + if (bytes == NULL) + { + DBG(DBG_PARSING + , DBG_log("skipping %u raw bytes of %s (%s)" + , (unsigned) len, ins->name, name); + DBG_dump(name, ins->cur, len)); + } + else + { + memcpy(bytes, ins->cur, len); + DBG(DBG_PARSING + , DBG_log("parsing %u raw bytes of %s into %s" + , (unsigned) len, ins->name, name); + DBG_dump(name, bytes, len)); + } + ins->cur += len; + return TRUE; + } +} + +/* "emit" a host struct into a network packet. + * + * This code assumes that the network and host structure + * members have the same alignment and size! This requires + * that all padding be explicit. + * + * If obj_pbs is non-NULL, its pbs describes a new output stream set up + * to contain the object. The cursor will be left at the variable part. + * This new stream must subsequently be finalized by close_output_pbs(). + * + * The value of any field of type ft_len is computed, not taken + * from the input struct. The length is actually filled in when + * the object's output stream is finalized. If obj_pbs is NULL, + * finalization is done by out_struct before it returns. + * + * This routine returns TRUE iff it succeeds. + */ + +bool +out_struct(const void *struct_ptr, struct_desc *sd +, pb_stream *outs, pb_stream *obj_pbs) +{ + err_t ugh = NULL; + const u_int8_t *inp = struct_ptr; + u_int8_t *cur = outs->cur; + + DBG(DBG_EMITTING + , DBG_prefix_print_struct(outs, "emit ", struct_ptr, sd, obj_pbs==NULL)); + + if (outs->roof - cur < (ptrdiff_t)sd->size) + { + ugh = builddiag("not enough room left in output packet to place %s" + , sd->name); + } + else + { + bool immediate = FALSE; + pb_stream obj; + field_desc *fp; + + obj.lenfld = NULL; /* until a length field is discovered */ + obj.lenfld_desc = NULL; + + for (fp = sd->fields; ugh == NULL; fp++) + { + size_t i = fp->size; + + passert(outs->roof - cur >= (ptrdiff_t)i); + passert(cur - outs->cur <= (ptrdiff_t)(sd->size - i)); + passert(inp - (cur - outs->cur) == struct_ptr); + +#if 0 + DBG(DBG_EMITTING, DBG_log("%d %s" + , (int) (cur - outs->cur), fp->name == NULL? "" : fp->name); +#endif + switch (fp->field_type) + { + case ft_mbz: /* must be zero */ + inp += i; + for (; i != 0; i--) + *cur++ = '\0'; + break; + case ft_nat: /* natural number (may be 0) */ + case ft_len: /* length of this struct and any following crud */ + case ft_lv: /* length/value field of attribute */ + case ft_enum: /* value from an enumeration */ + case ft_loose_enum: /* value from an enumeration with only some names known */ + case ft_af_enum: /* Attribute Format + value from an enumeration */ + case ft_af_loose_enum: /* Attribute Format + value from an enumeration */ + case ft_set: /* bits representing set */ + { + u_int32_t n = 0; + + switch (i) + { + case 8/BITS_PER_BYTE: + n = *(const u_int8_t *)inp; + break; + case 16/BITS_PER_BYTE: + n = *(const u_int16_t *)inp; + break; + case 32/BITS_PER_BYTE: + n = *(const u_int32_t *)inp; + break; + default: + bad_case(i); + } + + switch (fp->field_type) + { + case ft_len: /* length of this struct and any following crud */ + case ft_lv: /* length/value field of attribute */ + if (immediate) + break; /* not a length */ + /* We can't check the length because it will likely + * be filled in after variable part is supplied. + * We do record where this is so that it can be + * filled in by a subsequent close_output_pbs(). + */ + passert(obj.lenfld == NULL); /* only one ft_len allowed */ + obj.lenfld = cur; + obj.lenfld_desc = fp; + break; + case ft_af_loose_enum: /* Attribute Format + value from an enumeration */ + if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV) + immediate = TRUE; + break; + case ft_af_enum: /* Attribute Format + value from an enumeration */ + if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV) + immediate = TRUE; + /* FALL THROUGH */ + case ft_enum: /* value from an enumeration */ + if (enum_name(fp->desc, n) == NULL) + { + ugh = builddiag("%s of %s has an unknown value: %lu" + , fp->name, sd->name, (unsigned long)n); + } + /* FALL THROUGH */ + case ft_loose_enum: /* value from an enumeration with only some names known */ + break; + case ft_set: /* bits representing set */ + if (!testset(fp->desc, n)) + { + ugh = builddiag("bitset %s of %s has unknown member(s): %s" + , fp->name, sd->name, bitnamesof(fp->desc, n)); + } + break; + default: + break; + } + + while (i-- != 0) + { + cur[i] = (u_int8_t)n; + n >>= BITS_PER_BYTE; + } + inp += fp->size; + cur += fp->size; + break; + } + case ft_raw: /* bytes to be left in network-order */ + for (; i != 0; i--) + *cur++ = *inp++; + break; + case ft_end: /* end of field list */ + passert(cur == outs->cur + sd->size); + + obj.container = outs; + obj.desc = sd; + obj.name = sd->name; + obj.start = outs->cur; + obj.cur = cur; + obj.roof = outs->roof; /* limit of possible */ + /* obj.lenfld and obj.lenfld_desc already set */ + + if (obj_pbs == NULL) + { + close_output_pbs(&obj); /* fill in length field, if any */ + } + else + { + /* We set outs->cur to outs->roof so that + * any attempt to output something into outs + * before obj is closed will trigger an error. + */ + outs->cur = outs->roof; + + *obj_pbs = obj; + } + return TRUE; + + default: + bad_case(fp->field_type); + } + } + } + + /* some failure got us here: report it */ + loglog(RC_LOG_SERIOUS, ugh); /* ??? serious, but errno not relevant */ + return FALSE; +} + +bool +out_generic(u_int8_t np, struct_desc *sd +, pb_stream *outs, pb_stream *obj_pbs) +{ + struct isakmp_generic gen; + + passert(sd->fields == isakmp_generic_desc.fields); + gen.isag_np = np; + return out_struct(&gen, sd, outs, obj_pbs); +} + +bool +out_generic_raw(u_int8_t np, struct_desc *sd +, pb_stream *outs, const void *bytes, size_t len, const char *name) +{ + pb_stream pbs; + + if (!out_generic(np, sd, outs, &pbs) + || !out_raw(bytes, len, &pbs, name)) + return FALSE; + close_output_pbs(&pbs); + return TRUE; +} + +bool +out_raw(const void *bytes, size_t len, pb_stream *outs, const char *name) +{ + if (pbs_left(outs) < len) + { + loglog(RC_LOG_SERIOUS, "not enough room left to place %lu bytes of %s in %s" + , (unsigned long) len, name, outs->name); + return FALSE; + } + else + { + DBG(DBG_EMITTING + , DBG_log("emitting %u raw bytes of %s into %s" + , (unsigned) len, name, outs->name); + DBG_dump(name, bytes, len)); + memcpy(outs->cur, bytes, len); + outs->cur += len; + return TRUE; + } +} + +bool +out_zero(size_t len, pb_stream *outs, const char *name) +{ + if (pbs_left(outs) < len) + { + loglog(RC_LOG_SERIOUS, "not enough room left to place %s in %s", name, outs->name); + return FALSE; + } + else + { + DBG(DBG_EMITTING, DBG_log("emitting %u zero bytes of %s into %s" + , (unsigned) len, name, outs->name)); + memset(outs->cur, 0x00, len); + outs->cur += len; + return TRUE; + } +} + +/* Record current length. + * Note: currently, this may be repeated any number of times; + * the last one wins. + */ +void +close_output_pbs(pb_stream *pbs) +{ + if (pbs->lenfld != NULL) + { + u_int32_t len = pbs_offset(pbs); + int i = pbs->lenfld_desc->size; + + if (pbs->lenfld_desc->field_type == ft_lv) + len -= sizeof(struct isakmp_attribute); + DBG(DBG_EMITTING, DBG_log("emitting length of %s: %lu" + , pbs->name, (unsigned long) len)); + while (i-- != 0) + { + pbs->lenfld[i] = (u_int8_t)len; + len >>= BITS_PER_BYTE; + } + } + if (pbs->container != NULL) + pbs->container->cur = pbs->cur; /* pass space utilization up */ +} diff --git a/src/pluto/packet.h b/src/pluto/packet.h new file mode 100644 index 000000000..676a5e6cd --- /dev/null +++ b/src/pluto/packet.h @@ -0,0 +1,655 @@ +/* parsing packets: formats and tools + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: packet.h,v 1.5 2005/01/06 22:10:15 as Exp $ + */ + +#ifndef _PACKET_H +#define _PACKET_H + +/* a struct_desc describes a structure for the struct I/O routines. + * This requires arrays of field_desc values to describe struct fields. + */ + +typedef const struct struct_desc { + const char *name; + const struct field_desc *fields; + size_t size; +} struct_desc; + +/* Note: if an ft_af_enum field has the ISAKMP_ATTR_AF_TV bit set, + * the subsequent ft_lv field will be interpreted as an immediate value. + * This matches how attributes are encoded. + * See RFC 2408 "ISAKMP" 3.3 + */ + +enum field_type { + ft_mbz, /* must be zero */ + ft_nat, /* natural number (may be 0) */ + ft_len, /* length of this struct and any following crud */ + ft_lv, /* length/value field of attribute */ + ft_enum, /* value from an enumeration */ + ft_loose_enum, /* value from an enumeration with only some names known */ + ft_af_loose_enum, /* Attribute Format + enumeration, some names known */ + ft_af_enum, /* Attribute Format + value from an enumeration */ + ft_set, /* bits representing set */ + ft_raw, /* bytes to be left in network-order */ + ft_end, /* end of field list */ +}; + +typedef const struct field_desc { + enum field_type field_type; + int size; /* size, in bytes, of field */ + const char *name; + const void *desc; /* enum_names for enum or char *[] for bits */ +} field_desc; + +/* The formatting of input and output of packets is done + * through packet_byte_stream objects. + * These describe a stream of bytes in memory. + * Several routines are provided to manipulate these objects + * Actual packet transfer is done elsewhere. + */ +typedef struct packet_byte_stream { + struct packet_byte_stream *container; /* PBS of which we are part */ + struct_desc *desc; + const char *name; /* what does this PBS represent? */ + u_int8_t + *start, + *cur, /* current position in stream */ + *roof; /* byte after last in PBS (actually just a limit on output) */ + /* For an output PBS, the length field will be filled in later so + * we need to record its particulars. Note: it may not be aligned. + */ + u_int8_t *lenfld; + field_desc *lenfld_desc; +} pb_stream; + +/* For an input PBS, pbs_offset is amount of stream processed. + * For an output PBS, pbs_offset is current size of stream. + * For an input PBS, pbs_room is size of stream. + * For an output PBS, pbs_room is maximum size allowed. + */ +#define pbs_offset(pbs) ((size_t)((pbs)->cur - (pbs)->start)) +#define pbs_room(pbs) ((size_t)((pbs)->roof - (pbs)->start)) +#define pbs_left(pbs) ((size_t)((pbs)->roof - (pbs)->cur)) + +extern void init_pbs(pb_stream *pbs, u_int8_t *start, size_t len, const char *name); + +extern bool in_struct(void *struct_ptr, struct_desc *sd, + pb_stream *ins, pb_stream *obj_pbs); +extern bool in_raw(void *bytes, size_t len, pb_stream *ins, const char *name); + +extern bool out_struct(const void *struct_ptr, struct_desc *sd, + pb_stream *outs, pb_stream *obj_pbs); +extern bool out_generic(u_int8_t np, struct_desc *sd, + pb_stream *outs, pb_stream *obj_pbs); +extern bool out_generic_raw(u_int8_t np, struct_desc *sd, + pb_stream *outs, const void *bytes, size_t len, const char *name); +#define out_generic_chunk(np, sd, outs, ch, name) \ + out_generic_raw(np, sd, outs, (ch).ptr, (ch).len, name) +extern bool out_zero(size_t len, pb_stream *outs, const char *name); +extern bool out_raw(const void *bytes, size_t len, pb_stream *outs, const char *name); +#define out_chunk(ch, outs, name) out_raw((ch).ptr, (ch).len, (outs), (name)) +extern void close_output_pbs(pb_stream *pbs); + +#ifdef DEBUG +extern void DBG_print_struct(const char *label, const void *struct_ptr, + struct_desc *sd, bool len_meaningful); +#endif + +/* ISAKMP Header: for all messages + * layout from RFC 2408 "ISAKMP" section 3.1 + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Initiator ! + * ! Cookie ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Responder ! + * ! Cookie ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Message ID ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * Although the drafts are a little unclear, there are a few + * places that specify that messages should be padded with 0x00 + * octets (bytes) to make the length a multiple of something. + * + * RFC 2408 "ISAKMP" 3.6 specifies that all messages will be + * padded to be a multiple of 4 octets in length. + * ??? This looks vestigial, and we ignore this requirement. + * + * RFC 2409 "IKE" Appedix B specifies: + * Each message should be padded up to the nearest block size + * using bytes containing 0x00. + * ??? This does not appear to be limited to encrypted messages, + * but it surely must be: the block size is meant to be the encryption + * block size, and that is meaningless for a non-encrypted message. + * + * RFC 2409 "IKE" 5.3 specifies: + * Encrypted payloads are padded up to the nearest block size. + * All padding bytes, except for the last one, contain 0x00. The + * last byte of the padding contains the number of the padding + * bytes used, excluding the last one. Note that this means there + * will always be padding. + * ??? This is nuts since payloads are not padded, messages are. + * It also contradicts Appendix B. So we ignore it. + * + * Summary: we pad encrypted output messages with 0x00 to bring them + * up to a multiple of the encryption block size. On input, we require + * that any encrypted portion of a message be a multiple of the encryption + * block size. After any decryption, we ignore padding (any bytes after + * the first payload that specifies a next payload of none; we don't + * require them to be zero). + */ + +struct isakmp_hdr +{ + u_int8_t isa_icookie[COOKIE_SIZE]; + u_int8_t isa_rcookie[COOKIE_SIZE]; + u_int8_t isa_np; /* Next payload */ + u_int8_t isa_version; /* high-order 4 bits: Major; low order 4: Minor */ +#define ISA_MAJ_SHIFT 4 +#define ISA_MIN_MASK (~((~0u) << ISA_MAJ_SHIFT)) + u_int8_t isa_xchg; /* Exchange type */ + u_int8_t isa_flags; + u_int32_t isa_msgid; /* Message ID (RAW) */ + u_int32_t isa_length; /* Length of message */ +}; + +extern struct_desc isakmp_hdr_desc; + +/* Generic portion of all ISAKMP payloads. + * layout from RFC 2408 "ISAKMP" section 3.2 + * This describes the first 32-bit chunk of all payloads. + * The previous next payload depends on the actual payload type. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_generic +{ + u_int8_t isag_np; + u_int8_t isag_reserved; + u_int16_t isag_length; +}; + +extern struct_desc isakmp_generic_desc; + +/* ISAKMP Data Attribute (generic representation within payloads) + * layout from RFC 2408 "ISAKMP" section 3.3 + * This is not a payload type. + * In TLV format, this is followed by a value field. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * !A! Attribute Type ! AF=0 Attribute Length ! + * !F! ! AF=1 Attribute Value ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . AF=0 Attribute Value . + * . AF=1 Not Transmitted . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_attribute +{ + /* The high order bit of isaat_af_type is the Attribute Format + * If it is off, the format is TLV: lv is the length of the following + * attribute value. + * If it is on, the format is TV: lv is the value of the attribute. + * ISAKMP_ATTR_AF_MASK is the mask in host form. + * + * The low order 15 bits of isaat_af_type is the Attribute Type. + * ISAKMP_ATTR_RTYPE_MASK is the mask in host form. + */ + u_int16_t isaat_af_type; /* high order bit: AF; lower 15: rtype */ + u_int16_t isaat_lv; /* Length or value */ +}; + +#define ISAKMP_ATTR_AF_MASK 0x8000 +#define ISAKMP_ATTR_AF_TV ISAKMP_ATTR_AF_MASK /* value in lv */ +#define ISAKMP_ATTR_AF_TLV 0 /* length in lv; value follows */ + +#define ISAKMP_ATTR_RTYPE_MASK 0x7FFF + +extern struct_desc + isakmp_oakley_attribute_desc, + isakmp_ipsec_attribute_desc; + +/* ISAKMP Security Association Payload + * layout from RFC 2408 "ISAKMP" section 3.4 + * A variable length Situation follows. + * Previous next payload: ISAKMP_NEXT_SA + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Domain of Interpretation (DOI) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Situation ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_sa +{ + u_int8_t isasa_np; /* Next payload */ + u_int8_t isasa_reserved; + u_int16_t isasa_length; /* Payload length */ + u_int32_t isasa_doi; /* DOI */ +}; + +extern struct_desc isakmp_sa_desc; + +extern struct_desc ipsec_sit_desc; + +/* ISAKMP Proposal Payload + * layout from RFC 2408 "ISAKMP" section 3.5 + * A variable length SPI follows. + * Previous next payload: ISAKMP_NEXT_P + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Proposal # ! Protocol-Id ! SPI Size !# of Transforms! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! SPI (variable) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_proposal +{ + u_int8_t isap_np; + u_int8_t isap_reserved; + u_int16_t isap_length; + u_int8_t isap_proposal; + u_int8_t isap_protoid; + u_int8_t isap_spisize; + u_int8_t isap_notrans; /* Number of transforms */ +}; + +extern struct_desc isakmp_proposal_desc; + +/* ISAKMP Transform Payload + * layout from RFC 2408 "ISAKMP" section 3.6 + * Variable length SA Attributes follow. + * Previous next payload: ISAKMP_NEXT_T + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Transform # ! Transform-Id ! RESERVED2 ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ SA Attributes ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_transform +{ + u_int8_t isat_np; + u_int8_t isat_reserved; + u_int16_t isat_length; + u_int8_t isat_transnum; /* Number of the transform */ + u_int8_t isat_transid; + u_int16_t isat_reserved2; +}; + +extern struct_desc + isakmp_isakmp_transform_desc, + isakmp_ah_transform_desc, + isakmp_esp_transform_desc, + isakmp_ipcomp_transform_desc; + +/* ISAKMP Key Exchange Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.7 + * Variable Key Exchange Data follow the generic fields. + * Previous next payload: ISAKMP_NEXT_KE + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Key Exchange Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +extern struct_desc isakmp_keyex_desc; + +/* ISAKMP Identification Payload + * layout from RFC 2408 "ISAKMP" section 3.8 + * See "struct identity" declared later. + * Variable length Identification Data follow. + * Previous next payload: ISAKMP_NEXT_ID + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ID Type ! DOI Specific ID Data ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Identification Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_id +{ + u_int8_t isaid_np; + u_int8_t isaid_reserved; + u_int16_t isaid_length; + u_int8_t isaid_idtype; + u_int8_t isaid_doi_specific_a; + u_int16_t isaid_doi_specific_b; +}; + +extern struct_desc isakmp_identification_desc; + +/* IPSEC Identification Payload Content + * layout from RFC 2407 "IPsec DOI" section 4.6.2 + * See struct isakmp_id declared earlier. + * Note: Hashing skips the ISAKMP generic payload header + * Variable length Identification Data follow. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ID Type ! Protocol ID ! Port ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ Identification Data ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_ipsec_id +{ + u_int8_t isaiid_np; + u_int8_t isaiid_reserved; + u_int16_t isaiid_length; + u_int8_t isaiid_idtype; + u_int8_t isaiid_protoid; + u_int16_t isaiid_port; +}; + +extern struct_desc isakmp_ipsec_identification_desc; + +/* ISAKMP Certificate Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.9 + * Variable length Certificate Data follow the generic fields. + * Previous next payload: ISAKMP_NEXT_CERT. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Cert Encoding ! ! + * +-+-+-+-+-+-+-+-+ ! + * ~ Certificate Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_cert +{ + u_int8_t isacert_np; + u_int8_t isacert_reserved; + u_int16_t isacert_length; + u_int8_t isacert_type; +}; + +/* NOTE: this packet type has a fixed portion that is not a + * multiple of 4 octets. This means that sizeof(struct isakmp_cert) + * yields the wrong value for the length. + */ +#define ISAKMP_CERT_SIZE 5 + +extern struct_desc isakmp_ipsec_certificate_desc; + +/* ISAKMP Certificate Request Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.10 + * Variable length Certificate Types and Certificate Authorities follow. + * Previous next payload: ISAKMP_NEXT_CR. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Cert. Type ! ! + * +-+-+-+-+-+-+-+-+ ! + * ~ Certificate Authority ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_cr +{ + u_int8_t isacr_np; + u_int8_t isacr_reserved; + u_int16_t isacr_length; + u_int8_t isacr_type; +}; + +/* NOTE: this packet type has a fixed portion that is not a + * multiple of 4 octets. This means that sizeof(struct isakmp_cr) + * yields the wrong value for the length. + */ +#define ISAKMP_CR_SIZE 5 + +extern struct_desc isakmp_ipsec_cert_req_desc; + +/* ISAKMP Hash Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.11 + * Variable length Hash Data follow. + * Previous next payload: ISAKMP_NEXT_HASH. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Hash Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +extern struct_desc isakmp_hash_desc; + +/* ISAKMP Signature Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.12 + * Variable length Signature Data follow. + * Previous next payload: ISAKMP_NEXT_SIG. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Signature Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +extern struct_desc isakmp_signature_desc; + +/* ISAKMP Nonce Payload: no fixed fields beyond the generic ones. + * layout from RFC 2408 "ISAKMP" section 3.13 + * Variable length Nonce Data follow. + * Previous next payload: ISAKMP_NEXT_NONCE. + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Nonce Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +extern struct_desc isakmp_nonce_desc; + +/* ISAKMP Notification Payload + * layout from RFC 2408 "ISAKMP" section 3.14 + * This is followed by a variable length SPI + * and then possibly by variable length Notification Data. + * Previous next payload: ISAKMP_NEXT_N + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Domain of Interpretation (DOI) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Protocol-ID ! SPI Size ! Notify Message Type ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Security Parameter Index (SPI) ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Notification Data ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_notification +{ + u_int8_t isan_np; + u_int8_t isan_reserved; + u_int16_t isan_length; + u_int32_t isan_doi; + u_int8_t isan_protoid; + u_int8_t isan_spisize; + u_int16_t isan_type; +}; + +extern struct_desc isakmp_notification_desc; + +/* ISAKMP Delete Payload + * layout from RFC 2408 "ISAKMP" section 3.15 + * This is followed by a variable length SPI. + * Previous next payload: ISAKMP_NEXT_D + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Domain of Interpretation (DOI) ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Protocol-Id ! SPI Size ! # of SPIs ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Security Parameter Index(es) (SPI) ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct isakmp_delete +{ + u_int8_t isad_np; + u_int8_t isad_reserved; + u_int16_t isad_length; + u_int32_t isad_doi; + u_int8_t isad_protoid; + u_int8_t isad_spisize; + u_int16_t isad_nospi; +}; + +extern struct_desc isakmp_delete_desc; + +/* From draft-dukes-ike-mode-cfg +3.2. Attribute Payload + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload ! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Type ! RESERVED ! Identifier ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ! ! + ~ Attributes ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +struct isakmp_mode_attr +{ + u_int8_t isama_np; + u_int8_t isama_reserved; + u_int16_t isama_length; + u_int8_t isama_type; + u_int8_t isama_reserved2; + u_int16_t isama_identifier; +}; + +extern struct_desc isakmp_attr_desc; +extern struct_desc isakmp_modecfg_attribute_desc; + +/* ISAKMP Vendor ID Payload + * layout from RFC 2408 "ISAKMP" section 3.15 + * This is followed by a variable length VID. + * Previous next payload: ISAKMP_NEXT_VID + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! Next Payload ! RESERVED ! Payload Length ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ! ! + * ~ Vendor ID (VID) ~ + * ! ! + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +extern struct_desc isakmp_vendor_id_desc; + +struct isakmp_nat_oa +{ + u_int8_t isanoa_np; + u_int8_t isanoa_reserved_1; + u_int16_t isanoa_length; + u_int8_t isanoa_idtype; + u_int8_t isanoa_reserved_2; + u_int16_t isanoa_reserved_3; +}; + +extern struct_desc isakmp_nat_d; +extern struct_desc isakmp_nat_oa; + +/* union of all payloads */ + +union payload { + struct isakmp_generic generic; + struct isakmp_sa sa; + struct isakmp_proposal proposal; + struct isakmp_transform transform; + struct isakmp_id id; /* Main Mode */ + struct isakmp_cert cert; + struct isakmp_cr cr; + struct isakmp_ipsec_id ipsec_id; /* Quick Mode */ + struct isakmp_notification notification; + struct isakmp_delete delete; + struct isakmp_nat_oa nat_oa; + struct isakmp_mode_attr attribute; +}; + +/* descriptor for each payload type + * + * There is a slight problem in that some payloads differ, depending + * on the mode. Since this is table only used for top-level payloads, + * Proposal and Transform payloads need not be handled. + * That leaves only Identification payloads as a problem. + * We make all these entries NULL + */ +extern struct_desc *const payload_descs[ISAKMP_NEXT_ROOF]; + +#endif /* _PACKET_H */ diff --git a/src/pluto/pem.c b/src/pluto/pem.c new file mode 100644 index 000000000..db6d0d7e3 --- /dev/null +++ b/src/pluto/pem.c @@ -0,0 +1,463 @@ +/* Loading of PEM encoded files with optional encryption + * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pem.c,v 1.4 2005/08/17 16:31:24 as Exp $ + */ + +/* decrypt a PEM encoded data block using DES-EDE3-CBC + * see RFC 1423 PEM: Algorithms, Modes and Identifiers + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#define HEADER_DES_LOCL_H /* stupid trick to force prototype decl in */ +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "md5.h" +#include "whack.h" +#include "pem.h" + +/* + * check the presence of a pattern in a character string + */ +static bool +present(const char* pattern, chunk_t* ch) +{ + u_int pattern_len = strlen(pattern); + + if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0) + { + ch->ptr += pattern_len; + ch->len -= pattern_len; + return TRUE; + } + return FALSE; +} + +/* + * compare string with chunk + */ +static bool +match(const char *pattern, const chunk_t *ch) +{ + return ch->len == strlen(pattern) && + strncmp(pattern, ch->ptr, ch->len) == 0; +} + +/* + * find a boundary of the form -----tag name----- + */ +static bool +find_boundary(const char* tag, chunk_t *line) +{ + chunk_t name = empty_chunk; + + if (!present("-----", line)) + return FALSE; + if (!present(tag, line)) + return FALSE; + if (*line->ptr != ' ') + return FALSE; + line->ptr++; line->len--; + + /* extract name */ + name.ptr = line->ptr; + while (line->len > 0) + { + if (present("-----", line)) + { + DBG(DBG_PARSING, + DBG_log(" -----%s %.*s-----", + tag, (int)name.len, name.ptr); + ) + return TRUE; + } + line->ptr++; line->len--; name.len++; + } + return FALSE; +} + +/* + * eat whitespace + */ +static void +eat_whitespace(chunk_t *src) +{ + while (src->len > 0 && (*src->ptr == ' ' || *src->ptr == '\t')) + { + src->ptr++; src->len--; + } +} + +/* + * extracts a token ending with a given termination symbol + */ +static bool +extract_token(chunk_t *token, char termination, chunk_t *src) +{ + u_char *eot = memchr(src->ptr, termination, src->len); + + /* initialize empty token */ + *token = empty_chunk; + + if (eot == NULL) /* termination symbol not found */ + return FALSE; + + /* extract token */ + token->ptr = src->ptr; + token->len = (u_int)(eot - src->ptr); + + /* advance src pointer after termination symbol */ + src->ptr = eot + 1; + src->len -= (token->len + 1); + + return TRUE; +} + +/* + * extracts a name: value pair from the PEM header + */ +static bool +extract_parameter(chunk_t *name, chunk_t *value, chunk_t *line) +{ + DBG(DBG_PARSING, + DBG_log(" %.*s", (int)line->len, line->ptr); + ) + + /* extract name */ + if (!extract_token(name,':', line)) + return FALSE; + + eat_whitespace(line); + + /* extract value */ + *value = *line; + return TRUE; +} + +/* + * fetches a new line terminated by \n or \r\n + */ +static bool +fetchline(chunk_t *src, chunk_t *line) +{ + if (src->len == 0) /* end of src reached */ + return FALSE; + + if (extract_token(line, '\n', src)) + { + if (line->len > 0 && *(line->ptr + line->len -1) == '\r') + line->len--; /* remove optional \r */ + } + else /*last line ends without newline */ + { + *line = *src; + src->ptr += src->len; + src->len = 0; + } + return TRUE; +} + +/* + * decrypts a DES-EDE-CBC encrypted data block + */ +static bool +pem_decrypt_3des(chunk_t *blob, chunk_t *iv, const char *passphrase) +{ + MD5_CTX context; + u_char digest[MD5_DIGEST_SIZE]; + u_char des_iv[DES_CBC_BLOCK_SIZE]; + u_char key[24]; + des_cblock *deskey = (des_cblock *)key; + des_key_schedule ks[3]; + u_char padding, *last_padding_pos, *first_padding_pos; + + /* Convert passphrase to 3des key */ + MD5Init(&context); + MD5Update(&context, passphrase, strlen(passphrase)); + MD5Update(&context, iv->ptr, iv->len); + MD5Final(digest, &context); + + memcpy(key, digest, MD5_DIGEST_SIZE); + + MD5Init(&context); + MD5Update(&context, digest, MD5_DIGEST_SIZE); + MD5Update(&context, passphrase, strlen(passphrase)); + MD5Update(&context, iv->ptr, iv->len); + MD5Final(digest, &context); + + memcpy(key + MD5_DIGEST_SIZE, digest, 24 - MD5_DIGEST_SIZE); + + (void) des_set_key(&deskey[0], ks[0]); + (void) des_set_key(&deskey[1], ks[1]); + (void) des_set_key(&deskey[2], ks[2]); + + /* decrypt data block */ + memcpy(des_iv, iv->ptr, DES_CBC_BLOCK_SIZE); + des_ede3_cbc_encrypt((des_cblock *)blob->ptr, (des_cblock *)blob->ptr, + blob->len, ks[0], ks[1], ks[2], (des_cblock *)des_iv, FALSE); + + /* determine amount of padding */ + last_padding_pos = blob->ptr + blob->len - 1; + padding = *last_padding_pos; + first_padding_pos = (padding > blob->len)? + blob->ptr : last_padding_pos - padding; + + /* check the padding pattern */ + while (--last_padding_pos > first_padding_pos) + { + if (*last_padding_pos != padding) + return FALSE; + } + + /* remove padding */ + blob->len -= padding; + return TRUE; +} + +/* + * optionally prompts for a passphrase before decryption + * currently we support DES-EDE3-CBC, only + */ +static err_t +pem_decrypt(chunk_t *blob, chunk_t *iv, prompt_pass_t *pass, const char* label) +{ + DBG(DBG_CRYPT, + DBG_log(" decrypting file using 'DES-EDE3-CBC'"); + ) + if (iv->len != DES_CBC_BLOCK_SIZE) + return "size of DES-EDE3-CBC IV is not 8 bytes"; + + if (pass == NULL) + return "no passphrase available"; + + /* do we prompt for the passphrase? */ + if (pass->prompt && pass->fd != NULL_FD) + { + int i; + chunk_t blob_copy; + err_t ugh = "invalid passphrase, too many trials"; + + whack_log(RC_ENTERSECRET, "need passphrase for '%s'", label); + + for (i = 0; i < MAX_PROMPT_PASS_TRIALS; i++) + { + int n; + + if (i > 0) + whack_log(RC_ENTERSECRET, "invalid passphrase, please try again"); + + n = read(pass->fd, pass->secret, PROMPT_PASS_LEN); + + if (n == -1) + { + err_t ugh = "read(whackfd) failed"; + + whack_log(RC_LOG_SERIOUS,ugh); + return ugh; + } + + pass->secret[n-1] = '\0'; + + if (strlen(pass->secret) == 0) + { + err_t ugh = "no passphrase entered, aborted"; + + whack_log(RC_LOG_SERIOUS, ugh); + return ugh; + } + + clonetochunk(blob_copy, blob->ptr, blob->len, "blob copy"); + + if (pem_decrypt_3des(blob, iv, pass->secret)) + { + whack_log(RC_SUCCESS, "valid passphrase"); + pfree(blob_copy.ptr); + return NULL; + } + + /* blob is useless after wrong decryption, restore the original */ + pfree(blob->ptr); + *blob = blob_copy; + } + whack_log(RC_LOG_SERIOUS, ugh); + return ugh; + } + else + { + if (pem_decrypt_3des(blob, iv, pass->secret)) + return NULL; + else + return "invalid passphrase"; + } +} + +/* Converts a PEM encoded file into its binary form + * + * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 + * RFC 934 Message Encapsulation, January 1985 + */ +err_t +pemtobin(chunk_t *blob, prompt_pass_t *pass, const char* label, bool *pgp) +{ + typedef enum { + PEM_PRE = 0, + PEM_MSG = 1, + PEM_HEADER = 2, + PEM_BODY = 3, + PEM_POST = 4, + PEM_ABORT = 5 + } state_t; + + bool encrypted = FALSE; + + state_t state = PEM_PRE; + + chunk_t src = *blob; + chunk_t dst = *blob; + chunk_t line = empty_chunk; + chunk_t iv = empty_chunk; + + u_char iv_buf[MAX_DIGEST_LEN]; + + /* zero size of converted blob */ + dst.len = 0; + + /* zero size of IV */ + iv.ptr = iv_buf; + iv.len = 0; + + while (fetchline(&src, &line)) + { + if (state == PEM_PRE) + { + if (find_boundary("BEGIN", &line)) + { + *pgp = FALSE; + state = PEM_MSG; + } + continue; + } + else + { + if (find_boundary("END", &line)) + { + state = PEM_POST; + break; + } + if (state == PEM_MSG) + { + state = (memchr(line.ptr, ':', line.len) == NULL)? + PEM_BODY : PEM_HEADER; + } + if (state == PEM_HEADER) + { + chunk_t name = empty_chunk; + chunk_t value = empty_chunk; + + /* an empty line separates HEADER and BODY */ + if (line.len == 0) + { + state = PEM_BODY; + continue; + } + + /* we are looking for a name: value pair */ + if (!extract_parameter(&name, &value, &line)) + continue; + + if (match("Proc-Type", &name) && *value.ptr == '4') + encrypted = TRUE; + else if (match("DEK-Info", &name)) + { + const char *ugh = NULL; + size_t len = 0; + chunk_t dek; + + if (!extract_token(&dek, ',', &value)) + dek = value; + + /* we support DES-EDE3-CBC encrypted files, only */ + if (!match("DES-EDE3-CBC", &dek)) + return "we support DES-EDE3-CBC encrypted files, only"; + + eat_whitespace(&value); + ugh = ttodata(value.ptr, value.len, 16, + iv.ptr, MAX_DIGEST_LEN, &len); + if (ugh) + return "error in IV"; + + iv.len = len; + } + } + else /* state is PEM_BODY */ + { + const char *ugh = NULL; + size_t len = 0; + chunk_t data; + + /* remove any trailing whitespace */ + if (!extract_token(&data ,' ', &line)) + data = line; + + /* check for PGP armor checksum */ + if (*data.ptr == '=') + { + *pgp = TRUE; + data.ptr++; + data.len--; + DBG(DBG_PARSING, + DBG_log(" Armor checksum: %.*s", (int)data.len, data.ptr); + ) + continue; + } + + ugh = ttodata(data.ptr, data.len, 64, + dst.ptr, blob->len - dst.len, &len); + if (ugh) + { + DBG(DBG_PARSING, + DBG_log(" %s", ugh); + ) + state = PEM_ABORT; + break; + } + else + { + dst.ptr += len; + dst.len += len; + } + } + } + } + /* set length to size of binary blob */ + blob->len = dst.len; + + if (state != PEM_POST) + return "file coded in unknown format, discarded"; + + if (encrypted) + return pem_decrypt(blob, &iv, pass, label); + else + return NULL; +} diff --git a/src/pluto/pem.h b/src/pluto/pem.h new file mode 100644 index 000000000..815b5d85b --- /dev/null +++ b/src/pluto/pem.h @@ -0,0 +1,18 @@ +/* Loading of PEM encoded files with optional encryption + * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pem.h,v 1.1 2004/03/15 20:35:28 as Exp $ + */ + +extern err_t pemtobin(chunk_t *blob, prompt_pass_t *pass, const char* label + , bool *pgp); diff --git a/src/pluto/pgp.c b/src/pluto/pgp.c new file mode 100644 index 000000000..307303f6b --- /dev/null +++ b/src/pluto/pgp.c @@ -0,0 +1,647 @@ +/* Support of OpenPGP certificates + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pgp.c,v 1.7 2006/01/04 21:00:43 as Exp $ + */ + +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "log.h" +#include "id.h" +#include "pgp.h" +#include "certs.h" +#include "md5.h" +#include "whack.h" +#include "pkcs1.h" +#include "keys.h" + +/* + * chained list of OpenPGP end certificates + */ +static pgpcert_t *pgpcerts = NULL; + +/* + * OpenPGP packet tags defined in section 4.3 of RFC 2440 + */ +#define PGP_PKT_RESERVED 0 +#define PGP_PKT_PUBKEY_ENC_SESSION_KEY 1 +#define PGP_PKT_SIGNATURE 2 +#define PGP_PKT_SYMKEY_ENC_SESSION_KEY 3 +#define PGP_PKT_ONE_PASS_SIGNATURE_PKT 4 +#define PGP_PKT_SECRET_KEY 5 +#define PGP_PKT_PUBLIC_KEY 6 +#define PGP_PKT_SECRET_SUBKEY 7 +#define PGP_PKT_COMPRESSED_DATA 8 +#define PGP_PKT_SYMKEY_ENC_DATA 9 +#define PGP_PKT_MARKER 10 +#define PGP_PKT_LITERAL_DATA 11 +#define PGP_PKT_TRUST 12 +#define PGP_PKT_USER_ID 13 +#define PGP_PKT_PUBLIC_SUBKEY 14 +#define PGP_PKT_ROOF 15 + +static const char *const pgp_packet_type_name[] = { + "Reserved", + "Public-Key Encrypted Session Key Packet", + "Signature Packet", + "Symmetric-Key Encrypted Session Key Packet", + "One-Pass Signature Packet", + "Secret Key Packet", + "Public Key Packet", + "Secret Subkey Packet", + "Compressed Data Packet", + "Symmetrically Encrypted Data Packet", + "Marker Packet", + "Literal Data Packet", + "Trust Packet", + "User ID Packet", + "Public Subkey Packet" +}; + +/* + * OpenPGP public key algorithms defined in section 9.1 of RFC 2440 + */ +#define PGP_PUBKEY_ALG_RSA 1 +#define PGP_PUBKEY_ALG_RSA_ENC_ONLY 2 +#define PGP_PUBKEY_ALG_RSA_SIGN_ONLY 3 +#define PGP_PUBKEY_ALG_ELGAMAL_ENC_ONLY 16 +#define PGP_PUBKEY_ALG_DSA 17 +#define PGP_PUBKEY_ALG_ECC 18 +#define PGP_PUBKEY_ALG_ECDSA 19 +#define PGP_PUBKEY_ALG_ELGAMAL 20 + +/* + * OpenPGP symmetric key algorithms defined in section 9.2 of RFC 2440 + */ +#define PGP_SYM_ALG_PLAIN 0 +#define PGP_SYM_ALG_IDEA 1 +#define PGP_SYM_ALG_3DES 2 +#define PGP_SYM_ALG_CAST5 3 +#define PGP_SYM_ALG_BLOWFISH 4 +#define PGP_SYM_ALG_SAFER 5 +#define PGP_SYM_ALG_DES 6 +#define PGP_SYM_ALG_AES 7 +#define PGP_SYM_ALG_AES_192 8 +#define PGP_SYM_ALG_AES_256 9 +#define PGP_SYM_ALG_TWOFISH 10 +#define PGP_SYM_ALG_ROOF 11 + +static const char *const pgp_sym_alg_name[] = { + "Plaintext", + "IDEA", + "3DES", + "CAST5", + "Blowfish", + "SAFER", + "DES", + "AES", + "AES-192", + "AES-256", + "Twofish" +}; + +/* + * Size of PGP Key ID + */ +#define PGP_KEYID_SIZE 8 + +const pgpcert_t empty_pgpcert = { + NULL , /* *next */ + 0 , /* installed */ + 0 , /* count */ + { NULL, 0 }, /* certificate */ + 0 , /* created */ + 0 , /* until */ + 0 , /* pubkeyAlgorithm */ + { NULL, 0 }, /* modulus */ + { NULL, 0 }, /* publicExponent */ + "" /* fingerprint */ +}; + +static size_t +pgp_size(chunk_t *blob, int len) +{ + size_t size = 0; + + blob->len -= len; + while (len-- > 0) + size = 256*size + *blob->ptr++; + return size; +} + +/* + * extracts the length of a PGP packet + */ +static size_t +pgp_old_packet_length(chunk_t *blob) +{ + /* bits 0 and 1 define the packet length type */ + int len_type = 0x03 & *blob->ptr++; + + blob->len--; + + /* len_type: 0 -> 1 byte, 1 -> 2 bytes, 2 -> 4 bytes */ + return pgp_size(blob, (len_type == 0)? 1: len_type << 1); +} + +/* + * extracts PGP packet version (V3 or V4) + */ +static u_char +pgp_version(chunk_t *blob) +{ + u_char version = *blob->ptr++; + blob->len--; + DBG(DBG_PARSING, + DBG_log("L3 - version:"); + DBG_log(" V%d", version) + ) + return version; +} + +/* + * Parse OpenPGP public key packet defined in section 5.5.2 of RFC 2440 + */ +static bool +parse_pgp_pubkey_packet(chunk_t *packet, pgpcert_t *cert) +{ + u_char version = pgp_version(packet); + + if (version < 3 || version > 4) + { + plog("PGP packet version V%d not supported", version); + return FALSE; + } + + /* creation date - 4 bytes */ + cert->created = (time_t)pgp_size(packet, 4); + DBG(DBG_PARSING, + DBG_log("L3 - created:"); + DBG_log(" %s", timetoa(&cert->created, TRUE)) + ) + + if (version == 3) + { + /* validity in days - 2 bytes */ + cert->until = (time_t)pgp_size(packet, 2); + + /* validity of 0 days means that the key never expires */ + if (cert->until > 0) + cert->until = cert->created + 24*3600*cert->until; + + DBG(DBG_PARSING, + DBG_log("L3 - until:"); + DBG_log(" %s", timetoa(&cert->until, TRUE)); + ) + } + + /* public key algorithm - 1 byte */ + DBG(DBG_PARSING, + DBG_log("L3 - public key algorithm:") + ) + + switch (pgp_size(packet, 1)) + { + case PGP_PUBKEY_ALG_RSA: + case PGP_PUBKEY_ALG_RSA_SIGN_ONLY: + cert->pubkeyAlg = PUBKEY_ALG_RSA; + DBG(DBG_PARSING, + DBG_log(" RSA") + ) + /* modulus n */ + cert->modulus.len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE; + cert->modulus.ptr = packet->ptr; + packet->ptr += cert->modulus.len; + packet->len -= cert->modulus.len; + DBG(DBG_PARSING, + DBG_log("L3 - modulus:") + ) + DBG_cond_dump_chunk(DBG_RAW, "", cert->modulus); + + /* public exponent e */ + cert->publicExponent.len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE; + cert->publicExponent.ptr = packet->ptr; + packet->ptr += cert->publicExponent.len; + packet->len -= cert->publicExponent.len; + DBG(DBG_PARSING, + DBG_log("L3 - public exponent:") + ) + DBG_cond_dump_chunk(DBG_RAW, "", cert->publicExponent); + + if (version == 3) + { + /* a V3 fingerprint is the MD5 hash of modulus and public exponent */ + MD5_CTX context; + MD5Init(&context); + MD5Update(&context, cert->modulus.ptr, cert->modulus.len); + MD5Update(&context, cert->publicExponent.ptr, cert->publicExponent.len); + MD5Final(cert->fingerprint, &context); + } + else + { + plog(" computation of V4 key ID not implemented yet"); + } + break; + case PGP_PUBKEY_ALG_DSA: + cert->pubkeyAlg = PUBKEY_ALG_DSA; + DBG(DBG_PARSING, + DBG_log(" DSA") + ) + plog(" DSA public keys not supported"); + return FALSE; + default: + cert->pubkeyAlg = 0; + DBG(DBG_PARSING, + DBG_log(" other") + ) + plog(" exotic not RSA public keys not supported"); + return FALSE; + } + return TRUE; +} + +/* + * Parse OpenPGP secret key packet defined in section 5.5.3 of RFC 2440 + */ +static bool +parse_pgp_secretkey_packet(chunk_t *packet, RSA_private_key_t *key) +{ + int i, s2k; + pgpcert_t cert = empty_pgpcert; + + if (!parse_pgp_pubkey_packet(packet, &cert)) + return FALSE; + + init_RSA_public_key((RSA_public_key_t *)key, cert.publicExponent + , cert.modulus); + + /* string-to-key usage */ + s2k = pgp_size(packet, 1); + + DBG(DBG_PARSING, + DBG_log("L3 - string-to-key: %d", s2k) + ) + + if (s2k == 255) + { + plog(" string-to-key specifiers not supported"); + return FALSE; + } + + if (s2k >= PGP_SYM_ALG_ROOF) + { + plog(" undefined symmetric key algorithm"); + return FALSE; + } + + /* a known symmetric key algorithm is specified*/ + DBG(DBG_PARSING, + DBG_log(" %s", pgp_sym_alg_name[s2k]) + ) + + /* private key is unencrypted */ + if (s2k == PGP_SYM_ALG_PLAIN) + { + for (i = 2; i < RSA_PRIVATE_FIELD_ELEMENTS; i++) + { + mpz_t u; /* auxiliary variable */ + + /* compute offset to private key component i*/ + MP_INT *n = (MP_INT*)((char *)key + RSA_private_field[i].offset); + + switch (i) + { + case 2: + case 3: + case 4: + { + size_t len = (pgp_size(packet, 2)+7) / BITS_PER_BYTE; + + n_to_mpz(n, packet->ptr, len); + DBG(DBG_PARSING, + DBG_log("L3 - %s:", RSA_private_field[i].name) + ) + DBG_cond_dump(DBG_PRIVATE, "", packet->ptr, len); + packet->ptr += len; + packet->len -= len; + } + break; + case 5: /* dP = d mod (p-1) */ + mpz_init(u); + mpz_sub_ui(u, &key->p, 1); + mpz_mod(n, &key->d, u); + mpz_clear(u); + break; + case 6: /* dQ = d mod (q-1) */ + mpz_init(u); + mpz_sub_ui(u, &key->q, 1); + mpz_mod(n, &key->d, u); + mpz_clear(u); + break; + case 7: /* qInv = (q^-1) mod p */ + mpz_invert(n, &key->q, &key->p); + if (mpz_cmp_ui(n, 0) < 0) + mpz_add(n, n, &key->p); + passert(mpz_cmp(n, &key->p) < 0); + break; + } + } + return TRUE; + } + + plog(" %s encryption not supported", pgp_sym_alg_name[s2k]); + return FALSE; +} + +/* + * Parse OpenPGP signature packet defined in section 5.2.2 of RFC 2440 + */ +static bool +parse_pgp_signature_packet(chunk_t *packet, pgpcert_t *cert) +{ + time_t created; + chunk_t keyid; + u_char sig_type; + u_char version = pgp_version(packet); + + /* we parse only V3 signature packets */ + if (version != 3) + return TRUE; + + /* size byte must have the value 5 */ + if (pgp_size(packet, 1) != 5) + { + plog(" size must be 5"); + return FALSE; + } + + /* signature type - 1 byte */ + sig_type = (u_char)pgp_size(packet, 1); + DBG(DBG_PARSING, + DBG_log("L3 - signature type: 0x%2x", sig_type) + ) + + /* creation date - 4 bytes */ + created = (time_t)pgp_size(packet, 4); + DBG(DBG_PARSING, + DBG_log("L3 - created:"); + DBG_log(" %s", timetoa(&cert->created, TRUE)) + ) + + /* key ID of signer - 8 bytes */ + keyid.ptr = packet->ptr; + keyid.len = PGP_KEYID_SIZE; + DBG_cond_dump_chunk(DBG_PARSING, "L3 - key ID of signer", keyid); + + return TRUE; +} + +bool +parse_pgp(chunk_t blob, pgpcert_t *cert, RSA_private_key_t *key) +{ + DBG(DBG_PARSING, + DBG_log("L0 - PGP file:") + ) + DBG_cond_dump_chunk(DBG_RAW, "", blob); + + if (cert != NULL) + { + /* parse a PGP certificate file */ + cert->certificate = blob; + time(&cert->installed); + } + else if (key == NULL) + { + /* should not occur, nothing to parse */ + return FALSE; + } + + while (blob.len > 0) + { + chunk_t packet = empty_chunk; + u_char packet_tag = *blob.ptr; + + DBG(DBG_PARSING, + DBG_log("L1 - PGP packet: tag= 0x%2x", packet_tag) + ) + + /* bit 7 must be set */ + if (!(packet_tag & 0x80)) + { + plog(" incorrect Packet Tag"); + return FALSE; + } + + /* bit 6 set defines new packet format */ + if (packet_tag & 0x40) + { + plog(" new PGP packet format not supported"); + return FALSE; + } + else + { + int packet_type = (packet_tag & 0x3C) >> 2; + + packet.len = pgp_old_packet_length(&blob); + packet.ptr = blob.ptr; + blob.ptr += packet.len; + blob.len -= packet.len; + DBG(DBG_PARSING, + DBG_log(" %s (%d), old format, %d bytes", + (packet_type < PGP_PKT_ROOF) ? + pgp_packet_type_name[packet_type] : + "Undefined Packet Type", packet_type, (int)packet.len); + DBG_log("L2 - body:") + ) + DBG_cond_dump_chunk(DBG_RAW, "", packet); + + if (cert != NULL) + { + /* parse a PGP certificate */ + switch (packet_type) + { + case PGP_PKT_PUBLIC_KEY: + if (!parse_pgp_pubkey_packet(&packet, cert)) + return FALSE; + break; + case PGP_PKT_SIGNATURE: + if (!parse_pgp_signature_packet(&packet, cert)) + return FALSE; + break; + case PGP_PKT_USER_ID: + DBG(DBG_PARSING, + DBG_log("L3 - user ID:"); + DBG_log(" '%.*s'", (int)packet.len, packet.ptr) + ) + break; + default: + break; + } + } + else + { + /* parse a PGP private key file */ + switch (packet_type) + { + case PGP_PKT_SECRET_KEY: + if (!parse_pgp_secretkey_packet(&packet, key)) + return FALSE; + break; + default: + break; + } + } + } + } + return TRUE; +} + +/* + * compare two OpenPGP certificates + */ +static bool +same_pgpcert(pgpcert_t *a, pgpcert_t *b) +{ + return a->certificate.len == b->certificate.len && + memcmp(a->certificate.ptr, b->certificate.ptr, b->certificate.len) == 0; +} + +/* + * for each link pointing to the certificate increase the count by one + */ +void +share_pgpcert(pgpcert_t *cert) +{ + if (cert != NULL) + cert->count++; +} + +/* + * select the OpenPGP keyid as ID + */ +void +select_pgpcert_id(pgpcert_t *cert, struct id *end_id) +{ + end_id->kind = ID_KEY_ID; + end_id->name.len = PGP_FINGERPRINT_SIZE; + end_id->name.ptr = cert->fingerprint; + end_id->name.ptr = temporary_cyclic_buffer(); + memcpy(end_id->name.ptr, cert->fingerprint, PGP_FINGERPRINT_SIZE); +} + +/* + * add an OpenPGP user/host certificate to the chained list + */ +pgpcert_t* +add_pgpcert(pgpcert_t *cert) +{ + pgpcert_t *c = pgpcerts; + + while (c != NULL) + { + if (same_pgpcert(c, cert)) /* already in chain, free cert */ + { + free_pgpcert(cert); + return c; + } + c = c->next; + } + + /* insert new cert at the root of the chain */ + cert->next = pgpcerts; + pgpcerts = cert; + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log(" pgp cert inserted") + ) + return cert; +} + +/* release of a certificate decreases the count by one + " the certificate is freed when the counter reaches zero + */ +void +release_pgpcert(pgpcert_t *cert) +{ + if (cert != NULL && --cert->count == 0) + { + pgpcert_t **pp = &pgpcerts; + while (*pp != cert) + pp = &(*pp)->next; + *pp = cert->next; + free_pgpcert(cert); + } +} + +/* + * free a PGP certificate + */ +void +free_pgpcert(pgpcert_t *cert) +{ + if (cert != NULL) + { + if (cert->certificate.ptr != NULL) + pfree(cert->certificate.ptr); + pfree(cert); + } +} + +/* + * list all PGP end certificates in a chained list + */ +void +list_pgp_end_certs(bool utc) +{ + pgpcert_t *cert = pgpcerts; + time_t now; + + /* determine the current time */ + time(&now); + + if (cert != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of PGP End certificates:"); + whack_log(RC_COMMENT, " "); + } + + while (cert != NULL) + { + unsigned keysize; + char buf[BUF_LEN]; + cert_t c; + + c.type = CERT_PGP; + c.u.pgp = cert; + + whack_log(RC_COMMENT, "%s, count: %d", timetoa(&cert->installed, utc), cert->count); + datatot(cert->fingerprint, PGP_FINGERPRINT_SIZE, 'x', buf, BUF_LEN); + whack_log(RC_COMMENT, " fingerprint: %s", buf); + form_keyid(cert->publicExponent, cert->modulus, buf, &keysize); + whack_log(RC_COMMENT, " pubkey: %4d RSA Key %s%s", 8*keysize, buf, + (has_private_key(c))? ", has private key" : ""); + whack_log(RC_COMMENT, " created: %s", timetoa(&cert->created, utc)); + whack_log(RC_COMMENT, " until: %s %s", timetoa(&cert->until, utc), + check_expiry(cert->until, CA_CERT_WARNING_INTERVAL, TRUE)); + cert = cert->next; + } +} + diff --git a/src/pluto/pgp.h b/src/pluto/pgp.h new file mode 100644 index 000000000..4f34debc9 --- /dev/null +++ b/src/pluto/pgp.h @@ -0,0 +1,54 @@ +/* Support of OpenPGP certificates + * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pgp.h,v 1.3 2005/08/07 07:50:09 as Exp $ + */ + +#ifndef _PGP_H +#define _PGP_H + +#include "pkcs1.h" +/* + * Length of PGP V3 fingerprint + */ +#define PGP_FINGERPRINT_SIZE MD5_DIGEST_SIZE + +typedef char fingerprint_t[PGP_FINGERPRINT_SIZE]; + +/* access structure for an OpenPGP certificate */ + +typedef struct pgpcert pgpcert_t; + +struct pgpcert { + pgpcert_t *next; + time_t installed; + int count; + chunk_t certificate; + time_t created; + time_t until; + enum pubkey_alg pubkeyAlg; + chunk_t modulus; + chunk_t publicExponent; + fingerprint_t fingerprint; +}; + +extern const pgpcert_t empty_pgpcert; +extern bool parse_pgp(chunk_t blob, pgpcert_t *cert, RSA_private_key_t *key); +extern void share_pgpcert(pgpcert_t *cert); +extern void select_pgpcert_id(pgpcert_t *cert, struct id *end_id); +extern pgpcert_t* add_pgpcert(pgpcert_t *cert); +extern void list_pgp_end_certs(bool utc); +extern void release_pgpcert(pgpcert_t *cert); +extern void free_pgpcert(pgpcert_t *cert); + +#endif /* _PGP_H */ diff --git a/src/pluto/pkcs1.c b/src/pluto/pkcs1.c new file mode 100644 index 000000000..ade5fdd94 --- /dev/null +++ b/src/pluto/pkcs1.c @@ -0,0 +1,674 @@ +/* Support of PKCS#1 private key data structures + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2005 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pkcs1.c,v 1.17 2006/01/04 21:00:43 as Exp $ + */ + +#include +#include +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "asn1.h" +#include "oid.h" +#include "log.h" +#include "pkcs1.h" +#include "md2.h" +#include "md5.h" +#include "sha1.h" +#include "rnd.h" + +const struct fld RSA_private_field[] = +{ + { "Modulus", offsetof(RSA_private_key_t, pub.n) }, + { "PublicExponent", offsetof(RSA_private_key_t, pub.e) }, + + { "PrivateExponent", offsetof(RSA_private_key_t, d) }, + { "Prime1", offsetof(RSA_private_key_t, p) }, + { "Prime2", offsetof(RSA_private_key_t, q) }, + { "Exponent1", offsetof(RSA_private_key_t, dP) }, + { "Exponent2", offsetof(RSA_private_key_t, dQ) }, + { "Coefficient", offsetof(RSA_private_key_t, qInv) }, +}; + +/* ASN.1 definition of a PKCS#1 RSA private key */ + +static const asn1Object_t privkeyObjects[] = { + { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */ + { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */ + { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */ + { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 10 */ + { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ + { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */ + { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */ + { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END } /* 15 */ +}; + +#define PKCS1_PRIV_KEY_VERSION 1 +#define PKCS1_PRIV_KEY_MODULUS 2 +#define PKCS1_PRIV_KEY_PUB_EXP 3 +#define PKCS1_PRIV_KEY_COEFF 9 +#define PKCS1_PRIV_KEY_ROOF 16 + + +/* + * forms the FreeS/WAN keyid from the public exponent e and modulus n + */ +void +form_keyid(chunk_t e, chunk_t n, char* keyid, unsigned *keysize) +{ + /* eliminate leading zero bytes in modulus from ASN.1 coding */ + while (n.len > 1 && *n.ptr == 0x00) + { + n.ptr++; n.len--; + } + + /* form the FreeS/WAN keyid */ + keyid[0] = '\0'; /* in case of splitkeytoid failure */ + splitkeytoid(e.ptr, e.len, n.ptr, n.len, keyid, KEYID_BUF); + + /* return the RSA modulus size in octets */ + *keysize = n.len; +} + +/* + * initialize an RSA_public_key_t object + */ +void +init_RSA_public_key(RSA_public_key_t *rsa, chunk_t e, chunk_t n) +{ + n_to_mpz(&rsa->e, e.ptr, e.len); + n_to_mpz(&rsa->n, n.ptr, n.len); + + form_keyid(e, n, rsa->keyid, &rsa->k); +} + +#ifdef DEBUG +static void +RSA_show_key_fields(RSA_private_key_t *k, int fieldcnt) +{ + const struct fld *p; + + DBG_log(" keyid: *%s", k->pub.keyid); + + for (p = RSA_private_field; p < &RSA_private_field[fieldcnt]; p++) + { + MP_INT *n = (MP_INT *) ((char *)k + p->offset); + size_t sz = mpz_sizeinbase(n, 16); + char buf[RSA_MAX_OCTETS * 2 + 2]; /* ought to be big enough */ + + passert(sz <= sizeof(buf)); + mpz_get_str(buf, 16, n); + + DBG_log(" %s: 0x%s", p->name, buf); + } +} + +/* debugging info that compromises security! */ +void +RSA_show_private_key(RSA_private_key_t *k) +{ + RSA_show_key_fields(k, elemsof(RSA_private_field)); +} + +void +RSA_show_public_key(RSA_public_key_t *k) +{ + /* Kludge: pretend that it is a private key, but only display the + * first two fields (which are the public key). + */ + passert(offsetof(RSA_private_key_t, pub) == 0); + RSA_show_key_fields((RSA_private_key_t *)k, 2); +} +#endif + +err_t +RSA_private_key_sanity(RSA_private_key_t *k) +{ + /* note that the *last* error found is reported */ + err_t ugh = NULL; + mpz_t t, u, q1; + +#ifdef DEBUG /* debugging info that compromises security */ + DBG(DBG_PRIVATE, RSA_show_private_key(k)); +#endif + + /* PKCS#1 1.5 section 6 requires modulus to have at least 12 octets. + * We actually require more (for security). + */ + if (k->pub.k < RSA_MIN_OCTETS) + return RSA_MIN_OCTETS_UGH; + + /* we picked a max modulus size to simplify buffer allocation */ + if (k->pub.k > RSA_MAX_OCTETS) + return RSA_MAX_OCTETS_UGH; + + mpz_init(t); + mpz_init(u); + mpz_init(q1); + + /* check that n == p * q */ + mpz_mul(u, &k->p, &k->q); + if (mpz_cmp(u, &k->pub.n) != 0) + ugh = "n != p * q"; + + /* check that e divides neither p-1 nor q-1 */ + mpz_sub_ui(t, &k->p, 1); + mpz_mod(t, t, &k->pub.e); + if (mpz_cmp_ui(t, 0) == 0) + ugh = "e divides p-1"; + + mpz_sub_ui(t, &k->q, 1); + mpz_mod(t, t, &k->pub.e); + if (mpz_cmp_ui(t, 0) == 0) + ugh = "e divides q-1"; + + /* check that d is e^-1 (mod lcm(p-1, q-1)) */ + /* see PKCS#1v2, aka RFC 2437, for the "lcm" */ + mpz_sub_ui(q1, &k->q, 1); + mpz_sub_ui(u, &k->p, 1); + mpz_gcd(t, u, q1); /* t := gcd(p-1, q-1) */ + mpz_mul(u, u, q1); /* u := (p-1) * (q-1) */ + mpz_divexact(u, u, t); /* u := lcm(p-1, q-1) */ + + mpz_mul(t, &k->d, &k->pub.e); + mpz_mod(t, t, u); + if (mpz_cmp_ui(t, 1) != 0) + ugh = "(d * e) mod (lcm(p-1, q-1)) != 1"; + + /* check that dP is d mod (p-1) */ + mpz_sub_ui(u, &k->p, 1); + mpz_mod(t, &k->d, u); + if (mpz_cmp(t, &k->dP) != 0) + ugh = "dP is not congruent to d mod (p-1)"; + + /* check that dQ is d mod (q-1) */ + mpz_sub_ui(u, &k->q, 1); + mpz_mod(t, &k->d, u); + if (mpz_cmp(t, &k->dQ) != 0) + ugh = "dQ is not congruent to d mod (q-1)"; + + /* check that qInv is (q^-1) mod p */ + mpz_mul(t, &k->qInv, &k->q); + mpz_mod(t, t, &k->p); + if (mpz_cmp_ui(t, 1) != 0) + ugh = "qInv is not conguent ot (q^-1) mod p"; + + mpz_clear(t); + mpz_clear(u); + mpz_clear(q1); + return ugh; +} + +/* + * Check the equality of two RSA public keys + */ +bool +same_RSA_public_key(const RSA_public_key_t *a, const RSA_public_key_t *b) +{ + return a == b + || (a->k == b->k && mpz_cmp(&a->n, &b->n) == 0 && mpz_cmp(&a->e, &b->e) == 0); +} + +/* + * Parses a PKCS#1 private key + */ +bool +pkcs1_parse_private_key(chunk_t blob, RSA_private_key_t *key) +{ + err_t ugh = NULL; + asn1_ctx_t ctx; + chunk_t object, modulus, exp; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, 0, FALSE, DBG_PRIVATE); + + while (objectID < PKCS1_PRIV_KEY_ROOF) { + + if (!extract_object(privkeyObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + if (objectID == PKCS1_PRIV_KEY_VERSION) + { + if (object.len > 0 && *object.ptr != 0) + { + plog(" wrong PKCS#1 private key version"); + return FALSE; + } + } + else if (objectID >= PKCS1_PRIV_KEY_MODULUS && + objectID <= PKCS1_PRIV_KEY_COEFF) + { + MP_INT *u = (MP_INT *) ((char *)key + + RSA_private_field[objectID - PKCS1_PRIV_KEY_MODULUS].offset); + + n_to_mpz(u, object.ptr, object.len); + + if (objectID == PKCS1_PRIV_KEY_MODULUS) + modulus = object; + else if (objectID == PKCS1_PRIV_KEY_PUB_EXP) + exp = object; + } + objectID++; + } + form_keyid(exp, modulus, key->pub.keyid, &key->pub.k); + ugh = RSA_private_key_sanity(key); + return (ugh == NULL); +} + +/* + * compute a digest over a binary blob + */ +bool +compute_digest(chunk_t tbs, int alg, chunk_t *digest) +{ + switch (alg) + { + case OID_MD2: + case OID_MD2_WITH_RSA: + { + MD2_CTX context; + + MD2Init(&context); + MD2Update(&context, tbs.ptr, tbs.len); + MD2Final(digest->ptr, &context); + digest->len = MD2_DIGEST_SIZE; + return TRUE; + } + case OID_MD5: + case OID_MD5_WITH_RSA: + { + MD5_CTX context; + + MD5Init(&context); + MD5Update(&context, tbs.ptr, tbs.len); + MD5Final(digest->ptr, &context); + digest->len = MD5_DIGEST_SIZE; + return TRUE; + } + case OID_SHA1: + case OID_SHA1_WITH_RSA: + case OID_SHA1_WITH_RSA_OIW: + { + SHA1_CTX context; + + SHA1Init(&context); + SHA1Update(&context, tbs.ptr, tbs.len); + SHA1Final(digest->ptr, &context); + digest->len = SHA1_DIGEST_SIZE; + return TRUE; + } + case OID_SHA256: + case OID_SHA256_WITH_RSA: + { + sha256_context context; + + sha256_init(&context); + sha256_write(&context, tbs.ptr, tbs.len); + sha256_final(&context); + memcpy(digest->ptr, context.sha_out, SHA2_256_DIGEST_SIZE); + digest->len = SHA2_256_DIGEST_SIZE; + return TRUE; + } + case OID_SHA384: + case OID_SHA384_WITH_RSA: + { + sha512_context context; + + sha384_init(&context); + sha512_write(&context, tbs.ptr, tbs.len); + sha512_final(&context); + memcpy(digest->ptr, context.sha_out, SHA2_384_DIGEST_SIZE); + digest->len = SHA2_384_DIGEST_SIZE; + return TRUE; + } + case OID_SHA512: + case OID_SHA512_WITH_RSA: + { + sha512_context context; + + sha512_init(&context); + sha512_write(&context, tbs.ptr, tbs.len); + sha512_final(&context); + memcpy(digest->ptr, context.sha_out, SHA2_512_DIGEST_SIZE); + digest->len = SHA2_512_DIGEST_SIZE; + return TRUE; + } + default: + digest->len = 0; + return FALSE; + } +} + +/* + * compute an RSA signature with PKCS#1 padding + */ +void +sign_hash(const RSA_private_key_t *k, const u_char *hash_val, size_t hash_len + , u_char *sig_val, size_t sig_len) +{ + chunk_t ch; + mpz_t t1, t2; + size_t padlen; + u_char *p = sig_val; + + DBG(DBG_CONTROL | DBG_CRYPT, + DBG_log("signing hash with RSA Key *%s", k->pub.keyid) + ) + /* PKCS#1 v1.5 8.1 encryption-block formatting */ + *p++ = 0x00; + *p++ = 0x01; /* BT (block type) 01 */ + padlen = sig_len - 3 - hash_len; + memset(p, 0xFF, padlen); + p += padlen; + *p++ = 0x00; + memcpy(p, hash_val, hash_len); + passert(p + hash_len - sig_val == (ptrdiff_t)sig_len); + + /* PKCS#1 v1.5 8.2 octet-string-to-integer conversion */ + n_to_mpz(t1, sig_val, sig_len); /* (could skip leading 0x00) */ + + /* PKCS#1 v1.5 8.3 RSA computation y = x^c mod n + * Better described in PKCS#1 v2.0 5.1 RSADP. + * There are two methods, depending on the form of the private key. + * We use the one based on the Chinese Remainder Theorem. + */ + mpz_init(t2); + + mpz_powm(t2, t1, &k->dP, &k->p); /* m1 = c^dP mod p */ + + mpz_powm(t1, t1, &k->dQ, &k->q); /* m2 = c^dQ mod Q */ + + mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */ + mpz_mod(t2, t2, &k->p); + mpz_mul(t2, t2, &k->qInv); + mpz_mod(t2, t2, &k->p); + + mpz_mul(t2, t2, &k->q); /* m = m2 + h q */ + mpz_add(t1, t1, t2); + + /* PKCS#1 v1.5 8.4 integer-to-octet-string conversion */ + ch = mpz_to_n(t1, sig_len); + memcpy(sig_val, ch.ptr, sig_len); + pfree(ch.ptr); + + mpz_clear(t1); + mpz_clear(t2); +} + +/* + * encrypt data with an RSA public key after padding + */ +chunk_t +RSA_encrypt(const RSA_public_key_t *key, chunk_t in) +{ + u_char padded[RSA_MAX_OCTETS]; + u_char *pos = padded; + int padding = key->k - in.len - 3; + int i; + + if (padding < 8 || key->k > RSA_MAX_OCTETS) + return empty_chunk; + + /* add padding according to PKCS#1 7.2.1 1.+2. */ + *pos++ = 0x00; + *pos++ = 0x02; + + /* pad with pseudo random bytes unequal to zero */ + get_rnd_bytes(pos, padding); + for (i = 0; i < padding; i++) + { + while (!*pos) + get_rnd_bytes(pos, 1); + pos++; + } + + /* append the padding terminator */ + *pos++ = 0x00; + + /* now add the data */ + memcpy(pos, in.ptr, in.len); + DBG(DBG_RAW, + DBG_dump_chunk("data for rsa encryption:\n", in); + DBG_dump("padded data for rsa encryption:\n", padded, key->k) + ) + + /* convert chunk to integer (PKCS#1 7.2.1 3.a) */ + { + chunk_t out; + mpz_t m, c; + + mpz_init(c); + n_to_mpz(m, padded, key->k); + + /* encrypt(PKCS#1 7.2.1 3.b) */ + mpz_powm(c, m, &key->e, &key->n); + + /* convert integer back to a chunk (PKCS#1 7.2.1 3.c) */ + out = mpz_to_n(c, key->k); + mpz_clear(c); + mpz_clear(m); + + DBG(DBG_RAW, + DBG_dump_chunk("rsa encrypted data:\n", out) + ) + return out; + } +} + +/* + * decrypt data with an RSA private key and remove padding + */ +bool +RSA_decrypt(const RSA_private_key_t *key, chunk_t in, chunk_t *out) +{ + chunk_t padded; + u_char *pos; + mpz_t t1, t2; + + n_to_mpz(t1, in.ptr,in.len); + + /* PKCS#1 v1.5 8.3 RSA computation y = x^c mod n + * Better described in PKCS#1 v2.0 5.1 RSADP. + * There are two methods, depending on the form of the private key. + * We use the one based on the Chinese Remainder Theorem. + */ + mpz_init(t2); + + mpz_powm(t2, t1, &key->dP, &key->p); /* m1 = c^dP mod p */ + mpz_powm(t1, t1, &key->dQ, &key->q); /* m2 = c^dQ mod Q */ + + mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */ + mpz_mod(t2, t2, &key->p); + mpz_mul(t2, t2, &key->qInv); + mpz_mod(t2, t2, &key->p); + + mpz_mul(t2, t2, &key->q); /* m = m2 + h q */ + mpz_add(t1, t1, t2); + + padded = mpz_to_n(t1, key->pub.k); + mpz_clear(t1); + mpz_clear(t2); + + DBG(DBG_PRIVATE, + DBG_dump_chunk("rsa decrypted data with padding:\n", padded) + ) + pos = padded.ptr; + + /* PKCS#1 v1.5 8.1 encryption-block formatting (EB = 00 || 02 || PS || 00 || D) */ + + /* check for hex pattern 00 02 in decrypted message */ + if ((*pos++ != 0x00) || (*(pos++) != 0x02)) + { + plog("incorrect padding - probably wrong RSA key"); + freeanychunk(padded); + return FALSE; + } + padded.len -= 2; + + /* the plaintext data starts after first 0x00 byte */ + while (padded.len-- > 0 && *pos++ != 0x00) + + if (padded.len == 0) + { + plog("no plaintext data"); + freeanychunk(padded); + return FALSE; + } + + clonetochunk(*out, pos, padded.len, "decrypted data"); + freeanychunk(padded); + return TRUE; +} + +/* + * build signatureValue + */ +chunk_t +pkcs1_build_signature(chunk_t tbs, int hash_alg, const RSA_private_key_t *key +, bool bit_string) +{ + + size_t siglen = key->pub.k; + + u_char digest_buf[MAX_DIGEST_LEN]; + chunk_t digest = { digest_buf, MAX_DIGEST_LEN }; + chunk_t digestInfo, alg_id, signatureValue; + u_char *pos; + + switch (hash_alg) + { + case OID_MD5: + case OID_MD5_WITH_RSA: + alg_id = ASN1_md5_id; + break; + case OID_SHA1: + case OID_SHA1_WITH_RSA: + alg_id = ASN1_sha1_id; + break; + default: + return empty_chunk; + } + compute_digest(tbs, hash_alg, &digest); + + /* according to PKCS#1 v2.1 digest must be packaged into + * an ASN.1 structure for encryption + */ + digestInfo = asn1_wrap(ASN1_SEQUENCE, "cm" + , alg_id + , asn1_simple_object(ASN1_OCTET_STRING, digest)); + + /* generate the RSA signature */ + if (bit_string) + { + pos = build_asn1_object(&signatureValue, ASN1_BIT_STRING, 1 + siglen); + *pos++ = 0x00; + } + else + { + pos = build_asn1_object(&signatureValue, ASN1_OCTET_STRING, siglen); + } + sign_hash(key, digestInfo.ptr, digestInfo.len, pos, siglen); + pfree(digestInfo.ptr); + + return signatureValue; +} + +/* + * build a DER-encoded PKCS#1 private key object + */ +chunk_t +pkcs1_build_private_key(const RSA_private_key_t *key) +{ + chunk_t pkcs1 = asn1_wrap(ASN1_SEQUENCE, "cmmmmmmmm" + , ASN1_INTEGER_0 + , asn1_integer_from_mpz(&key->pub.n) + , asn1_integer_from_mpz(&key->pub.e) + , asn1_integer_from_mpz(&key->d) + , asn1_integer_from_mpz(&key->p) + , asn1_integer_from_mpz(&key->q) + , asn1_integer_from_mpz(&key->dP) + , asn1_integer_from_mpz(&key->dQ) + , asn1_integer_from_mpz(&key->qInv)); + + DBG(DBG_PRIVATE, + DBG_dump_chunk("PKCS#1 encoded private key:", pkcs1) + ) + return pkcs1; +} + +/* + * build a DER-encoded PKCS#1 public key object + */ +chunk_t +pkcs1_build_public_key(const RSA_public_key_t *rsa) +{ + return asn1_wrap(ASN1_SEQUENCE, "mm" + , asn1_integer_from_mpz(&rsa->n) + , asn1_integer_from_mpz(&rsa->e)); +} + +/* + * build a DER-encoded publicKeyInfo object + */ +chunk_t +pkcs1_build_publicKeyInfo(const RSA_public_key_t *rsa) +{ + chunk_t publicKey; + chunk_t rawKey = pkcs1_build_public_key(rsa); + + u_char *pos = build_asn1_object(&publicKey, ASN1_BIT_STRING + , 1 + rawKey.len); + *pos++ = 0x00; + mv_chunk(&pos, rawKey); + + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_rsaEncryption_id + , publicKey); +} +void +free_RSA_public_content(RSA_public_key_t *rsa) +{ + mpz_clear(&rsa->n); + mpz_clear(&rsa->e); +} + +void +free_RSA_private_content(RSA_private_key_t *rsak) +{ + free_RSA_public_content(&rsak->pub); + mpz_clear(&rsak->d); + mpz_clear(&rsak->p); + mpz_clear(&rsak->q); + mpz_clear(&rsak->dP); + mpz_clear(&rsak->dQ); + mpz_clear(&rsak->qInv); +} + diff --git a/src/pluto/pkcs1.h b/src/pluto/pkcs1.h new file mode 100644 index 000000000..c927db0f8 --- /dev/null +++ b/src/pluto/pkcs1.h @@ -0,0 +1,88 @@ +/* Support of PKCS#1 private key data structures + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2005 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pkcs1.h,v 1.14 2005/12/06 22:52:12 as Exp $ + */ + +#ifndef _PKCS1_H +#define _PKCS1_H + +#include /* GNU Multi Precision library */ + +#include "defs.h" + +typedef struct RSA_public_key RSA_public_key_t; + +struct RSA_public_key +{ + char keyid[KEYID_BUF]; /* see ipsec_keyblobtoid(3) */ + + /* length of modulus n in octets: [RSA_MIN_OCTETS, RSA_MAX_OCTETS] */ + unsigned k; + + /* public: */ + MP_INT + n, /* modulus: p * q */ + e; /* exponent: relatively prime to (p-1) * (q-1) [probably small] */ +}; + +typedef struct RSA_private_key RSA_private_key_t; + +struct RSA_private_key { + struct RSA_public_key pub; /* must be at start for RSA_show_public_key */ + + MP_INT + d, /* private exponent: (e^-1) mod ((p-1) * (q-1)) */ + /* help for Chinese Remainder Theorem speedup: */ + p, /* first secret prime */ + q, /* second secret prime */ + dP, /* first factor's exponent: (e^-1) mod (p-1) == d mod (p-1) */ + dQ, /* second factor's exponent: (e^-1) mod (q-1) == d mod (q-1) */ + qInv; /* (q^-1) mod p */ +}; + +struct fld { + const char *name; + size_t offset; +}; + +extern const struct fld RSA_private_field[]; +#define RSA_PRIVATE_FIELD_ELEMENTS 8 + +extern void init_RSA_public_key(RSA_public_key_t *rsa, chunk_t e, chunk_t n); +extern bool pkcs1_parse_private_key(chunk_t blob, RSA_private_key_t *key); +extern chunk_t pkcs1_build_private_key(const RSA_private_key_t *key); +extern chunk_t pkcs1_build_public_key(const RSA_public_key_t *rsa); +extern chunk_t pkcs1_build_publicKeyInfo(const RSA_public_key_t *rsa); +extern chunk_t pkcs1_build_signature(chunk_t tbs, int hash_alg + , const RSA_private_key_t *key, bool bit_string); +extern bool compute_digest(chunk_t tbs, int alg, chunk_t *digest); +extern void sign_hash(const RSA_private_key_t *k, const u_char *hash_val + , size_t hash_len, u_char *sig_val, size_t sig_len); +extern chunk_t RSA_encrypt(const RSA_public_key_t *key, chunk_t in); +extern bool RSA_decrypt(const RSA_private_key_t *key, chunk_t in + , chunk_t *out); +extern bool same_RSA_public_key(const RSA_public_key_t *a + , const RSA_public_key_t *b); +extern void form_keyid(chunk_t e, chunk_t n, char* keyid, unsigned *keysize); +extern err_t RSA_private_key_sanity(RSA_private_key_t *k); +#ifdef DEBUG +extern void RSA_show_public_key(RSA_public_key_t *k); +extern void RSA_show_private_key(RSA_private_key_t *k); +#endif +extern void free_RSA_public_content(RSA_public_key_t *rsa); +extern void free_RSA_private_content(RSA_private_key_t *rsak); + +#endif /* _PKCS1_H */ diff --git a/src/pluto/pkcs7.c b/src/pluto/pkcs7.c new file mode 100644 index 000000000..3068c0c94 --- /dev/null +++ b/src/pluto/pkcs7.c @@ -0,0 +1,862 @@ +/* Support of PKCS#7 data structures + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2005 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pkcs7.c,v 1.13 2005/12/22 22:11:24 as Exp $ + */ + +#include +#include +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "asn1.h" +#include "oid.h" +#include "log.h" +#include "x509.h" +#include "certs.h" +#include "pkcs7.h" +#include "rnd.h" + +const contentInfo_t empty_contentInfo = { + OID_UNKNOWN , /* type */ + { NULL, 0 } /* content */ +}; + +/* ASN.1 definition of the PKCS#7 ContentInfo type */ + +static const asn1Object_t contentInfoObjects[] = { + { 0, "contentInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "contentType", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "content", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_BODY }, /* 2 */ + { 1, "end opt", ASN1_EOC, ASN1_END } /* 3 */ +}; + +#define PKCS7_INFO_TYPE 1 +#define PKCS7_INFO_CONTENT 2 +#define PKCS7_INFO_ROOF 4 + +/* ASN.1 definition of the PKCS#7 signedData type */ + +static const asn1Object_t signedDataObjects[] = { + { 0, "signedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "digestAlgorithms", ASN1_SET, ASN1_LOOP }, /* 2 */ + { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 3 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "contentInfo", ASN1_EOC, ASN1_RAW }, /* 5 */ + { 1, "certificates", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_LOOP }, /* 6 */ + { 2, "certificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 7 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 8 */ + { 1, "crls", ASN1_CONTEXT_C_1, ASN1_OPT | + ASN1_LOOP }, /* 9 */ + { 2, "crl", ASN1_SEQUENCE, ASN1_OBJ }, /* 10 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END }, /* 11 */ + { 1, "signerInfos", ASN1_SET, ASN1_LOOP }, /* 12 */ + { 2, "signerInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 15 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 16 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 17 */ + { 3, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 18 */ + { 3, "authenticatedAttributes", ASN1_CONTEXT_C_0, ASN1_OPT | + ASN1_OBJ }, /* 19 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 20 */ + { 3, "digestEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 21 */ + { 3, "encryptedDigest", ASN1_OCTET_STRING, ASN1_BODY }, /* 22 */ + { 3, "unauthenticatedAttributes", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 23 */ + { 3, "end opt", ASN1_EOC, ASN1_END }, /* 24 */ + { 1, "end loop", ASN1_EOC, ASN1_END } /* 25 */ +}; + +#define PKCS7_DIGEST_ALG 3 +#define PKCS7_SIGNED_CONTENT_INFO 5 +#define PKCS7_SIGNED_CERT 7 +#define PKCS7_SIGNER_INFO 13 +#define PKCS7_SIGNED_ISSUER 16 +#define PKCS7_SIGNED_SERIAL_NUMBER 17 +#define PKCS7_DIGEST_ALGORITHM 18 +#define PKCS7_AUTH_ATTRIBUTES 19 +#define PKCS7_DIGEST_ENC_ALGORITHM 21 +#define PKCS7_ENCRYPTED_DIGEST 22 +#define PKCS7_SIGNED_ROOF 26 + +/* ASN.1 definition of the PKCS#7 envelopedData type */ + +static const asn1Object_t envelopedDataObjects[] = { + { 0, "envelopedData", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "recipientInfos", ASN1_SET, ASN1_LOOP }, /* 2 */ + { 2, "recipientInfo", ASN1_SEQUENCE, ASN1_BODY }, /* 3 */ + { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 3, "issuerAndSerialNumber", ASN1_SEQUENCE, ASN1_BODY }, /* 5 */ + { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 6 */ + { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 3, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 8 */ + { 3, "encryptedKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 9 */ + { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */ + { 1, "encryptedContentInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ + { 2, "contentType", ASN1_OID, ASN1_BODY }, /* 12 */ + { 2, "contentEncryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 13 */ + { 2, "encryptedContent", ASN1_CONTEXT_S_0, ASN1_BODY } /* 14 */ +}; + +#define PKCS7_ENVELOPED_VERSION 1 +#define PKCS7_RECIPIENT_INFO_VERSION 4 +#define PKCS7_ISSUER 6 +#define PKCS7_SERIAL_NUMBER 7 +#define PKCS7_ENCRYPTION_ALG 8 +#define PKCS7_ENCRYPTED_KEY 9 +#define PKCS7_CONTENT_TYPE 12 +#define PKCS7_CONTENT_ENC_ALGORITHM 13 +#define PKCS7_ENCRYPTED_CONTENT 14 +#define PKCS7_ENVELOPED_ROOF 15 + +/* PKCS7 contentInfo OIDs */ + +static u_char ASN1_pkcs7_data_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 +}; + +static u_char ASN1_pkcs7_signed_data_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 +}; + +static u_char ASN1_pkcs7_enveloped_data_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x03 +}; + +static u_char ASN1_pkcs7_signed_enveloped_data_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x04 +}; + +static u_char ASN1_pkcs7_digested_data_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x05 +}; + +static char ASN1_pkcs7_encrypted_data_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 +}; + +static const chunk_t ASN1_pkcs7_data_oid = + strchunk(ASN1_pkcs7_data_oid_str); +static const chunk_t ASN1_pkcs7_signed_data_oid = + strchunk(ASN1_pkcs7_signed_data_oid_str); +static const chunk_t ASN1_pkcs7_enveloped_data_oid = + strchunk(ASN1_pkcs7_enveloped_data_oid_str); +static const chunk_t ASN1_pkcs7_signed_enveloped_data_oid = + strchunk(ASN1_pkcs7_signed_enveloped_data_oid_str); +static const chunk_t ASN1_pkcs7_digested_data_oid = + strchunk(ASN1_pkcs7_digested_data_oid_str); +static const chunk_t ASN1_pkcs7_encrypted_data_oid = + strchunk(ASN1_pkcs7_encrypted_data_oid_str); + +/* 3DES and DES encryption OIDs */ + +static u_char ASN1_3des_ede_cbc_oid_str[] = { + 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x07 +}; + +static u_char ASN1_des_cbc_oid_str[] = { + 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x07 +}; + +static const chunk_t ASN1_3des_ede_cbc_oid = + strchunk(ASN1_3des_ede_cbc_oid_str); +static const chunk_t ASN1_des_cbc_oid = + strchunk(ASN1_des_cbc_oid_str); + +/* PKCS#7 attribute type OIDs */ + +static u_char ASN1_contentType_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x03 +}; + +static u_char ASN1_messageDigest_oid_str[] = { + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04 +}; + +static const chunk_t ASN1_contentType_oid = + strchunk(ASN1_contentType_oid_str); +static const chunk_t ASN1_messageDigest_oid = + strchunk(ASN1_messageDigest_oid_str); + +/* + * Parse PKCS#7 ContentInfo object + */ +bool +pkcs7_parse_contentInfo(chunk_t blob, u_int level0, contentInfo_t *cInfo) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, level0, FALSE, DBG_RAW); + + while (objectID < PKCS7_INFO_ROOF) + { + if (!extract_object(contentInfoObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + if (objectID == PKCS7_INFO_TYPE) + { + cInfo->type = known_oid(object); + if (cInfo->type < OID_PKCS7_DATA + || cInfo->type > OID_PKCS7_ENCRYPTED_DATA) + { + plog("unknown pkcs7 content type"); + return FALSE; + } + } + else if (objectID == PKCS7_INFO_CONTENT) + { + cInfo->content = object; + } + objectID++; + } + return TRUE; +} + +/* + * Parse a PKCS#7 signedData object + */ +bool +pkcs7_parse_signedData(chunk_t blob, contentInfo_t *data, x509cert_t **cert +, chunk_t *attributes, const x509cert_t *cacert) +{ + u_char buf[BUF_LEN]; + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int digest_alg = OID_UNKNOWN; + int enc_alg = OID_UNKNOWN; + int signerInfos = 0; + int objectID = 0; + + contentInfo_t cInfo = empty_contentInfo; + chunk_t encrypted_digest = empty_chunk; + + if (!pkcs7_parse_contentInfo(blob, 0, &cInfo)) + return FALSE; + + if (cInfo.type != OID_PKCS7_SIGNED_DATA) + { + plog("pkcs7 content type is not signedData"); + return FALSE; + } + + asn1_init(&ctx, cInfo.content, 2, FALSE, DBG_RAW); + + while (objectID < PKCS7_SIGNED_ROOF) + { + if (!extract_object(signedDataObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + switch (objectID) + { + case PKCS7_DIGEST_ALG: + digest_alg = parse_algorithmIdentifier(object, level, NULL); + break; + case PKCS7_SIGNED_CONTENT_INFO: + if (data != NULL) + { + pkcs7_parse_contentInfo(object, level, data); + } + break; + case PKCS7_SIGNED_CERT: + if (cert != NULL) + { + chunk_t cert_blob; + + x509cert_t *newcert = alloc_thing(x509cert_t + , "pkcs7 wrapped x509cert"); + + clonetochunk(cert_blob, object.ptr, object.len + , "pkcs7 cert blob"); + *newcert = empty_x509cert; + + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log("parsing pkcs7-wrapped certificate") + ) + if (parse_x509cert(cert_blob, level+1, newcert)) + { + newcert->next = *cert; + *cert = newcert; + } + else + { + free_x509cert(newcert); + } + } + break; + case PKCS7_SIGNER_INFO: + signerInfos++; + DBG(DBG_PARSING, + DBG_log(" signer #%d", signerInfos) + ) + break; + case PKCS7_SIGNED_ISSUER: + DBG(DBG_PARSING, + dntoa(buf, BUF_LEN, object); + DBG_log(" '%s'",buf) + ) + break; + case PKCS7_AUTH_ATTRIBUTES: + if (attributes != NULL) + { + *attributes = object; + *attributes->ptr = ASN1_SET; + } + break; + case PKCS7_DIGEST_ALGORITHM: + digest_alg = parse_algorithmIdentifier(object, level, NULL); + break; + case PKCS7_DIGEST_ENC_ALGORITHM: + enc_alg = parse_algorithmIdentifier(object, level, NULL); + break; + case PKCS7_ENCRYPTED_DIGEST: + encrypted_digest = object; + } + objectID++; + } + + /* check the signature only if a cacert is available */ + if (cacert != NULL) + { + if (signerInfos == 0) + { + plog("no signerInfo object found"); + return FALSE; + } + else if (signerInfos > 1) + { + plog("more than one signerInfo object found"); + return FALSE; + } + if (attributes->ptr == NULL) + { + plog("no authenticatedAttributes object found"); + return FALSE; + } + if (!check_signature(*attributes, encrypted_digest, digest_alg + , enc_alg, cacert)) + { + plog("invalid signature"); + return FALSE; + } + else + { + DBG(DBG_CONTROL, + DBG_log("signature is valid") + ) + } + } + return TRUE; +} + +/* + * Parse a PKCS#7 envelopedData object + */ +bool +pkcs7_parse_envelopedData(chunk_t blob, chunk_t *data +, chunk_t serialNumber, const RSA_private_key_t *key) +{ + asn1_ctx_t ctx; + chunk_t object; + chunk_t iv = empty_chunk; + chunk_t symmetric_key = empty_chunk; + chunk_t encrypted_content = empty_chunk; + + u_char buf[BUF_LEN]; + u_int level; + u_int total_keys = 3; + int enc_alg = OID_UNKNOWN; + int content_enc_alg = OID_UNKNOWN; + int objectID = 0; + + contentInfo_t cInfo = empty_contentInfo; + *data = empty_chunk; + + if (!pkcs7_parse_contentInfo(blob, 0, &cInfo)) + goto failed; + + if (cInfo.type != OID_PKCS7_ENVELOPED_DATA) + { + plog("pkcs7 content type is not envelopedData"); + return FALSE; + } + + asn1_init(&ctx, cInfo.content, 2, FALSE, DBG_RAW); + + while (objectID < PKCS7_ENVELOPED_ROOF) + { + if (!extract_object(envelopedDataObjects, &objectID, &object, &level, &ctx)) + goto failed; + + switch (objectID) + { + case PKCS7_ENVELOPED_VERSION: + if (*object.ptr != 0) + { + plog("envelopedData version is not 0"); + goto failed; + } + break; + case PKCS7_RECIPIENT_INFO_VERSION: + if (*object.ptr != 0) + { + plog("recipient info version is not 0"); + goto failed; + } + break; + case PKCS7_ISSUER: + DBG(DBG_PARSING, + dntoa(buf, BUF_LEN, object); + DBG_log(" '%s'", buf) + ) + break; + case PKCS7_SERIAL_NUMBER: + if (!same_chunk(serialNumber, object)) + { + plog("serial numbers do not match"); + goto failed; + } + break; + case PKCS7_ENCRYPTION_ALG: + enc_alg = parse_algorithmIdentifier(object, level, NULL); + if (enc_alg != OID_RSA_ENCRYPTION) + { + plog("only rsa encryption supported"); + goto failed; + } + break; + case PKCS7_ENCRYPTED_KEY: + if (!RSA_decrypt(key, object, &symmetric_key)) + { + plog("symmetric key could not be decrypted with rsa"); + goto failed; + } + DBG(DBG_PRIVATE, + DBG_dump_chunk("symmetric key :", symmetric_key) + ) + break; + case PKCS7_CONTENT_TYPE: + if (known_oid(object) != OID_PKCS7_DATA) + { + plog("encrypted content not of type pkcs7 data"); + goto failed; + } + break; + case PKCS7_CONTENT_ENC_ALGORITHM: + content_enc_alg = parse_algorithmIdentifier(object, level, &iv); + + switch (content_enc_alg) + { + case OID_DES_CBC: + total_keys = 1; + break; + case OID_3DES_EDE_CBC: + total_keys = 3; + break; + default: + plog("Only DES and 3DES supported for symmetric encryption"); + goto failed; + } + if (symmetric_key.len != (total_keys * DES_CBC_BLOCK_SIZE)) + { + plog("key length is not %d",(total_keys * DES_CBC_BLOCK_SIZE)); + goto failed; + } + if (!parse_asn1_simple_object(&iv, ASN1_OCTET_STRING, level+1, "IV")) + { + plog("IV could not be parsed"); + goto failed; + } + if (iv.len != DES_CBC_BLOCK_SIZE) + { + plog("IV has wrong length"); + goto failed; + } + break; + case PKCS7_ENCRYPTED_CONTENT: + encrypted_content = object; + break; + } + objectID++; + } + + /* decrypt the content */ + { + u_int i; + des_cblock des_key[3], des_iv; + des_key_schedule key_s[3]; + + memcpy((char *)des_key, symmetric_key.ptr, symmetric_key.len); + memcpy((char *)des_iv, iv.ptr, iv.len); + + for (i = 0; i < total_keys; i++) + { + if (des_set_key(&des_key[i], key_s[i])) + { + plog("des key schedule failed"); + goto failed; + } + } + + data->len = encrypted_content.len; + data->ptr = alloc_bytes(data->len, "decrypted data"); + + switch (content_enc_alg) + { + case OID_DES_CBC: + des_cbc_encrypt((des_cblock*)encrypted_content.ptr + , (des_cblock*)data->ptr, data->len + , key_s[0], &des_iv, DES_DECRYPT); + break; + case OID_3DES_EDE_CBC: + des_ede3_cbc_encrypt( (des_cblock*)encrypted_content.ptr + , (des_cblock*)data->ptr, data->len + , key_s[0], key_s[1], key_s[2] + , &des_iv, DES_DECRYPT); + } + DBG(DBG_PRIVATE, + DBG_dump_chunk("decrypted content with padding:\n", *data) + ) + } + + /* remove the padding */ + { + u_char *pos = data->ptr + data->len - 1; + u_char pattern = *pos; + size_t padding = pattern; + + if (padding > data->len) + { + plog("padding greater than data length"); + goto failed; + } + data->len -= padding; + + while (padding-- > 0) + { + if (*pos-- != pattern) + { + plog("wrong padding pattern"); + goto failed; + } + } + } + freeanychunk(symmetric_key); + return TRUE; + +failed: + freeanychunk(symmetric_key); + pfreeany(data->ptr); + return FALSE; +} + +/** + * @brief Builds a contentType attribute + * + * @return ASN.1 encoded contentType attribute + */ +chunk_t +pkcs7_contentType_attribute(void) +{ + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_contentType_oid + , asn1_simple_object(ASN1_SET, ASN1_pkcs7_data_oid)); +} + +/** + * @brief Builds a messageDigest attribute + * + * + * @param[in] blob content to create digest of + * @param[in] digest_alg digest algorithm to be used + * @return ASN.1 encoded messageDigest attribute + * + */ +chunk_t +pkcs7_messageDigest_attribute(chunk_t content, int digest_alg) +{ + u_char digest_buf[MAX_DIGEST_LEN]; + chunk_t digest = { digest_buf, MAX_DIGEST_LEN }; + + compute_digest(content, digest_alg, &digest); + + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_messageDigest_oid + , asn1_wrap(ASN1_SET, "m" + , asn1_simple_object(ASN1_OCTET_STRING, digest) + ) + ); +} +/* + * build a DER-encoded contentInfo object + */ +static chunk_t +pkcs7_build_contentInfo(contentInfo_t *cInfo) +{ + chunk_t content_type; + + /* select DER-encoded OID for pkcs7 contentInfo type */ + switch(cInfo->type) + { + case OID_PKCS7_DATA: + content_type = ASN1_pkcs7_data_oid; + break; + case OID_PKCS7_SIGNED_DATA: + content_type = ASN1_pkcs7_signed_data_oid; + break; + case OID_PKCS7_ENVELOPED_DATA: + content_type = ASN1_pkcs7_enveloped_data_oid; + break; + case OID_PKCS7_SIGNED_ENVELOPED_DATA: + content_type = ASN1_pkcs7_signed_enveloped_data_oid; + break; + case OID_PKCS7_DIGESTED_DATA: + content_type = ASN1_pkcs7_digested_data_oid; + break; + case OID_PKCS7_ENCRYPTED_DATA: + content_type = ASN1_pkcs7_encrypted_data_oid; + break; + case OID_UNKNOWN: + default: + fprintf(stderr, "invalid pkcs7 contentInfo type"); + return empty_chunk; + } + + return (cInfo->content.ptr == NULL) + ? asn1_simple_object(ASN1_SEQUENCE, content_type) + : asn1_wrap(ASN1_SEQUENCE, "cm" + , content_type + , asn1_simple_object(ASN1_CONTEXT_C_0, cInfo->content) + ); +} + +/* + * build issuerAndSerialNumber object + */ +chunk_t +pkcs7_build_issuerAndSerialNumber(const x509cert_t *cert) +{ + return asn1_wrap(ASN1_SEQUENCE, "cm" + , cert->issuer + , asn1_simple_object(ASN1_INTEGER, cert->serialNumber)); +} + +/* + * create a signed pkcs7 contentInfo object + */ +chunk_t +pkcs7_build_signedData(chunk_t data, chunk_t attributes, const x509cert_t *cert +, int digest_alg, const RSA_private_key_t *key) +{ + contentInfo_t pkcs7Data, signedData; + chunk_t authenticatedAttributes, encryptedDigest, signerInfo, cInfo; + + chunk_t digestAlgorithm = asn1_algorithmIdentifier(digest_alg); + + if (attributes.ptr != NULL) + { + encryptedDigest = pkcs1_build_signature(attributes, digest_alg + , key, FALSE); + clonetochunk(authenticatedAttributes, attributes.ptr, attributes.len + , "authenticatedAttributes"); + *authenticatedAttributes.ptr = ASN1_CONTEXT_C_0; + } + else + { + encryptedDigest = (data.ptr == NULL)? empty_chunk + : pkcs1_build_signature(data, digest_alg, key, FALSE); + authenticatedAttributes = empty_chunk; + } + + signerInfo = asn1_wrap(ASN1_SEQUENCE, "cmcmcm" + , ASN1_INTEGER_1 + , pkcs7_build_issuerAndSerialNumber(cert) + , digestAlgorithm + , authenticatedAttributes + , ASN1_rsaEncryption_id + , encryptedDigest); + + pkcs7Data.type = OID_PKCS7_DATA; + pkcs7Data.content = (data.ptr == NULL)? empty_chunk + : asn1_simple_object(ASN1_OCTET_STRING, data); + + signedData.type = OID_PKCS7_SIGNED_DATA; + signedData.content = asn1_wrap(ASN1_SEQUENCE, "cmmmm" + , ASN1_INTEGER_1 + , asn1_simple_object(ASN1_SET, digestAlgorithm) + , pkcs7_build_contentInfo(&pkcs7Data) + , asn1_simple_object(ASN1_CONTEXT_C_0, cert->certificate) + , asn1_wrap(ASN1_SET, "m", signerInfo)); + + cInfo = pkcs7_build_contentInfo(&signedData); + DBG(DBG_RAW, + DBG_dump_chunk("signedData:\n", cInfo) + ) + + freeanychunk(pkcs7Data.content); + freeanychunk(signedData.content); + return cInfo; +} + +/* + * create a symmetrically encrypted pkcs7 contentInfo object + */ +chunk_t +pkcs7_build_envelopedData(chunk_t data, const x509cert_t *cert, int cipher) +{ + bool des_check_key_save; + des_key_schedule ks[3]; + des_cblock key[3], des_iv, des_iv_buf; + + chunk_t iv = { (u_char *)des_iv_buf, DES_CBC_BLOCK_SIZE }; + chunk_t out; + chunk_t cipher_oid; + + u_int total_keys, i; + size_t padding = pad_up(data.len, DES_CBC_BLOCK_SIZE); + + RSA_public_key_t public_key; + + init_RSA_public_key(&public_key, cert->publicExponent + , cert->modulus); + + if (padding == 0) + padding += DES_CBC_BLOCK_SIZE; + + out.len = data.len + padding; + out.ptr = alloc_bytes(out.len, "DES-encrypted output"); + + DBG(DBG_CONTROL, + DBG_log("padding %d bytes of data to multiple DES block size of %d bytes" + , (int)data.len, (int)out.len) + ) + + /* copy data */ + memcpy(out.ptr, data.ptr, data.len); + /* append padding */ + memset(out.ptr + data.len, padding, padding); + + DBG(DBG_RAW, + DBG_dump_chunk("Padded unencrypted data:\n", out) + ) + + /* select OID and keylength for specified cipher */ + switch (cipher) + { + case OID_DES_CBC: + total_keys = 1; + cipher_oid = ASN1_des_cbc_oid; + break; + case OID_3DES_EDE_CBC: + default: + total_keys = 3; + cipher_oid = ASN1_3des_ede_cbc_oid; + } + DBG(DBG_CONTROLMORE, + DBG_log("pkcs7 encryption cipher: %s", oid_names[cipher].name) + ) + + /* generate a strong random key for DES/3DES */ + des_check_key_save = des_check_key; + des_check_key = TRUE; + for (i = 0; i < total_keys;i++) + { + for (;;) + { + get_rnd_bytes((char*)key[i], DES_CBC_BLOCK_SIZE); + des_set_odd_parity(&key[i]); + if (!des_set_key(&key[i], ks[i])) + break; + plog("weak DES key discarded - we try again"); + } + DBG(DBG_PRIVATE, + DBG_dump("DES key:", key[i], 8) + ) + } + des_check_key = des_check_key_save; + + /* generate an iv for DES/3DES CBC */ + get_rnd_bytes(des_iv, DES_CBC_BLOCK_SIZE); + memcpy(iv.ptr, des_iv, DES_CBC_BLOCK_SIZE); + DBG(DBG_RAW, + DBG_dump_chunk("DES IV :", iv) + ) + + /* encryption using specified cipher */ + switch (cipher) + { + case OID_DES_CBC: + des_cbc_encrypt((des_cblock*)out.ptr, (des_cblock*)out.ptr, out.len + , ks[0], &des_iv, DES_ENCRYPT); + break; + case OID_3DES_EDE_CBC: + default: + des_ede3_cbc_encrypt((des_cblock*)out.ptr, (des_cblock*)out.ptr, out.len + , ks[0], ks[1], ks[2], &des_iv, DES_ENCRYPT); + } + DBG(DBG_RAW, + DBG_dump_chunk("Encrypted data:\n", out)); + + /* build pkcs7 enveloped data object */ + { + chunk_t contentEncryptionAlgorithm = asn1_wrap(ASN1_SEQUENCE, "cm" + , cipher_oid + , asn1_simple_object(ASN1_OCTET_STRING, iv)); + + chunk_t encryptedContentInfo = asn1_wrap(ASN1_SEQUENCE, "cmm" + , ASN1_pkcs7_data_oid + , contentEncryptionAlgorithm + , asn1_wrap(ASN1_CONTEXT_S_0, "m", out)); + + chunk_t plainKey = { (u_char *)key, DES_CBC_BLOCK_SIZE * total_keys }; + + chunk_t encryptedKey = asn1_wrap(ASN1_OCTET_STRING, "m" + , RSA_encrypt(&public_key, plainKey)); + + chunk_t recipientInfo = asn1_wrap(ASN1_SEQUENCE, "cmcm" + , ASN1_INTEGER_0 + , pkcs7_build_issuerAndSerialNumber(cert) + , ASN1_rsaEncryption_id + , encryptedKey); + + chunk_t cInfo; + contentInfo_t envelopedData; + + envelopedData.type = OID_PKCS7_ENVELOPED_DATA; + envelopedData.content = asn1_wrap(ASN1_SEQUENCE, "cmm" + , ASN1_INTEGER_0 + , asn1_wrap(ASN1_SET, "m", recipientInfo) + , encryptedContentInfo); + + cInfo = pkcs7_build_contentInfo(&envelopedData); + DBG(DBG_RAW, + DBG_dump_chunk("envelopedData:\n", cInfo) + ) + + free_RSA_public_content(&public_key); + freeanychunk(envelopedData.content); + return cInfo; + } +} diff --git a/src/pluto/pkcs7.h b/src/pluto/pkcs7.h new file mode 100644 index 000000000..38c633f4e --- /dev/null +++ b/src/pluto/pkcs7.h @@ -0,0 +1,51 @@ +/* Support of PKCS#7 data structures + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2005 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: pkcs7.h,v 1.10 2005/12/22 22:11:24 as Exp $ + */ + +#ifndef _PKCS7_H +#define _PKCS7_H + +#include "defs.h" +#include "pkcs1.h" +#include "x509.h" + +/* Access structure for a PKCS#7 ContentInfo object */ + +typedef struct contentInfo contentInfo_t; + +struct contentInfo { + int type; + chunk_t content; +}; + +extern const contentInfo_t empty_contentInfo; + +extern bool pkcs7_parse_contentInfo(chunk_t blob, u_int level0 + , contentInfo_t *cInfo); +extern bool pkcs7_parse_signedData(chunk_t blob, contentInfo_t *data + , x509cert_t **cert, chunk_t *attributes, const x509cert_t *cacert); +extern bool pkcs7_parse_envelopedData(chunk_t blob, chunk_t *data + , chunk_t serialNumber, const RSA_private_key_t *key); +extern chunk_t pkcs7_contentType_attribute(void); +extern chunk_t pkcs7_messageDigest_attribute(chunk_t content, int digest_alg); +extern chunk_t pkcs7_build_issuerAndSerialNumber(const x509cert_t *cert); +extern chunk_t pkcs7_build_signedData(chunk_t data, chunk_t attributes + ,const x509cert_t *cert, int digest_alg, const RSA_private_key_t *key); +extern chunk_t pkcs7_build_envelopedData(chunk_t data, const x509cert_t *cert + , int cipher); + +#endif /* _PKCS7_H */ diff --git a/src/pluto/pluto.8 b/src/pluto/pluto.8 new file mode 100644 index 000000000..b80d13772 --- /dev/null +++ b/src/pluto/pluto.8 @@ -0,0 +1,1649 @@ +.TH IPSEC_PLUTO 8 "28 March 1999" +.SH NAME +ipsec pluto \- IPsec IKE keying daemon +.br +ipsec whack \- control interface for IPSEC keying daemon +.SH SYNOPSIS +.na +.nh +.HP +.ft B +ipsec pluto +[\-\-help] +[\-\-version] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-nofork] +[\-\-stderrlog] +[\-\-noklips] +[\-\-uniqueids] +[\fB\-\-interface\fP \fIinterfacename\fP] +[\-\-ikeport\ \c +\fIportnumber\fP] +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-secretsfile\ \c +\fIsecrets\(hyfile\fP] +[\-\-adns \fIpathname\fP] +[\-\-lwdnsq \fIpathname\fP] +[\-\-perpeerlog] +[\-\-perpeerlogbase\ \c +\fIdirname\fP] +[\-\-debug\(hynone] +[\-\-debug\(hyall] +[\-\-debug\(hyraw] +[\-\-debug\(hycrypt] +[\-\-debug\(hyparsing] +[\-\-debug\(hyemitting] +[\-\-debug\(hycontrol] +[\-\-debug\(hylifecycle] +[\-\-debug\(hyklips] +[\-\-debug\(hydns] +[\-\-debug\(hyoppo] +[\-\-debug\(hyprivate] +.HP +.ft B +ipsec whack +[\-\-help] +[\-\-version] +.HP +.ft B +ipsec whack +\-\-name\ \c +\fIconnection-name\fP +.br +[\-\-id\ \c +\fIid\fP] \c +[\-\-host\ \c +\fIip\(hyaddress\fP] +[\-\-ikeport\ \c +\fIport\(hynumber\fP] +[\-\-nexthop\ \c +\fIip\(hyaddress\fP] +[\-\-client\ \c +\fIsubnet\fP] +[\-\-dnskeyondemand] +[\-\-updown\ \c +\fIupdown\fP] +.br +\-\-to +.br +[\-\-id\ \c +\fIid\fP] +[\-\-host\ \c +\fIip\(hyaddress\fP] +[\-\-ikeport\ \c +\fIport\(hynumber\fP] +[\-\-nexthop\ \c +\fIip\(hyaddress\fP] +[\-\-client\ \c +\fIsubnet\fP] +[\-\-dnskeyondemand] +[\-\-updown\ \c +\fIupdown\fP] +.br +[\-\-psk] +[\-\-rsasig] +[\-\-encrypt] +[\-\-authenticate] +[\-\-compress] +[\-\-tunnel] +[\-\-pfs] +[\-\-disablearrivalcheck] +[\-\-ipv4] +[\-\-ipv6] +[\-\-tunnelipv4] +[\-\-tunnelipv6] +[\-\-ikelifetime\ \c +\fIseconds\fP] +[\-\-ipseclifetime\ \c +\fIseconds\fP] +[\-\-rekeymargin\ \c +\fIseconds\fP] +[\-\-rekeyfuzz\ \c +\fIpercentage\fP] +[\-\-keyingtries\ \c +\fIcount\fP] +[\-\-dontrekey] +[\-\-delete] +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-keyid\ \c +\fIid\fP +[\-\-addkey] +[\-\-pubkeyrsa\ \c +\fIkey\fP] +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-myid\ \c +\fIid\fP +.HP +.ft B +ipsec whack +\-\-listen|\-\-unlisten +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-route|\-\-unroute +\-\-name\ \c +\fIconnection-name\fP +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-initiate|\-\-terminate +\-\-name\ \c +\fIconnection-name\fP +[\-\-asynchronous] +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +[\-\-tunnelipv4] +[\-\-tunnelipv6] +\-\-oppohere \fIip\(hyaddress\fP +\-\-oppothere \fIip\(hyaddress\fP +.HP +.ft B +ipsec whack +\-\-delete +\-\-name\ \c +\fIconnection-name\fP +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-deletestate\ \c +\fIstate-number\fP +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +[\-\-name\ \c +\fIconnection-name\fP] +[\-\-debug\(hynone] +[\-\-debug\(hyall] +[\-\-debug\(hyraw] +[\-\-debug\(hycrypt] +[\-\-debug\(hyparsing] +[\-\-debug\(hyemitting] +[\-\-debug\(hycontrol] +[\-\-debug\(hylifecycle] +[\-\-debug\(hyklips] +[\-\-debug\(hydns] +[\-\-debug\(hyoppo] +[\-\-debug\(hyprivate] +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-status +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.HP +.ft B +ipsec whack +\-\-shutdown +[\-\-ctlbase\ \c +\fIpath\fP] +[\-\-optionsfrom\ \c +\fIfilename\fP] +[\-\-label\ \c +\fIstring\fP] +.ft R +.hy +.ad +.SH DESCRIPTION +.BR pluto +is an IKE (``IPsec Key Exchange'') daemon. +.BR whack +is an auxiliary program to allow requests to be made to a running +.BR pluto . +.LP +.BR pluto +is used to automatically build shared ``security associations'' on a +system that has IPsec, the secure IP protocol. +In other words, +.BR pluto +can eliminate much of the work of manual keying. +The actual +secure transmission of packets is the responsibility of other parts of +the system (see +.BR KLIPS , +the companion implementation of IPsec). +\fIipsec_auto\fP(8) provides a more convenient interface to +\fBpluto\fP and \fBwhack\fP. +.SS IKE's Job +.LP +A \fISecurity Association\fP (\fISA\fP) is an agreement between two network nodes on +how to process certain traffic between them. This processing involves +encapsulation, authentication, encryption, or compression. +.LP +IKE can be deployed on a network node to negotiate Security +Associations for that node. These IKE implementations can only +negotiate with other IKE implementations, so IKE must be on each node +that is to be an endpoint of an IKE-negotiated Security Association. +No other nodes need to be running IKE. +.LP +An IKE instance (i.e. an IKE implementation on a particular network +node) communicates with another IKE instance using UDP IP packets, so +there must be a route between the nodes in each direction. +.LP +The negotiation of Security Associations requires a number of choices +that involve tradeoffs between security, convenience, trust, and +efficiency. These are policy issues and are normally specified to the +IKE instance by the system administrator. +.LP +IKE deals with two kinds of Security Associations. The first part of +a negotiation between IKE instances is to build an ISAKMP SA. An +ISAKMP SA is used to protect communication between the two IKEs. +IPsec SAs can then be built by the IKEs \- these are used to carry +protected IP traffic between the systems. +.LP +The negotiation of the ISAKMP SA is known as Phase 1. In theory, +Phase 1 can be accomplished by a couple of different exchange types, +but we only implement one called Main Mode (we don't implement +Aggressive Mode). +.LP +Any negotiation under the protection of an ISAKMP SA, including the +negotiation of IPsec SAs, is part of Phase 2. The exchange type +that we use to negotiate an IPsec SA is called Quick Mode. +.LP +IKE instances must be able to authenticate each other as part of their +negotiation of an ISAKMP SA. This can be done by several mechanisms +described in the draft standards. +.LP +IKE negotiation can be initiated by any instance with any other. If +both can find an agreeable set of characteristics for a Security +Association, and both recognize each others authenticity, they can set +up a Security Association. The standards do not specify what causes +an IKE instance to initiate a negotiation. +.LP +In summary, an IKE instance is prepared to automate the management of +Security Associations in an IPsec environment, but a number of issues +are considered policy and are left in the system administrator's hands. +.SS Pluto +.LP +\fBpluto\fP is an implementation of IKE. It runs as a daemon on a network +node. Currently, this network node must be a LINUX system running the +\fBKLIPS\fP implementation of IPsec. +.LP +\fBpluto\fP only implements a subset of IKE. This is enough for it to +interoperate with other instances of \fBpluto\fP, and many other IKE +implementations. We are working on implementing more of IKE. +.LP +The policy for acceptable characteristics for Security Associations is +mostly hardwired into the code of \fBpluto\fP (spdb.c). Eventually +this will be moved into a security policy database with reasonable +expressive power and more convenience. +.LP +\fBpluto\fP uses shared secrets or RSA signatures to authenticate +peers with whom it is negotiating. +.LP +\fBpluto\fP initiates negotiation of a Security Association when it is +manually prodded: the program \fBwhack\fP is run to trigger this. +It will also initiate a negotiation when \fBKLIPS\fP traps an outbound packet +for Opportunistic Encryption. +.LP +\fBpluto\fP implements ISAKMP SAs itself. After it has negotiated the +characteristics of an IPsec SA, it directs \fBKLIPS\fP to implement it. +It also invokes a script to adjust any firewall and issue \fIroute\fP(8) +commands to direct IP packets through \fBKLIPS\fP. +.LP +When \fBpluto\fP shuts down, it closes all Security Associations. +.SS Before Running Pluto +.LP +\fBpluto\fP runs as a daemon with userid root. Before running it, a few +things must be set up. +.LP +\fBpluto\fP requires \fBKLIPS\fP, the FreeS/WAN implementation of IPsec. +All of the components of \fBKLIPS\fP and \fBpluto\fP should be installed. +.LP +\fBpluto\fP supports multiple public networks (that is, networks +that are considered insecure and thus need to have their traffic +encrypted or authenticated). It discovers the +public interfaces to use by looking at all interfaces that are +configured (the \fB\-\-interface\fP option can be used to limit +the interfaces considered). +It does this only when \fBwhack\fP tells it to \-\-listen, +so the interfaces must be configured by then. Each interface with a name of the form +\fBipsec\fP[\fB0\fP-\fB9\fP] is taken as a \fBKLIPS\fP virtual public interface. +Another network interface with the same IP address (there should be only +one) is taken as the corresponding real public +interface. \fIifconfig\fP(8) with the \fB\-a\fP flag will show +the name and status of each network interface. +.LP +\fBpluto\fP requires a database of preshared secrets and RSA private keys. +This is described in the +.IR ipsec.secrets (5). +\fBpluto\fP is told of RSA public keys via \fBwhack\fP commands. +If the connection is Opportunistic, and no RSA public key is known, +\fBpluto\fP will attempt to fetch RSA keys using the Domain Name System. +.SS Setting up \fBKLIPS\fP for \fBpluto\fP +.LP +The most basic network topology that \fBpluto\fP supports has two security +gateways negotiating on behalf of client subnets. The diagram of RGB's +testbed is a good example (see \fIklips/doc/rgb_setup.txt\fP). +.LP +The file \fIINSTALL\fP in the base directory of this distribution +explains how to start setting up the whole system, including \fBKLIPS\fP. +.LP +Make sure that the security gateways have routes to each other. This +is usually covered by the default route, but may require issuing +.IR route (8) +commands. The route must go through a particular IP +interface (we will assume it is \fIeth0\fP, but it need not be). The +interface that connects the security gateway to its client must be a +different one. +.LP +It is necessary to issue a +.IR ipsec_tncfg (8) +command on each gateway. The required command is: + +\ \ \ ipsec tncfg \-\-attach\ \-\-virtual\ ipsec0 \-\-physical\ eth0 + +A command to set up the ipsec0 virtual interface will also need to be +run. It will have the same parameters as the command used to set up +the physical interface to which it has just been connected using +.IR ipsec_tncfg (8). +.SS ipsec.secrets file +.LP +A \fBpluto\fP daemon and another IKE daemon (for example, another instance +of \fBpluto\fP) must convince each other that they are who they are supposed +to be before any negotiation can succeed. This authentication is +accomplished by using either secrets that have been shared beforehand +(manually) or by using RSA signatures. There are other techniques, +but they have not been implemented in \fBpluto\fP. +.LP +The file \fI/etc/ipsec.secrets\fP is used to keep preshared secret keys +and RSA private keys for +authentication with other IKE daemons. For debugging, there is an +argument to the \fBpluto\fP command to use a different file. +This file is described in +.IR ipsec.secrets (5). +.SS Running Pluto +.LP +To fire up the daemon, just type \fBpluto\fP (be sure to be running as +the superuser). +The default IKE port number is 500, the UDP port assigned by IANA for IKE Daemons. +\fBpluto\fP must be run by the superuser to be able to use the UDP 500 port. +.LP +\fBpluto\fP attempts to create a lockfile with the name +\fI/var/run/pluto.pid\fP. If the lockfile cannot be created, +\fBpluto\fP exits \- this prevents multiple \fBpluto\fPs from +competing Any ``leftover'' lockfile must be removed before +\fBpluto\fP will run. \fBpluto\fP writes its pid into this file so +that scripts can find it. This lock will not function properly if it +is on an NFS volume (but sharing locks on multiple machines doesn't +make sense anyway). +.LP +\fBpluto\fP then forks and the parent exits. This is the conventional +``daemon fork''. It can make debugging awkward, so there is an option +to suppress this fork. +.LP +All logging, including diagnostics, is sent to +.IR syslog (3) +with facility=authpriv; +it decides where to put these messages (possibly in /var/log/secure). +Since this too can make debugging awkward, there is an option to +steer logging to stderr. +.LP +If the \fB\-\-perpeerlog\fP option is given, then pluto will open +a log file per connection. By default, this is in /var/log/pluto/peer, +in a subdirectory formed by turning all dot (.) [IPv4} or colon (:) +[IPv6] into slashes (/). +.LP +The base directory can be changed with the \fB\-\-perpeerlogbase\fP. +.LP +Once \fBpluto\fP is started, it waits for requests from \fBwhack\fP. +.SS Pluto's Internal State +.LP +To understand how to use \fBpluto\fP, it is helpful to understand a little +about its internal state. Furthermore, the terminology is needed to decipher +some of the diagnostic messages. +.LP +The \fI(potential) connection\fP database describes attributes of a +connection. These include the IP addresses of the hosts and client +subnets and the security characteristics desired. \fBpluto\fP +requires this information (simply called a connection) before it can +respond to a request to build an SA. Each connection is given a name +when it is created, and all references are made using this name. +.LP +During the IKE exchange to build an SA, the information about the +negotiation is represented in a \fIstate object\fP. Each state object +reflects how far the negotiation has reached. Once the negotiation is +complete and the SA established, the state object remains to represent +the SA. When the SA is terminated, the state object is discarded. +Each State object is given a serial number and this is used to refer +to the state objects in logged messages. +.LP +Each state object corresponds to a connection and can be thought of +as an instantiation of that connection. +At any particular time, there may be any number of state objects +corresponding to a particular connection. +Often there is one representing an ISAKMP SA and another representing +an IPsec SA. +.LP +\fBKLIPS\fP hooks into the routing code in a LINUX kernel. +Traffic to be processed by an IPsec SA must be directed through +\fBKLIPS\fP by routing commands. Furthermore, the processing to be +done is specified by \fIipsec eroute(8)\fP commands. +\fBpluto\fP takes the responsibility of managing both of these special +kinds of routes. +.LP +Each connection may be routed, and must be while it has an IPsec SA. +The connection specifies the characteristics of the route: the +interface on this machine, the ``gateway'' (the nexthop), +and the peer's client subnet. Two +connections may not be simultaneously routed if they are for the same +peer's client subnet but use different interfaces or gateways +(\fBpluto\fP's logic does not reflect any advanced routing capabilities). +.LP +Each eroute is associated with the state object for an IPsec SA +because it has the particular characteristics of the SA. +Two eroutes conflict if they specify the identical local +and remote clients (unlike for routes, the local clients are +taken into account). +.LP +When \fBpluto\fP needs to install a route for a connection, +it must make sure that no conflicting route is in use. If another +connection has a conflicting route, that route will be taken down, as long +as there is no IPsec SA instantiating that connection. +If there is such an IPsec SA, the attempt to install a route will fail. +.LP +There is an exception. If \fBpluto\fP, as Responder, needs to install +a route to a fixed client subnet for a connection, and there is +already a conflicting route, then the SAs using the route are deleted +to make room for the new SAs. The rationale is that the new +connection is probably more current. The need for this usually is a +product of Road Warrior connections (these are explained later; they +cannot be used to initiate). +.LP +When \fBpluto\fP needs to install an eroute for an IPsec SA (for a +state object), first the state object's connection must be routed (if +this cannot be done, the eroute and SA will not be installed). +If a conflicting eroute is already in place for another connection, +the eroute and SA will not be installed (but note that the routing +exception mentioned above may have already deleted potentially conflicting SAs). +If another IPsec +SA for the same connection already has an eroute, all its outgoing traffic +is taken over by the new eroute. The incoming traffic will still be +processed. This characteristic is exploited during rekeying. +.LP +All of these routing characteristics are expected change when +\fBKLIPS\fP is modified to use the firewall hooks in the LINUX 2.4.x +kernel. +.SS Using Whack +.LP +\fBwhack\fP is used to command a running \fBpluto\fP. +\fBwhack\fP uses a UNIX domain socket to speak to \fBpluto\fP +(by default, \fI/var/pluto.ctl\fP). +.LP +\fBwhack\fP has an intricate argument syntax. +This syntax allows many different functions to be specified. +The help form shows the usage or version information. +The connection form gives \fBpluto\fP a description of a potential connection. +The public key form informs \fBpluto\fP of the RSA public key for a potential peer. +The delete form deletes a connection description and all SAs corresponding +to it. +The listen form tells \fBpluto\fP to start or stop listening on the public interfaces +for IKE requests from peers. +The route form tells \fBpluto\fP to set up routing for a connection; +the unroute form undoes this. +The initiate form tells \fBpluto\fP to negotiate an SA corresponding to a connection. +The terminate form tells \fBpluto\fP to remove all SAs corresponding to a connection, +including those being negotiated. +The status form displays the \fBpluto\fP's internal state. +The debug form tells \fBpluto\fP to change the selection of debugging output +``on the fly''. The shutdown form tells +\fBpluto\fP to shut down, deleting all SAs. +.LP +Most options are specific to one of the forms, and will be described +with that form. There are three options that apply to all forms. +.TP +\fB\-\-ctlbase\fP\ \fIpath\fP +\fIpath\fP.ctl is used as the UNIX domain socket for talking +to \fBpluto\fP. +This option facilitates debugging. +.TP +\fB\-\-optionsfrom\fP\ \fIfilename\fP +adds the contents of the file to the argument list. +.TP +\fB\-\-label\fP\ \fIstring\fP +adds the string to all error messages generated by \fBwhack\fP. +.LP +The help form of \fBwhack\fP is self-explanatory. +.TP +\fB\-\-help\fP +display the usage message. +.TP +\fB\-\-version\fP +display the version of \fBwhack\fP. +.LP +The connection form describes a potential connection to \fBpluto\fP. +\fBpluto\fP needs to know what connections can and should be negotiated. +When \fBpluto\fP is the initiator, it needs to know what to propose. +When \fBpluto\fP is the responder, it needs to know enough to decide whether +is is willing to set up the proposed connection. +.LP +The description of a potential connection can specify a large number +of details. Each connection has a unique name. This name will appear +in a updown shell command, so it should not contain punctuation +that would make the command ill-formed. +.TP +\fB\-\-name\fP\ \fIconnection-name\fP +.LP +The topology of +a connection is symmetric, so to save space here is half a picture: + +\ \ \ client_subnet<\-\->host:ikeport<\-\->nexthop<\-\-\- + +A similar trick is used in the flags. The same flag names are used for +both ends. Those before the \fB\-\-to\fP flag describe the left side +and those afterwards describe the right side. When \fBpluto\fP attempts +to use the connection, it decides whether it is the left side or the right +side of the connection, based on the IP numbers of its interfaces. +.TP +\fB\-\-id\fP\ \fIid\fP +the identity of the end. Currently, this can be an IP address (specified +as dotted quad or as a Fully Qualified Domain Name, which will be resolved +immediately) or as a Fully Qualified Domain Name itself (prefixed by ``@'' +to signify that it should not be resolved), or as user@FQDN, or as the +magic value \fB%myid\fP. +\fBPluto\fP only authenticates the identity, and does not use it for +addressing, so, for example, an IP address need not be the one to which +packets are to be sent. If the option is absent, the +identity defaults to the IP address specified by \fB\-\-host\fP. +\fB%myid\fP allows the identity to be separately specified (by the \fBpluto\fP or \fBwhack\fP option \fB\-\-myid\fP +or by the \fBipsec.conf\fP(5) \fBconfig setup\fP parameter \fPmyid\fP). +Otherwise, \fBpluto\fP tries to guess what \fB%myid\fP should stand for: +the IP address of \fB%defaultroute\fP, if it is supported by a suitable TXT record in the reverse domain for that IP address, +or the system's hostname, if it is supported by a suitable TXT record in its forward domain. +.\" The identity is transmitted in the IKE protocol, and is what is authenticated. +.TP +\fB\-\-host\fP\ \fIip\(hyaddress\fP +.TP +\fB\-\-host\fP\ \fB%any\fP +.TP +\fB\-\-host\fP\ \fB%opportunistic\fP +the IP address of the end (generally the public interface). +If \fBpluto\fP is to act as a responder +for IKE negotiations initiated from unknown IP addresses (the +``Road Warrior'' case), the +IP address should be specified as \fB%any\fP (currently, +the obsolete notation \fB0.0.0.0\fP is also accepted for this). +If \fBpluto\fP is to opportunistically initiate the connection, +use \fB%opportunistic\fP +.TP +\fB\-\-ikeport\fP\ \fIport\(hynumber\fP +the UDP port that IKE listens to on that host. The default is 500. +(\fBpluto\fP on this machine uses the port specified by its own command +line argument, so this only affects where \fBpluto\fP sends messages.) +.TP +\fB\-\-nexthop\fP\ \fIip\(hyaddress\fP +where to route packets for the peer's client (presumably for the peer too, +but it will not be used for this). +When \fBpluto\fP installs an IPsec SA, it issues a route command. +It uses the nexthop as the gateway. +The default is the peer's IP address (this can be explicitly written as +\fB%direct\fP; the obsolete notation \fB0.0.0.0\fP is accepted). +This option is necessary if \fBpluto\fP's host's interface used for sending +packets to the peer is neither point-to-point nor directly connected to the +peer. +.TP +\fB\-\-client\fP\ \fIsubnet\fP +the subnet for which the IPsec traffic will be destined. If not specified, +the host will be the client. +The subnet can be specified in any of the forms supported by \fIipsec_atosubnet\fP(3). +The general form is \fIaddress\fP/\fImask\fP. The \fIaddress\fP can be either +a domain name or four decimal numbers (specifying octets) separated by dots. +The most convenient form of the \fImask\fP is a decimal integer, specifying +the number of leading one bits in the mask. So, for example, 10.0.0.0/8 +would specify the class A network ``Net 10''. +.TP +\fB\-\-dnskeyondemand]\fP +specifies that when an RSA public key is needed to authenticate this +host, and it isn't already known, fetch it from DNS. +.TP +\fB\-\-updown\fP\ \fIupdown\fP +specifies an external shell command to be run whenever \fBpluto\fP +brings up or down a connection. +The script is used to build a shell command, so it may contain positional +parameters, but ought not to have punctuation that would cause the +resulting command to be ill-formed. +The default is \fIipsec _updown\fP. +.TP +\fB\-\-to\fP +separates the specification of the left and right ends of the connection. +.LP +The potential connection description also specifies characteristics of +rekeying and security. +.TP +\fB\-\-psk\fP +Propose and allow preshared secret authentication for IKE peers. This authentication +requires that each side use the same secret. May be combined with \fB\-\-rsasig\fP; +at least one must be specified. +.TP +\fB\-\-rsasig\fP +Propose and allow RSA signatures for authentication of IKE peers. This authentication +requires that each side have have a private key of its own and know the +public key of its peer. May be combined with \fB\-\-psk\fP; +at least one must be specified. +.TP +\fB\-\-encrypt\fP +All proposed or accepted IPsec SAs will include non-null ESP. +The actual choices of transforms are wired into \fBpluto\fP. +.TP +\fB\-\-authenticate\fP +All proposed IPsec SAs will include AH. +All accepted IPsec SAs will include AH or ESP with authentication. +The actual choices of transforms are wired into \fBpluto\fP. +Note that this has nothing to do with IKE authentication. +.TP +\fB\-\-compress\fP +All proposed IPsec SAs will include IPCOMP (compression). +This will be ignored if KLIPS is not configured with IPCOMP support. +.TP +\fB\-\-tunnel\fP +the IPsec SA should use tunneling. Implicit if the SA is for clients. +Must only be used with \fB\-\-authenticate\fP or \fB\-\-encrypt\fP. +.TP +\fB\-\-ipv4\fP +The host addresses will be interpreted as IPv4 addresses. This is the +default. Note that for a connection, all host addresses must be of +the same Address Family (IPv4 and IPv6 use different Address Families). +.TP +\fB\-\-ipv6\fP +The host addresses (including nexthop) will be interpreted as IPv6 addresses. +Note that for a connection, all host addresses must be of +the same Address Family (IPv4 and IPv6 use different Address Families). +.TP +\fB\-\-tunnelipv4\fP +The client addresses will be interpreted as IPv4 addresses. The default is +to match what the host will be. This does not imply \fB\-\-tunnel\fP so the +flag can be safely used when no tunnel is actually specified. +Note that for a connection, all tunnel addresses must be of the same +Address Family. +.TP +\fB\-\-tunnelipv6\fP +The client addresses will be interpreted as IPv6 addresses. The default is +to match what the host will be. This does not imply \fB\-\-tunnel\fP so the +flag can be safely used when no tunnel is actually specified. +Note that for a connection, all tunnel addresses must be of the same +Address Family. +.TP +\fB\-\-pfs\fP +There should be Perfect Forward Secrecy \- new keying material will +be generated for each IPsec SA rather than being derived from the ISAKMP +SA keying material. +Since the group to be used cannot be negotiated (a dubious feature of the +standard), \fBpluto\fP will propose the same group that was used during Phase 1. +We don't implement a stronger form of PFS which would require that the +ISAKMP SA be deleted after the IPSEC SA is negotiated. +.TP +\fB\-\-disablearrivalcheck\fP +If the connection is a tunnel, allow packets arriving through the tunnel +to have any source and destination addresses. +.LP +If none of the \fB\-\-encrypt\fP, \fB\-\-authenticate\fP, \fB\-\-compress\fP, +or \fB\-\-pfs\fP flags is given, the initiating the connection will +only build an ISAKMP SA. For such a connection, client subnets have +no meaning and must not be specified. +.LP +More work is needed to allow for flexible policies. Currently +policy is hardwired in the source file spdb.c. The ISAKMP SAs may use +Oakley groups MODP1024 and MODP1536; 3DES encryption; SHA1-96 +and MD5-96 authentication. The IPsec SAs may use 3DES and +MD5-96 or SHA1-96 for ESP, or just MD5-96 or SHA1-96 for AH. +IPCOMP Compression is always Deflate. +.TP +\fB\-\-ikelifetime\fP\ \fIseconds\fP +how long \fBpluto\fP will propose that an ISAKMP SA be allowed to live. +The default is 10800 (three hours) and the maximum is 86400 (one day). +This option will not affect what is accepted. +\fBpluto\fP will reject proposals that exceed the maximum. +.TP +\fB\-\-ipseclifetime\fP\ \fIseconds\fP +how long \fBpluto\fP will propose that an IPsec SA be allowed to live. +The default is 3600 (one hour) and the maximum is 86400 (one day). +This option will not affect what is accepted. +\fBpluto\fP will reject proposals that exceed the maximum. +.TP +\fB\-\-rekeymargin\fP\ \fIseconds\fP +how long before an SA's expiration should \fBpluto\fP try to negotiate +a replacement SA. This will only happen if \fBpluto\fP was the initiator. +The default is 540 (nine minutes). +.TP +\fB\-\-rekeyfuzz\fP\ \fIpercentage\fP +maximum size of random component to add to rekeymargin, expressed as +a percentage of rekeymargin. \fBpluto\fP will select a delay uniformly +distributed within this range. By default, the percentage will be 100. +If greater determinism is desired, specify 0. It may be appropriate +for the percentage to be much larger than 100. +.TP +\fB\-\-keyingtries\fP\ \fIcount\fP +how many times \fBpluto\fP should try to negotiate an SA, +either for the first time or for rekeying. +A value of 0 is interpreted as a very large number: never give up. +The default is three. +.TP +\fB\-\-dontrekey\fP +A misnomer. +Only rekey a connection if we were the Initiator and there was recent +traffic on the existing connection. +This applies to Phase 1 and Phase 2. +This is currently the only automatic way for a connection to terminate. +It may be useful with Road Warrior or Opportunistic connections. +.br +Since SA lifetime negotiation is take-it-or-leave it, a Responder +normally uses the shorter of the negotiated or the configured lifetime. +This only works because if the lifetime is shorter than negotiated, +the Responder will rekey in time so that everything works. +This interacts badly with \fB\-\-dontrekey\fP. In this case, +the Responder will end up rekeying to rectify a shortfall in an IPsec SA +lifetime; for an ISAKMP SA, the Responder will accept the negotiated +lifetime. +.TP +\fB\-\-delete\fP +when used in the connection form, it causes any previous connection +with this name to be deleted before this one is added. Unlike a +normal delete, no diagnostic is produced if there was no previous +connection to delete. Any routing in place for the connection is undone. +.LP +The delete form deletes a named connection description and any +SAs established or negotiations initiated using this connection. +Any routing in place for the connection is undone. +.TP +\fB\-\-delete\fP +.TP +\fB\-\-name\fP\ \fIconnection-name\fP +.LP +The deletestate form deletes the state object with the specified serial number. +This is useful for selectively deleting instances of connections. +.TP +\fB\-\-deletestate\fP\ \fIstate-number\fP +.LP +The route form of the \fBwhack\fP command tells \fBpluto\fP to set up +routing for a connection. +Although like a traditional route, it uses an ipsec device as a +virtual interface. +Once routing is set up, no packets will be +sent ``in the clear'' to the peer's client specified in the connection. +A TRAP shunt eroute will be installed; if outbound traffic is caught, +Pluto will initiate the connection. +An explicit \fBwhack\fP route is not always needed: if it hasn't been +done when an IPsec SA is being installed, one will be automatically attempted. +.LP +When a routing is attempted for a connection, there must not already +be a routing for a different connection with the same subnet but different +interface or destination, or if +there is, it must not be being used by an IPsec SA. Otherwise the +attempt will fail. +.TP +\fB\-\-route\fP +.TP +\fB\-\-name\fP\ \fIconnection-name\fP +.LP +The unroute form of the \fBwhack\fP command tells \fBpluto\fP to undo +a routing. \fBpluto\fP will refuse if an IPsec SA is using the connection. +If another connection is sharing the same routing, it will be left in place. +Without a routing, packets will be sent without encryption or authentication. +.TP +\fB\-\-unroute\fP +.TP +\fB\-\-name\fP\ \fIconnection-name\fP +.LP +The initiate form tells \fBpluto\fP to initiate a negotiation with another +\fBpluto\fP (or other IKE daemon) according to the named connection. +Initiation requires a route that \fB\-\-route\fP would provide; +if none is in place at the time an IPsec SA is being installed, +\fBpluto\fP attempts to set one up. +.TP +\fB\-\-initiate\fP +.TP +\fB\-\-name\fP\ \fIconnection-name\fP +.TP +\fB\-\-asynchronous +.LP +The initiate form of the \fBwhack\fP command will relay back from +\fBpluto\fP status information via the UNIX domain socket (unless +\-\-asynchronous is specified). The status information is meant to +look a bit like that from \fBFTP\fP. Currently \fBwhack\fP simply +copies this to stderr. When the request is finished (eg. the SAs are +established or \fBpluto\fP gives up), \fBpluto\fP closes the channel, +causing \fBwhack\fP to terminate. +.LP +The opportunistic initiate form is mainly used for debugging. +.TP +\fB\-\-tunnelipv4\fP +.TP +\fB\-\-tunnelipv6\fP +.TP +\fB\-\-oppohere\fP\ \fIip-address\fP +.TP +\fB\-\-oppothere\fP\ \fIip-address\fP +.LP +This will cause \fBpluto\fP to attempt to opportunistically initiate a +connection from here to the there, even if a previous attempt +had been made. +The whack log will show the progress of this attempt. +.LP +The terminate form tells \fBpluto\fP to delete any SAs that use the specified +connection and to stop any negotiations in process. +It does not prevent new negotiations from starting (the delete form +has this effect). +.TP +\fB\-\-terminate\fP +.TP +\fB\-\-name\fP\ \fIconnection-name\fP +.LP +The public key for informs \fBpluto\fP of the RSA public key for a potential peer. +Private keys must be kept secret, so they are kept in +.IR ipsec.secrets (5). +.TP +\fB\-\-keyid\ \fP\fIid\fP +specififies the identity of the peer for which a public key should be used. +Its form is identical to the identity in the connection. +If no public key is specified, \fBpluto\fP attempts to find KEY records +from DNS for the id (if a FQDN) or through reverse lookup (if an IP address). +Note that there several interesting ways in which this is not secure. +.TP +\fB\-\-addkey\fP +specifies that the new key is added to the collection; otherwise the +new key replaces any old ones. +.TP +\fB\-\-pubkeyrsa\ \fP\fIkey\fP +specifies the value of the RSA public key. It is a sequence of bytes +as described in RFC 2537 ``RSA/MD5 KEYs and SIGs in the Domain Name System (DNS)''. +It is denoted in a way suitable for \fIipsec_ttodata\fP(3). +For example, a base 64 numeral starts with 0s. +.LP +The listen form tells \fBpluto\fP to start listening for IKE requests +on its public interfaces. To avoid race conditions, it is normal to +load the appropriate connections into \fBpluto\fP before allowing it +to listen. If \fBpluto\fP isn't listening, it is pointless to +initiate negotiations, so it will refuse requests to do so. Whenever +the listen form is used, \fBpluto\fP looks for public interfaces and +will notice when new ones have been added and when old ones have been +removed. This is also the trigger for \fBpluto\fP to read the +\fIipsec.secrets\fP file. So listen may useful more than once. +.TP +\fB\-\-listen\fP +start listening for IKE traffic on public interfaces. +.TP +\fB\-\-unlisten\fP +stop listening for IKE traffic on public interfaces. +.LP +The status form will display information about the internal state of +\fBpluto\fP: information about each potential connection, about +each state object, and about each shunt that \fBpluto\fP is managing +without an associated connection. +.TP +\fB\-\-status\fP +.LP +The shutdown form is the proper way to shut down \fBpluto\fP. +It will tear down the SAs on this machine that \fBpluto\fP has negotiated. +It does not inform its peers, so the SAs on their machines remain. +.TP +\fB\-\-shutdown\fP +.SS Examples +.LP +It would be normal to start \fBpluto\fP in one of the system initialization +scripts. It needs to be run by the superuser. Generally, no arguments are needed. +To run in manually, the superuser can simply type + +\ \ \ ipsec pluto + +The command will immediately return, but a \fBpluto\fP process will be left +running, waiting for requests from \fBwhack\fP or a peer. +.LP +Using \fBwhack\fP, several potential connections would be described: +.HP +.na +\ \ \ ipsec whack \-\-name\ silly +\-\-host\ 127.0.0.1 \-\-to \-\-host\ 127.0.0.2 +\-\-ikelifetime\ 900 \-\-ipseclifetime\ 800 \-\-keyingtries\ 3 +.ad +.LP +Since this silly connection description specifies neither encryption, +authentication, nor tunneling, it could only be used to establish +an ISAKMP SA. +.HP +.na +\ \ \ ipsec whack \-\-name\ secret \-\-host\ 10.0.0.1 \-\-client\ 10.0.1.0/24 +\-\-to \-\-host\ 10.0.0.2 \-\-client\ 10.0.2.0/24 +\-\-encrypt +.ad +.LP +This is something that must be done on both sides. If the other +side is \fBpluto\fP, the same \fBwhack\fP command could be used on it +(the command syntax is designed to not distinguish which end is ours). +.LP +Now that the connections are specified, \fBpluto\fP is ready to handle +requests and replies via the public interfaces. We must tell it to discover +those interfaces and start accepting messages from peers: + +\ \ \ ipsec whack \-\-listen +.LP +If we don't immediately wish to bring up a secure connection between +the two clients, we might wish to prevent insecure traffic. +The routing form asks \fBpluto\fP to cause the packets sent from +our client to the peer's client to be routed through the ipsec0 +device; if there is no SA, they will be discarded: + +\ \ \ ipsec whack \-\-route secret +.LP +Finally, we are ready to get \fBpluto\fP to initiate negotiation +for an IPsec SA (and implicitly, an ISAKMP SA): + +\ \ \ ipsec whack \-\-initiate\ \-\-name\ secret + +A small log of interesting events will appear on standard output +(other logging is sent to syslog). +.LP +\fBwhack\fP can also be used to terminate \fBpluto\fP cleanly, tearing down +all SAs that it has negotiated. + +\ \ \ ipsec whack \-\-shutdown + +Notification of any IPSEC SA deletion, but not ISAKMP SA deletion +is sent to the peer. Unfortunately, such Notification is not reliable. +Furthermore, \fBpluto\fP itself ignores Notifications. +.SS The updown command +.LP +Whenever \fBpluto\fP brings a connection up or down, it invokes +the updown command. This command is specified using the \fB\-\-updown\fP +option. This allows for customized control over routing and firewall manipulation. +.LP +The updown is invoked for five different operations. Each of +these operations can be for our client subnet or for our host itself. +.TP +\fBprepare-host\fP or \fBprepare-client\fP +is run before bringing up a new connection if no other connection +with the same clients is up. Generally, this is useful for deleting a +route that might have been set up before \fBpluto\fP was run or +perhaps by some agent not known to \fBpluto\fP. +.TP +\fBroute-host\fP or \fBroute-client\fP +is run when bringing up a connection for a new peer client subnet +(even if \fBprepare-host\fP or \fBprepare-client\fP was run). The +command should install a suitable route. Routing decisions are based +only on the destination (peer's client) subnet address, unlike eroutes +which discriminate based on source too. +.TP +\fBunroute-host\fP or \fBunroute-client\fP +is run when bringing down the last connection for a particular peer +client subnet. It should undo what the \fBroute-host\fP or \fBroute-client\fP +did. +.TP +\fBup-host\fP or \fBup-client\fP +is run when bringing up a tunnel eroute with a pair of client subnets +that does not already have a tunnel eroute. +This command should install firewall rules as appropriate. +It is generally a good idea to allow IKE messages (UDP port 500) +travel between the hosts. +.TP +\fBdown-host\fP or \fBdown-client\fP +is run when bringing down the eroute for a pair of client subnets. +This command should delete firewall rules as appropriate. Note that +there may remain some inbound IPsec SAs with these client subnets. +.LP +The script is passed a large number of environment variables to specify +what needs to be done. +.TP +\fBPLUTO_VERSION\fP +indicates what version of this interface is being used. This document +describes version 1.1. This is upwardly compatible with version 1.0. +.TP +\fBPLUTO_VERB\fP +specifies the name of the operation to be performed +(\fBprepare-host\fP,r \fBprepare-client\fP, +\fBup-host\fP, \fBup-client\fP, +\fBdown-host\fP, or \fBdown-client\fP). If the address family for +security gateway to security gateway communications is IPv6, then +a suffix of -v6 is added to the verb. +.TP +\fBPLUTO_CONNECTION\fP +is the name of the connection for which we are routing. +.TP +\fBPLUTO_NEXT_HOP\fP +is the next hop to which packets bound for the peer must be sent. +.TP +\fBPLUTO_INTERFACE\fP +is the name of the ipsec interface to be used. +.TP +\fBPLUTO_ME\fP +is the IP address of our host. +.TP +\fBPLUTO_MY_CLIENT\fP +is the IP address / count of our client subnet. +If the client is just the host, this will be the host's own IP address / max +(where max is 32 for IPv4 and 128 for IPv6). +.TP +\fBPLUTO_MY_CLIENT_NET\fP +is the IP address of our client net. +If the client is just the host, this will be the host's own IP address. +.TP +\fBPLUTO_MY_CLIENT_MASK\fP +is the mask for our client net. +If the client is just the host, this will be 255.255.255.255. +.TP +\fBPLUTO_PEER\fP +is the IP address of our peer. +.TP +\fBPLUTO_PEER_CLIENT\fP +is the IP address / count of the peer's client subnet. +If the client is just the peer, this will be the peer's own IP address / max +(where max is 32 for IPv4 and 128 for IPv6). +.TP +\fBPLUTO_PEER_CLIENT_NET\fP +is the IP address of the peer's client net. +If the client is just the peer, this will be the peer's own IP address. +.TP +\fBPLUTO_PEER_CLIENT_MASK\fP +is the mask for the peer's client net. +If the client is just the peer, this will be 255.255.255.255. +.LP +All output sent by the script to stderr or stdout is logged. The +script should return an exit status of 0 if and only if it succeeds. +.LP +\fBPluto\fP waits for the script to finish and will not do any other +processing while it is waiting. +The script may assume that \fBpluto\fP will not change anything +while the script runs. +The script should avoid doing anything that takes much time and it +should not issue any command that requires processing by \fBpluto\fP. +Either of these activities could be performed by a background +subprocess of the script. +.SS Rekeying +.LP +When an SA that was initiated by \fBpluto\fP has only a bit of +lifetime left, +\fBpluto\fP will initiate the creation of a new SA. This applies to +ISAKMP and IPsec SAs. +The rekeying will be initiated when the SA's remaining lifetime is +less than the rekeymargin plus a random percentage, between 0 and +rekeyfuzz, of the rekeymargin. +.LP +Similarly, when an SA that was initiated by the peer has only a bit of +lifetime left, \fBpluto\fP will try to initiate the creation of a +replacement. +To give preference to the initiator, this rekeying will only be initiated +when the SA's remaining lifetime is half of rekeymargin. +If rekeying is done by the responder, the roles will be reversed: the +responder for the old SA will be the initiator for the replacement. +The former initiator might also initiate rekeying, so there may +be redundant SAs created. +To avoid these complications, make sure that rekeymargin is generous. +.LP +One risk of having the former responder initiate is that perhaps +none of its proposals is acceptable to the former initiator +(they have not been used in a successful negotiation). +To reduce the chances of this happening, and to prevent loss of security, +the policy settings are taken from the old SA (this is the case even if +the former initiator is initiating). +These may be stricter than those of the connection. +.LP +\fBpluto\fP will not rekey an SA if that SA is not the most recent of its +type (IPsec or ISAKMP) for its potential connection. +This avoids creating redundant SAs. +.LP +The random component in the rekeying time (rekeyfuzz) is intended to +make certain pathological patterns of rekeying unstable. If both +sides decide to rekey at the same time, twice as many SAs as necessary +are created. This could become a stable pattern without the +randomness. +.LP +Another more important case occurs when a security gateway has SAs +with many other security gateways. Each of these connections might +need to be rekeyed at the same time. This would cause a high peek +requirement for resources (network bandwidth, CPU time, entropy for +random numbers). The rekeyfuzz can be used to stagger the rekeying +times. +.LP +Once a new set of SAs has been negotiated, \fBpluto\fP will never send +traffic on a superseded one. Traffic will be accepted on an old SA +until it expires. +.SS Selecting a Connection When Responding: Road Warrior Support +.LP +When \fBpluto\fP receives an initial Main Mode message, it needs to +decide which connection this message is for. It picks based solely on +the source and destination IP addresses of the message. There might +be several connections with suitable IP addresses, in which case one +of them is arbitrarily chosen. (The ISAKMP SA proposal contained in +the message could be taken into account, but it is not.) +.LP +The ISAKMP SA is negotiated before the parties pass further +identifying information, so all ISAKMP SA characteristics specified in +the connection description should be the same for every connection +with the same two host IP addresses. At the moment, the only +characteristic that might differ is authentication method. +.LP +Up to this point, +all configuring has presumed that the IP addresses +are known to all parties ahead of time. This will not work +when either end is mobile (or assigned a dynamic IP address for other +reasons). We call this situation ``Road Warrior''. It is fairly tricky +and has some important limitations, most of which are features of +the IKE protocol. +.LP +Only the initiator may be mobile: +the initiator may have an IP number unknown to the responder. When +the responder doesn't recognize the IP address on the first Main Mode +packet, it looks for a connection with itself as one end and \fB%any\fP +as the other. +If it cannot find one, it refuses to negotiate. If it +does find one, it creates a temporary connection that is a duplicate +except with the \fB%any\fP replaced by the source IP address from the +packet; if there was no identity specified for the peer, the new IP +address will be used. +.LP +When \fBpluto\fP is using one of these temporary connections and +needs to find the preshared secret or RSA private key in \fIipsec.secrets\fP, +and and the connection specified no identity for the peer, \fB%any\fP +is used as its identity. After all, the real IP address was apparently +unknown to the configuration, so it is unreasonable to require that +it be used in this table. +.LP +Part way into the Phase 1 (Main Mode) negotiation using one of these +temporary connection descriptions, \fBpluto\fP will be receive an +Identity Payload. At this point, \fBpluto\fP checks for a more +appropriate connection, one with an identity for the peer that matches +the payload but which would use the same keys so-far used for +authentication. If it finds one, it will switch to using this better +connection (or a temporary derived from this, if it has \fB%any\fP +for the peer's IP address). It may even turn out that no connection +matches the newly discovered identity, including the current connection; +if so, \fBpluto\fP terminates negotiation. +.LP +Unfortunately, if preshared secret authentication is being used, the +Identity Payload is encrypted using this secret, so the secret must be +selected by the responder without knowing this payload. This +limits there to being at most one preshared secret for all Road Warrior +systems connecting to a host. RSA Signature authentications does not +require that the responder know how to select the initiator's public key +until after the initiator's Identity Payload is decoded (using the +responder's private key, so that must be preselected). +.LP +When \fBpluto\fP is responding to a Quick Mode negotiation via one of these +temporary connection descriptions, it may well find that the subnets +specified by the initiator don't match those in the temporary +connection description. If so, it will look for a connection with +matching subnets, its own host address, a peer address of \fB%any\fP +and matching identities. +If it finds one, a new temporary connection is derived from this one +and used for the Quick Mode negotiation of IPsec SAs. If it does not +find one, \fBpluto\fP terminates negotiation. +.LP +Be sure to specify an appropriate nexthop for the responder +to send a message to the initiator: \fBpluto\fP has no way of guessing +it (if forwarding isn't required, use an explicit \fB%direct\fP as the nexthop +and the IP address of the initiator will be filled in; the obsolete +notation \fB0.0.0.0\fP is still accepted). +.LP +\fBpluto\fP has no special provision for the initiator side. The current +(possibly dynamic) IP address and nexthop must be used in defining +connections. These must be +properly configured each time the initiator's IP address changes. +\fBpluto\fP has no mechanism to do this automatically. +.LP +Although we call this Road Warrior Support, it could also be used to +support encrypted connections with anonymous initiators. The +responder's organization could announce the preshared secret that would be used +with unrecognized initiators and let anyone connect. Of course the initiator's +identity would not be authenticated. +.LP +If any Road Warrior connections are supported, \fBpluto\fP cannot +reject an exchange initiated by an unknown host until it has +determined that the secret is not shared or the signature is invalid. +This must await the +third Main Mode message from the initiator. If no Road Warrior +connection is supported, the first message from an unknown source +would be rejected. This has implications for ease of debugging +configurations and for denial of service attacks. +.LP +Although a Road Warrior connection must be initiated by the mobile +side, the other side can and will rekey using the temporary connection +it has created. If the Road Warrior wishes to be able to disconnect, +it is probably wise to set \fB\-\-keyingtries\fP to 1 in the +connection on the non-mobile side to prevent it trying to rekey the +connection. Unfortunately, there is no mechanism to unroute the +connection automatically. +.SS Debugging +.LP +\fBpluto\fP accepts several optional arguments, useful mostly for debugging. +Except for \fB\-\-interface\fP, each should appear at most once. +.TP +\fB\-\-interface\fP \fIinterfacename\fP +specifies that the named real public network interface should be considered. +The interface name specified should not be \fBipsec\fP\fIN\fP. +If the option doesn't appear, all interfaces are considered. +To specify several interfaces, use the option once for each. +One use of this option is to specify which interface should be used +when two or more share the same IP address. +.TP +\fB\-\-ikeport\fP \fIport-number\fP +changes the UDP port that \fBpluto\fP will use +(default, specified by IANA: 500) +.TP +\fB\-\-ctlbase\fP \fIpath\fP +basename for control files. +\fIpath\fP.ctl is the socket through which \fBwhack\fP communicates with +\fBpluto\fP. +\fIpath\fP.pid is the lockfile to prevent multiple \fBpluto\fP instances. +The default is \fI/var/run/pluto\fP). +.TP +\fB\-\-secretsfile\fP \fIfile\fP +specifies the file for authentication secrets +(default: \fI/etc/ipsec.secrets\fP). +This name is subject to ``globbing'' as in \fIsh\fP(1), +so every file with a matching name is processed. +Quoting is generally needed to prevent the shell from doing the globbing. +.TP +\fB\-\-adns\fP \fIpathname\fP +.TP +\fB\-\-lwdnsq\fP \fIpathname\fP +specifies where to find \fBpluto\fP's helper program for asynchronous DNS lookup. +\fBpluto\fP can be built to use one of two helper programs: \fB_pluto_adns\fP +or \fBlwdnsq\fP. You must use the program for which it was built. +By default, \fBpluto\fP will look for the program in +\fB$IPSEC_DIR\fP (if that environment variable is defined) or, failing that, +in the same directory as \fBpluto\fP. +.TP +\fB\-\-nofork\fP +disable ``daemon fork'' (default is to fork). In addition, after the +lock file and control socket are created, print the line ``Pluto +initialized'' to standard out. +.TP +\fB\-\-noklips\fP +don't actually implement negotiated IPsec SAs +.TP +\fB\-\-uniqueids\fP +if this option has been selected, whenever a new ISAKMP SA is +established, any connection with the same Peer ID but a different +Peer IP address is unoriented (causing all its SAs to be deleted). +This helps clean up dangling SAs when a connection is lost and +then regained at another IP address. +.TP +\fB\-\-stderrlog\fP +log goes to standard out {default is to use \fIsyslogd\fP(8)) +.LP +For example +.TP +pluto \-\-secretsfile\ ipsec.secrets \-\-ctlbase\ pluto.base \-\-ikeport\ 8500 \-\-nofork \-\-noklips \-\-stderrlog +.LP +lets one test \fBpluto\fP without using the superuser account. +.LP +\fBpluto\fP is willing to produce a prodigious amount of debugging +information. To do so, it must be compiled with \-DDEBUG. There are +several classes of debugging output, and \fBpluto\fP may be directed to +produce a selection of them. All lines of +debugging output are prefixed with ``|\ '' to distinguish them from error +messages. +.LP +When \fBpluto\fP is invoked, it may be given arguments to specify +which classes to output. The current options are: +.TP +\fB\-\-debug-raw\fP +show the raw bytes of messages +.TP +\fB\-\-debug-crypt\fP +show the encryption and decryption of messages +.TP +\fB\-\-debug-parsing\fP +show the structure of input messages +.TP +\fB\-\-debug-emitting\fP +show the structure of output messages +.TP +\fB\-\-debug-control\fP +show \fBpluto\fP's decision making +.TP +\fB\-\-debug-lifecycle\fP +[this option is temporary] log more detail of lifecycle of SAs +.TP +\fB\-\-debug-klips\fP +show \fBpluto\fP's interaction with \fBKLIPS\fP +.TP +\fB\-\-debug-dns\fP +show \fBpluto\fP's interaction with \fBDNS\fP for KEY and TXT records +.TP +\fB\-\-debug-oppo\fP +show why \fBpluto\fP didn't find a suitable DNS TXT record to authorize opportunistic initiation +.TP +\fB\-\-debug-all\fP +all of the above +.TP +\fB\-\-debug-private\fP +allow debugging output with private keys. +.TP +\fB\-\-debug-none\fP +none of the above +.LP +The debug form of the +\fBwhack\fP command will change the selection in a running +\fBpluto\fP. +If a connection name is specified, the flags are added whenever +\fBpluto\fP has identified that it is dealing with that connection. +Unfortunately, this is often part way into the operation being observed. +.LP +For example, to start a \fBpluto\fP with a display of the structure of input +and output: +.IP +pluto \-\-debug-emitting \-\-debug-parsing +.LP +To later change this \fBpluto\fP to only display raw bytes: +.IP +whack \-\-debug-raw +.LP +For testing, SSH's IKE test page is quite useful: +.IP +\fIhttp://isakmp-test.ssh.fi/\fP +.LP +Hint: ISAKMP SAs are often kept alive by IKEs even after the IPsec SA +is established. This allows future IPsec SA's to be negotiated +directly. If one of the IKEs is restarted, the other may try to use +the ISAKMP SA but the new IKE won't know about it. This can lead to +much confusion. \fBpluto\fP is not yet smart enough to get out of such a +mess. +.SS Pluto's Behaviour When Things Go Wrong +.LP +When \fBpluto\fP doesn't understand or accept a message, it just +ignores the message. It is not yet capable of communicating the +problem to the other IKE daemon (in the future it might use +Notifications to accomplish this in many cases). It does log a diagnostic. +.LP +When \fBpluto\fP gets no response from a message, it resends the same +message (a message will be sent at most three times). This is +appropriate: UDP is unreliable. +.LP +When pluto gets a message that it has already seen, there are many +cases when it notices and discards it. This too is appropriate for UDP. +.LP +Combine these three rules, and you can explain many apparently +mysterious behaviours. In a \fBpluto\fP log, retrying isn't usually the +interesting event. The critical thing is either earlier (\fBpluto\fP +got a message which it didn't like and so ignored, so it was still +awaiting an acceptable message and got impatient) or on the other +system (\fBpluto\fP didn't send a reply because it wasn't happy with +the previous message). +.SS Notes +.LP +If \fBpluto\fP is compiled without \-DKLIPS, it negotiates Security +Associations but never ask the kernel to put them in place and never +makes routing changes. This allows \fBpluto\fP to be tested on systems +without \fBKLIPS\fP, but makes it rather useless. +.LP +Each IPsec SA is assigned an SPI, a 32-bit number used to refer to the SA. +The IKE protocol lets the destination of the SA choose the SPI. +The range 0 to 0xFF is reserved for IANA. +\fBPluto\fP also avoids choosing an SPI in the range 0x100 to 0xFFF, +leaving these SPIs free for manual keying. +Remember that the peer, if not \fBpluto\fP, may well chose +SPIs in this range. +.SS Policies +.LP +This catalogue of policies may be of use when trying to configure +\fBPluto\fP and another IKE implementation to interoperate. +.LP +In Phase 1, only Main Mode is supported. We are not sure that +Aggressive Mode is secure. For one thing, it does not support +identity protection. It may allow more severe Denial Of Service +attacks. +.LP +No Informational Exchanges are supported. These are optional and +since their delivery is not assured, they must not matter. +It is the case that some IKE implementations won't interoperate +without Informational Exchanges, but we feel they are broken. +.LP +No Informational Payloads are supported. These are optional, but +useful. It is of concern that these payloads are not authenticated in +Phase 1, nor in those Phase 2 messages authenticated with HASH(3). +.IP \(bu \w'\(bu\ 'u +Diffie Hellman Groups MODP 1024 and MODP 1536 (2 and 5) +are supported. +Group MODP768 (1) is not supported because it is too weak. +.IP \(bu +Host authetication can be done by RSA Signatures or Pre-Shared +Secrets. +.IP \(bu +3DES CBC (Cypher Block Chaining mode) is the only encryption +supported, both for ISAKMP SAs and IPSEC SAs. +.IP \(bu +MD5 and SHA1 hashing are supported for packet authentication in both +kinds of SAs. +.IP \(bu +The ESP, AH, or AH plus ESP are supported. If, and only if, AH and +ESP are combined, the ESP need not have its own authentication +component. The selection is controlled by the \-\-encrypt and +\-\-authenticate flags. +.IP \(bu +Each of these may be combined with IPCOMP Deflate compression, +but only if the potential connection specifies compression and only +if KLIPS is configured with IPCOMP support. +.IP \(bu +The IPSEC SAs may be tunnel or transport mode, where appropriate. +The \-\-tunnel flag controls this when \fBpluto\fP is initiating. +.IP \(bu +When responding to an ISAKMP SA proposal, the maximum acceptable +lifetime is eight hours. The default is one hour. There is no +minimum. The \-\-ikelifetime flag controls this when \fBpluto\fP +is initiating. +.IP \(bu +When responding to an IPSEC SA proposal, the maximum acceptable +lifetime is one day. The default is eight hours. There is no +minimum. The \-\-ipseclifetime flag controls this when \fBpluto\fP +is initiating. +.IP \(bu +PFS is acceptable, and will be proposed if the \-\-pfs flag was +specified. The DH group proposed will be the same as negotiated for +Phase 1. +.SH SIGNALS +.LP +\fBPluto\fP responds to \fBSIGHUP\fP by issuing a suggestion that ``\fBwhack\fP +\-\-listen'' might have been intended. +.LP +\fBPluto\fP exits when it recieves \fBSIGTERM\fP. +.SH EXIT STATUS +.LP +\fBpluto\fP normally forks a daemon process, so the exit status is +normally a very preliminary result. +.TP +0 +means that all is OK so far. +.TP +1 +means that something was wrong. +.TP +10 +means that the lock file already exists. +.LP +If \fBwhack\fP detects a problem, it will return an exit status of 1. +If it received progress messages from \fBpluto\fP, it returns as status +the value of the numeric prefix from the last such message +that was not a message sent to syslog or a comment +(but the prefix for success is treated as 0). +Otherwise, the exit status is 0. +.SH FILES +\fI/var/run/pluto.pid\fP +.br +\fI/var/run/pluto.ctl\fP +.br +\fI/etc/ipsec.secrets\fP +.br +\fI$IPSEC_LIBDIR/_pluto_adns\fP +.br +\fI$IPSEC_EXECDIR/lwdnsq\fP +.br +\fI/dev/urandom\fP +.SH ENVIRONMENT +\fIIPSEC_LIBDIR\fP +.br +\fIIPSEC_EXECDIR\fP +.br +\fIIPSECmyid\fP +.SH SEE ALSO +.LP +The rest of the FreeS/WAN distribution, in particular \fIipsec\fP(8). +.LP +\fIipsec_auto\fP(8) is designed to make using \fBpluto\fP more pleasant. +Use it! +.LP +.IR ipsec.secrets (5) +describes the format of the secrets file. +.LP +\fIipsec_atoaddr\fP(3), part of the FreeS/WAN distribution, describes the +forms that IP addresses may take. +\fIipsec_atosubnet\fP(3), part of the FreeS/WAN distribution, describes the +forms that subnet specifications. +.LP +For more information on IPsec, the mailing list, and the relevant +documents, see: +.IP +.nh +\fIhttp://www.ietf.cnri.reston.va.us/html.charters/ipsec-charter.html\fP +.hy +.LP +At the time of writing, the most relevant IETF RFCs are: +.IP +RFC2409 The Internet Key Exchange (IKE) +.IP +RFC2408 Internet Security Association and Key Management Protocol (ISAKMP) +.IP +RFC2407 The Internet IP Security Domain of Interpretation for ISAKMP +.LP +The FreeS/WAN web site +and the mailing lists described there. +.SH HISTORY +This code is released under the GPL terms. +See the accompanying file COPYING-2.0 for more details. +The GPL does NOT apply to those pieces of code written by others +which are included in this distribution, except as noted by the +individual authors. +.LP +This software was originally written +for the FreeS/WAN project + +by Angelos D. Keromytis +(angelos@dsl.cis.upenn.edu), in May/June 1997, in Athens, Greece. +Thanks go to John Ioannidis for his help. +.LP +It is currently (2000) +being developed and maintained by D. Hugh Redelmeier +(hugh@mimosa.com), in Canada. The regulations of Greece and Canada +allow us to make the code freely redistributable. +.LP +Kai Martius (admin@imib.med.tu-dresden.de) contributed the initial +version of the code supporting PFS. +.LP +Richard Guy Briggs and Peter Onion + added the PFKEY2 support. +.LP +We gratefully acknowledge that we use parts of Eric Young's \fIlibdes\fP +package; see \fI../libdes/COPYRIGHT\fP. +.SH BUGS +.BR pluto +is a work-in-progress. It currently has many limitations. +For example, it ignores notification messages that it receives, and +it generates only Delete Notifications and those only for IPSEC SAs. +.LP +\fBpluto\fP does not support the Commit Flag. +The Commit Flag is a bad feature of the IKE protocol. +It isn't protected -- neither encrypted nor authenticated. +A man in the middle could turn it on, leading to DoS. +We just ignore it, with a warning. +This should let us interoperate with +implementations that insist on it, with minor damage. +.LP +\fBpluto\fP does not check that the SA returned by the Responder +is actually one that was proposed. It only checks that the SA is +acceptable. The difference is not large, but can show up in attributes +such as SA lifetime. +.LP +There is no good way for a connection to be automatically terminated. +This is a problem for Road Warrior and Opportunistic connections. +The \fB\-\-dontrekey\fP option does prevent the SAs from +being rekeyed on expiry. +Additonally, if a Road Warrior connection has a client subnet with a fixed IP +address, a negotiation with that subnet will cause any other +connection instantiations with that same subnet to be unoriented +(deleted, in effect). +See also the \-\-uniqueids option for an extension of this. +.LP +When \fBpluto\fP sends a message to a peer that has disappeared, +\fBpluto\fP receives incomplete information from the kernel, so it +logs the unsatisfactory message ``some IKE message we sent has been +rejected with ECONNREFUSED (kernel supplied no details)''. John +Denker suggests that this command is useful for tracking down the +source of these problems: +.br + tcpdump -i eth0 icmp[0] != 8 and icmp[0] != 0 +.br +Substitute your public interface for eth0 if it is different. +.LP +The word ``authenticate'' is used for two different features. We must +authenticate each IKE peer to the other. This is an important task of +Phase 1. Each packet must be authenticated, both in IKE and in IPsec, +and the method for IPsec is negotiated as an AH SA or part of an ESP SA. +Unfortunately, the protocol has no mechanism for authenticating the Phase 2 +identities. +.LP +Bugs should be reported to the mailing list. +Caution: we cannot accept +actual code from US residents, or even US citizens living outside the +US, because that would bring FreeS/WAN under US export law. Some +other countries cause similar problems. In general, we would prefer +that you send detailed problem reports rather than code: we want +FreeS/WAN to be unquestionably freely exportable, which means being +very careful about where the code comes from, and for a small bug fix, +that is often more time-consuming than just reinventing the fix +ourselves. diff --git a/src/pluto/plutomain.c b/src/pluto/plutomain.c new file mode 100644 index 000000000..e235ff765 --- /dev/null +++ b/src/pluto/plutomain.c @@ -0,0 +1,655 @@ +/* Pluto main program + * Copyright (C) 1997 Angelos D. Keromytis. + * Copyright (C) 1998-2001 D. Hugh Redelmeier. + * + * This program 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. See . + * + * 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 General Public License + * for more details. + * + * RCSID $Id: plutomain.c,v 1.16 2005/09/25 21:30:52 as Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* missing from on old systems */ +#include + +#include + +#include +#include + +#include "constants.h" +#include "defs.h" +#include "id.h" +#include "ca.h" +#include "certs.h" +#include "ac.h" +#include "connections.h" +#include "foodgroups.h" +#include "packet.h" +#include "demux.h" /* needs packet.h */ +#include "server.h" +#include "kernel.h" +#include "log.h" +#include "keys.h" +#include "adns.h" /* needs */ +#include "dnskey.h" /* needs keys.h and adns.h */ +#include "rnd.h" +#include "state.h" +#include "ipsec_doi.h" /* needs demux.h and state.h */ +#include "ocsp.h" +#include "crl.h" +#include "fetch.h" +#include "xauth.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" /* requires sha1.h and md5.h */ +#include "nat_traversal.h" +#include "virtual.h" + +static void +usage(const char *mess) +{ + if (mess != NULL && *mess != '\0') + fprintf(stderr, "%s\n", mess); + fprintf(stderr + , "Usage: pluto" + " [--help]" + " [--version]" + " [--optionsfrom ]" + " \\\n\t" + "[--nofork]" + " [--stderrlog]" + " [--noklips]" + " [--nocrsend]" + " \\\n\t" + "[--strictcrlpolicy]" + " [--crlcheckinterval ]" + " [--cachecrls]" + " [--uniqueids]" + " \\\n\t" + "[--interface ]" + " [--ikeport ]" + " \\\n\t" + "[--ctlbase ]" + " \\\n\t" + "[--perpeerlogbase ] [--perpeerlog]" + " \\\n\t" + "[--secretsfile ]" + " [--policygroupsdir ]" + " \\\n\t" + "[--adns ]" + "[--pkcs11module ]" + "[--pkcs11keepstate" +#ifdef DEBUG + " \\\n\t" + "[--debug-none]" + " [--debug-all]" + " \\\n\t" + "[--debug-raw]" + " [--debug-crypt]" + " [--debug-parsing]" + " [--debug-emitting]" + " \\\n\t" + "[--debug-control]" + " [--debug-lifecycle]" + " [--debug-klips]" + " [--debug-dns]" + " \\\n\t" + "[--debug-oppo]" + " [--debug-controlmore]" + " [--debug-private]" +#endif + " [ --debug-natt]" + " \\\n\t" + "[--nat_traversal] [--keep_alive ]" + " \\\n\t" + "[--force_keepalive] [--disable_port_floating]" + " \\\n\t" + "[--virtual_private ]" + "\n" + "strongSwan %s\n" + , ipsec_version_code()); + exit_pluto(mess == NULL? 0 : 1); +} + + +/* lock file support + * - provides convenient way for scripts to find Pluto's pid + * - prevents multiple Plutos competing for the same port + * - same basename as unix domain control socket + * NOTE: will not take account of sharing LOCK_DIR with other systems. + */ + +static char pluto_lock[sizeof(ctl_addr.sun_path)] = DEFAULT_CTLBASE LOCK_SUFFIX; +static bool pluto_lock_created = FALSE; + +/* create lockfile, or die in the attempt */ +static int +create_lock(void) +{ + int fd = open(pluto_lock, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC + , S_IRUSR | S_IRGRP | S_IROTH); + + if (fd < 0) + { + if (errno == EEXIST) + { + fprintf(stderr, "pluto: lock file \"%s\" already exists\n" + , pluto_lock); + exit_pluto(10); + } + else + { + fprintf(stderr + , "pluto: unable to create lock file \"%s\" (%d %s)\n" + , pluto_lock, errno, strerror(errno)); + exit_pluto(1); + } + } + pluto_lock_created = TRUE; + return fd; +} + +static bool +fill_lock(int lockfd, pid_t pid) +{ + char buf[30]; /* holds "\n" */ + int len = snprintf(buf, sizeof(buf), "%u\n", (unsigned int) pid); + bool ok = len > 0 && write(lockfd, buf, len) == len; + + close(lockfd); + return ok; +} + +static void +delete_lock(void) +{ + if (pluto_lock_created) + { + delete_ctl_socket(); + unlink(pluto_lock); /* is noting failure useful? */ + } +} + +/* by default pluto sends certificate requests to its peers */ +bool no_cr_send = FALSE; + +/* by default the CRL policy is lenient */ +bool strict_crl_policy = FALSE; + +/* by default CRLs are cached locally as files */ +bool cache_crls = FALSE; + +/* by default pluto does not check crls dynamically */ +long crl_check_interval = 0; + +/* path to the PKCS#11 module */ +char *pkcs11_module_path = NULL; + +/* by default pluto logs out after every smartcard use */ +bool pkcs11_keep_state = FALSE; + +/* by default pluto does not allow pkcs11 proxy access via whack */ +bool pkcs11_proxy = FALSE; + +int +main(int argc, char **argv) +{ + bool fork_desired = TRUE; + bool log_to_stderr_desired = FALSE; + bool nat_traversal = FALSE; + bool nat_t_spf = TRUE; /* support port floating */ + unsigned int keep_alive = 0; + bool force_keepalive = FALSE; + char *virtual_private = NULL; + int lockfd; + + /* handle arguments */ + for (;;) + { +# define DBG_OFFSET 256 + static const struct option long_opts[] = { + /* name, has_arg, flag, val */ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "optionsfrom", required_argument, NULL, '+' }, + { "nofork", no_argument, NULL, 'd' }, + { "stderrlog", no_argument, NULL, 'e' }, + { "noklips", no_argument, NULL, 'n' }, + { "nocrsend", no_argument, NULL, 'c' }, + { "strictcrlpolicy", no_argument, NULL, 'r' }, + { "crlcheckinterval", required_argument, NULL, 'x'}, + { "cachecrls", no_argument, NULL, 'C' }, + { "uniqueids", no_argument, NULL, 'u' }, + { "interface", required_argument, NULL, 'i' }, + { "ikeport", required_argument, NULL, 'p' }, + { "ctlbase", required_argument, NULL, 'b' }, + { "secretsfile", required_argument, NULL, 's' }, + { "foodgroupsdir", required_argument, NULL, 'f' }, + { "perpeerlogbase", required_argument, NULL, 'P' }, + { "perpeerlog", no_argument, NULL, 'l' }, + { "policygroupsdir", required_argument, NULL, 'f' }, +#ifdef USE_LWRES + { "lwdnsq", required_argument, NULL, 'a' }, +#else /* !USE_LWRES */ + { "adns", required_argument, NULL, 'a' }, +#endif /* !USE_LWRES */ + { "pkcs11module", required_argument, NULL, 'm' }, + { "pkcs11keepstate", no_argument, NULL, 'k' }, + { "pkcs11proxy", no_argument, NULL, 'y' }, + { "nat_traversal", no_argument, NULL, '1' }, + { "keep_alive", required_argument, NULL, '2' }, + { "force_keepalive", no_argument, NULL, '3' }, + { "disable_port_floating", no_argument, NULL, '4' }, + { "debug-natt", no_argument, NULL, '5' }, + { "virtual_private", required_argument, NULL, '6' }, +#ifdef DEBUG + { "debug-none", no_argument, NULL, 'N' }, + { "debug-all", no_argument, NULL, 'A' }, + { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET }, + { "debug-crypt", no_argument, NULL, DBG_CRYPT + DBG_OFFSET }, + { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET }, + { "debug-emitting", no_argument, NULL, DBG_EMITTING + DBG_OFFSET }, + { "debug-control", no_argument, NULL, DBG_CONTROL + DBG_OFFSET }, + { "debug-lifecycle", no_argument, NULL, DBG_LIFECYCLE + DBG_OFFSET }, + { "debug-klips", no_argument, NULL, DBG_KLIPS + DBG_OFFSET }, + { "debug-dns", no_argument, NULL, DBG_DNS + DBG_OFFSET }, + { "debug-oppo", no_argument, NULL, DBG_OPPO + DBG_OFFSET }, + { "debug-controlmore", no_argument, NULL, DBG_CONTROLMORE + DBG_OFFSET }, + { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET }, + + { "impair-delay-adns-key-answer", no_argument, NULL, IMPAIR_DELAY_ADNS_KEY_ANSWER + DBG_OFFSET }, + { "impair-delay-adns-txt-answer", no_argument, NULL, IMPAIR_DELAY_ADNS_TXT_ANSWER + DBG_OFFSET }, + { "impair-bust-mi2", no_argument, NULL, IMPAIR_BUST_MI2 + DBG_OFFSET }, + { "impair-bust-mr2", no_argument, NULL, IMPAIR_BUST_MR2 + DBG_OFFSET }, +#endif + { 0,0,0,0 } + }; + /* Note: we don't like the way short options get parsed + * by getopt_long, so we simply pass an empty string as + * the list. It could be "hvdenp:l:s:" "NARXPECK". + */ + int c = getopt_long(argc, argv, "", long_opts, NULL); + + /* Note: "breaking" from case terminates loop */ + switch (c) + { + case EOF: /* end of flags */ + break; + + case 0: /* long option already handled */ + continue; + + case ':': /* diagnostic already printed by getopt_long */ + case '?': /* diagnostic already printed by getopt_long */ + usage(""); + break; /* not actually reached */ + + case 'h': /* --help */ + usage(NULL); + break; /* not actually reached */ + + case 'v': /* --version */ + { + const char **sp = ipsec_copyright_notice(); + + printf("%s%s\n", ipsec_version_string(), + compile_time_interop_options); + for (; *sp != NULL; sp++) + puts(*sp); + } + exit_pluto(0); + break; /* not actually reached */ + + case '+': /* --optionsfrom */ + optionsfrom(optarg, &argc, &argv, optind, stderr); + /* does not return on error */ + continue; + + case 'd': /* --nofork*/ + fork_desired = FALSE; + continue; + + case 'e': /* --stderrlog */ + log_to_stderr_desired = TRUE; + continue; + + case 'n': /* --noklips */ + no_klips = TRUE; + continue; + + case 'c': /* --nocrsend */ + no_cr_send = TRUE; + continue; + + case 'r': /* --strictcrlpolicy */ + strict_crl_policy = TRUE; + continue; + + case 'x': /* --crlcheckinterval