From 1029d84d2365ca6e6b2d2a96e5c0a1fad3c127e3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 28 Apr 2006 10:51:19 +0000 Subject: --- src/Makefile | 37 + src/Makefile.program | 150 + src/_copyright/.cvsignore | 1 + src/_copyright/Makefile | 44 + src/_copyright/_copyright.8 | 32 + src/_copyright/_copyright.c | 69 + src/_updown/.cvsignore | 2 + src/_updown/Makefile | 22 + src/_updown/_updown.8 | 19 + src/_updown/_updown.in | 503 ++ src/_updown_espmark/Makefile | 22 + src/_updown_espmark/_updown_espmark.8 | 18 + src/_updown_espmark/_updown_espmark.in | 452 ++ src/charon/Doxyfile | 220 + src/charon/Makefile | 99 + src/charon/charon.kdevelop | 105 + src/charon/charon/Makefile.charon | 25 + src/charon/charon/config/Makefile.config | 32 + src/charon/charon/config/configuration.c | 112 + src/charon/charon/config/configuration.h | 89 + .../charon/config/connections/Makefile.connections | 24 + src/charon/charon/config/connections/connection.c | 367 ++ src/charon/charon/config/connections/connection.h | 283 + .../charon/config/connections/connection_store.h | 112 + .../config/connections/local_connection_store.c | 228 + .../config/connections/local_connection_store.h | 63 + .../charon/config/credentials/Makefile.credentials | 20 + .../charon/config/credentials/credential_store.h | 91 + .../config/credentials/local_credential_store.c | 315 ++ .../config/credentials/local_credential_store.h | 84 + .../charon/config/policies/Makefile.policies | 24 + .../charon/config/policies/local_policy_store.c | 136 + .../charon/config/policies/local_policy_store.h | 60 + src/charon/charon/config/policies/policy.c | 397 ++ src/charon/charon/config/policies/policy.h | 249 + src/charon/charon/config/policies/policy_store.h | 76 + src/charon/charon/config/proposal.c | 642 +++ src/charon/charon/config/proposal.h | 269 + src/charon/charon/config/traffic_selector.c | 425 ++ src/charon/charon/config/traffic_selector.h | 258 + src/charon/charon/daemon.c | 390 ++ src/charon/charon/daemon.h | 324 ++ src/charon/charon/encoding/Makefile.encoding | 30 + src/charon/charon/encoding/generator.c | 1077 ++++ src/charon/charon/encoding/generator.h | 101 + src/charon/charon/encoding/message.c | 1251 +++++ src/charon/charon/encoding/message.h | 367 ++ src/charon/charon/encoding/parser.c | 1065 ++++ src/charon/charon/encoding/parser.h | 95 + .../charon/encoding/payloads/Makefile.payloads | 108 + src/charon/charon/encoding/payloads/auth_payload.c | 265 + src/charon/charon/encoding/payloads/auth_payload.h | 122 + src/charon/charon/encoding/payloads/cert_payload.c | 279 + src/charon/charon/encoding/payloads/cert_payload.h | 155 + .../charon/encoding/payloads/certreq_payload.c | 259 + .../charon/encoding/payloads/certreq_payload.h | 125 + .../encoding/payloads/configuration_attribute.c | 282 + .../encoding/payloads/configuration_attribute.h | 149 + src/charon/charon/encoding/payloads/cp_payload.c | 305 ++ src/charon/charon/encoding/payloads/cp_payload.h | 138 + .../charon/encoding/payloads/delete_payload.c | 322 ++ .../charon/encoding/payloads/delete_payload.h | 156 + src/charon/charon/encoding/payloads/eap_payload.c | 227 + src/charon/charon/encoding/payloads/eap_payload.h | 105 + src/charon/charon/encoding/payloads/encodings.c | 68 + src/charon/charon/encoding/payloads/encodings.h | 540 ++ .../charon/encoding/payloads/encryption_payload.c | 702 +++ .../charon/encoding/payloads/encryption_payload.h | 196 + src/charon/charon/encoding/payloads/id_payload.c | 320 ++ src/charon/charon/encoding/payloads/id_payload.h | 172 + src/charon/charon/encoding/payloads/ike_header.c | 408 ++ src/charon/charon/encoding/payloads/ike_header.h | 261 + src/charon/charon/encoding/payloads/ke_payload.c | 276 + src/charon/charon/encoding/payloads/ke_payload.h | 110 + .../charon/encoding/payloads/nonce_payload.c | 241 + .../charon/encoding/payloads/nonce_payload.h | 89 + .../charon/encoding/payloads/notify_payload.c | 441 ++ .../charon/encoding/payloads/notify_payload.h | 200 + src/charon/charon/encoding/payloads/payload.c | 131 + src/charon/charon/encoding/payloads/payload.h | 279 + .../encoding/payloads/proposal_substructure.c | 629 +++ .../encoding/payloads/proposal_substructure.h | 231 + src/charon/charon/encoding/payloads/sa_payload.c | 390 ++ src/charon/charon/encoding/payloads/sa_payload.h | 140 + .../payloads/traffic_selector_substructure.c | 374 ++ .../payloads/traffic_selector_substructure.h | 171 + .../charon/encoding/payloads/transform_attribute.c | 333 ++ .../charon/encoding/payloads/transform_attribute.h | 154 + .../encoding/payloads/transform_substructure.c | 485 ++ .../encoding/payloads/transform_substructure.h | 198 + src/charon/charon/encoding/payloads/ts_payload.c | 365 ++ src/charon/charon/encoding/payloads/ts_payload.h | 152 + .../charon/encoding/payloads/unknown_payload.c | 207 + .../charon/encoding/payloads/unknown_payload.h | 95 + .../charon/encoding/payloads/vendor_id_payload.c | 227 + .../charon/encoding/payloads/vendor_id_payload.h | 103 + src/charon/charon/network/Makefile.network | 24 + src/charon/charon/network/packet.c | 189 + src/charon/charon/network/packet.h | 135 + src/charon/charon/network/socket.c | 457 ++ src/charon/charon/network/socket.h | 128 + src/charon/charon/queues/Makefile.queues | 30 + src/charon/charon/queues/event_queue.c | 349 ++ src/charon/charon/queues/event_queue.h | 117 + src/charon/charon/queues/job_queue.c | 153 + src/charon/charon/queues/job_queue.h | 99 + src/charon/charon/queues/jobs/Makefile.jobs | 40 + .../queues/jobs/delete_established_ike_sa_job.c | 90 + .../queues/jobs/delete_established_ike_sa_job.h | 78 + .../queues/jobs/delete_half_open_ike_sa_job.c | 90 + .../queues/jobs/delete_half_open_ike_sa_job.h | 79 + .../charon/queues/jobs/incoming_packet_job.c | 102 + .../charon/queues/jobs/incoming_packet_job.h | 78 + .../charon/queues/jobs/initiate_ike_sa_job.c | 101 + .../charon/queues/jobs/initiate_ike_sa_job.h | 75 + src/charon/charon/queues/jobs/job.c | 34 + src/charon/charon/queues/jobs/job.h | 120 + .../charon/queues/jobs/retransmit_request_job.c | 132 + .../charon/queues/jobs/retransmit_request_job.h | 105 + src/charon/charon/queues/send_queue.c | 153 + src/charon/charon/queues/send_queue.h | 100 + src/charon/charon/sa/Makefile.sa | 37 + src/charon/charon/sa/authenticator.c | 405 ++ src/charon/charon/sa/authenticator.h | 138 + src/charon/charon/sa/child_sa.c | 590 ++ src/charon/charon/sa/child_sa.h | 149 + src/charon/charon/sa/ike_sa.c | 1199 +++++ src/charon/charon/sa/ike_sa.h | 462 ++ src/charon/charon/sa/ike_sa_id.c | 185 + src/charon/charon/sa/ike_sa_id.h | 146 + src/charon/charon/sa/ike_sa_manager.c | 812 +++ src/charon/charon/sa/ike_sa_manager.h | 185 + src/charon/charon/sa/states/Makefile.states | 43 + src/charon/charon/sa/states/ike_auth_requested.c | 671 +++ src/charon/charon/sa/states/ike_auth_requested.h | 72 + src/charon/charon/sa/states/ike_sa_established.c | 239 + src/charon/charon/sa/states/ike_sa_established.h | 64 + .../charon/sa/states/ike_sa_init_requested.c | 798 +++ .../charon/sa/states/ike_sa_init_requested.h | 68 + .../charon/sa/states/ike_sa_init_responded.c | 695 +++ .../charon/sa/states/ike_sa_init_responded.h | 73 + src/charon/charon/sa/states/initiator_init.c | 360 ++ src/charon/charon/sa/states/initiator_init.h | 84 + src/charon/charon/sa/states/responder_init.c | 568 ++ src/charon/charon/sa/states/responder_init.h | 68 + src/charon/charon/sa/states/state.c | 37 + src/charon/charon/sa/states/state.h | 166 + src/charon/charon/threads/Makefile.threads | 39 + src/charon/charon/threads/kernel_interface.c | 729 +++ src/charon/charon/threads/kernel_interface.h | 185 + src/charon/charon/threads/receiver.c | 128 + src/charon/charon/threads/receiver.h | 67 + src/charon/charon/threads/scheduler.c | 124 + src/charon/charon/threads/scheduler.h | 67 + src/charon/charon/threads/sender.c | 126 + src/charon/charon/threads/sender.h | 63 + src/charon/charon/threads/stroke_interface.c | 661 +++ src/charon/charon/threads/stroke_interface.h | 86 + src/charon/charon/threads/thread_pool.c | 623 +++ src/charon/charon/threads/thread_pool.h | 78 + src/charon/doc/Architecture.txt | 56 + src/charon/doc/Known-bugs.txt | 6 + src/charon/doc/Todo-list.txt | 49 + src/charon/lib/Makefile.lib | 31 + src/charon/lib/asn1/Makefile.asn1 | 29 + src/charon/lib/asn1/asn1.c | 738 +++ src/charon/lib/asn1/asn1.h | 137 + src/charon/lib/asn1/oid.c | 197 + src/charon/lib/asn1/oid.h | 80 + src/charon/lib/asn1/oid.pl | 127 + src/charon/lib/asn1/oid.txt | 184 + src/charon/lib/asn1/pem.c | 343 ++ src/charon/lib/asn1/pem.h | 25 + src/charon/lib/asn1/ttodata.c | 374 ++ src/charon/lib/asn1/ttodata.h | 30 + src/charon/lib/crypto/Makefile.transforms | 37 + src/charon/lib/crypto/crypters/Makefile.crypters | 23 + src/charon/lib/crypto/crypters/aes_cbc_crypter.c | 1627 ++++++ src/charon/lib/crypto/crypters/aes_cbc_crypter.h | 61 + src/charon/lib/crypto/crypters/crypter.c | 63 + src/charon/lib/crypto/crypters/crypter.h | 153 + src/charon/lib/crypto/diffie_hellman.c | 615 +++ src/charon/lib/crypto/diffie_hellman.h | 149 + src/charon/lib/crypto/hashers/Makefile.hashers | 27 + src/charon/lib/crypto/hashers/hasher.c | 60 + src/charon/lib/crypto/hashers/hasher.h | 147 + src/charon/lib/crypto/hashers/md5_hasher.c | 394 ++ src/charon/lib/crypto/hashers/md5_hasher.h | 60 + src/charon/lib/crypto/hashers/sha1_hasher.c | 269 + src/charon/lib/crypto/hashers/sha1_hasher.h | 60 + src/charon/lib/crypto/hmac.c | 209 + src/charon/lib/crypto/hmac.h | 118 + src/charon/lib/crypto/prf_plus.c | 157 + src/charon/lib/crypto/prf_plus.h | 93 + src/charon/lib/crypto/prfs/Makefile.prfs | 23 + src/charon/lib/crypto/prfs/hmac_prf.c | 117 + src/charon/lib/crypto/prfs/hmac_prf.h | 64 + src/charon/lib/crypto/prfs/prf.c | 67 + src/charon/lib/crypto/prfs/prf.h | 136 + src/charon/lib/crypto/rsa/Makefile.rsa | 23 + src/charon/lib/crypto/rsa/rsa_private_key.c | 772 +++ src/charon/lib/crypto/rsa/rsa_private_key.h | 185 + src/charon/lib/crypto/rsa/rsa_public_key.c | 458 ++ src/charon/lib/crypto/rsa/rsa_public_key.h | 153 + src/charon/lib/crypto/signers/Makefile.signers | 23 + src/charon/lib/crypto/signers/hmac_signer.c | 169 + src/charon/lib/crypto/signers/hmac_signer.h | 58 + src/charon/lib/crypto/signers/signer.c | 59 + src/charon/lib/crypto/signers/signer.h | 147 + src/charon/lib/crypto/x509.c | 937 ++++ src/charon/lib/crypto/x509.h | 136 + src/charon/lib/definitions.c | 40 + src/charon/lib/definitions.h | 120 + src/charon/lib/library.c | 42 + src/charon/lib/library.h | 100 + src/charon/lib/types.c | 115 + src/charon/lib/types.h | 186 + src/charon/lib/utils/Makefile.utils | 47 + src/charon/lib/utils/host.c | 365 ++ src/charon/lib/utils/host.h | 225 + src/charon/lib/utils/identification.c | 1167 ++++ src/charon/lib/utils/identification.h | 245 + src/charon/lib/utils/iterator.h | 153 + src/charon/lib/utils/leak_detective.c | 540 ++ src/charon/lib/utils/leak_detective.h | 50 + src/charon/lib/utils/linked_list.c | 727 +++ src/charon/lib/utils/linked_list.h | 203 + src/charon/lib/utils/logger.c | 342 ++ src/charon/lib/utils/logger.h | 198 + src/charon/lib/utils/logger_manager.c | 220 + src/charon/lib/utils/logger_manager.h | 160 + src/charon/lib/utils/randomizer.c | 164 + src/charon/lib/utils/randomizer.h | 110 + src/charon/lib/utils/tester.c | 256 + src/charon/lib/utils/tester.h | 148 + src/charon/patches/strongswan-2.7.0.patch | 874 +++ src/charon/scripts/alice-key.der | Bin 0 -> 1190 bytes src/charon/scripts/alice.der | Bin 0 -> 764 bytes src/charon/scripts/bob-key.der | Bin 0 -> 1187 bytes src/charon/scripts/bob.der | Bin 0 -> 759 bytes src/charon/scripts/complex1.der | Bin 0 -> 934 bytes src/charon/scripts/complex2.der | Bin 0 -> 956 bytes src/charon/scripts/daemon-loop.sh | 13 + src/charon/scripts/deleteline | 9 + src/charon/scripts/replace | 9 + src/charon/scripts/to-alice.sh | 27 + src/charon/scripts/to-bob.sh | 27 + src/charon/stroke/Makefile.stroke | 17 + src/charon/stroke/stroke.c | 304 ++ src/charon/stroke/stroke.h | 91 + src/charon/testing/Makefile.testcases | 139 + src/charon/testing/aes_cbc_crypter_test.c | 201 + src/charon/testing/aes_cbc_crypter_test.h | 38 + src/charon/testing/certificate_test.c | 112 + src/charon/testing/certificate_test.h | 42 + src/charon/testing/child_sa_test.c | 101 + src/charon/testing/child_sa_test.h | 42 + src/charon/testing/connection_test.c | 82 + src/charon/testing/connection_test.h | 38 + src/charon/testing/diffie_hellman_test.c | 76 + src/charon/testing/diffie_hellman_test.h | 37 + src/charon/testing/encryption_payload_test.c | 139 + src/charon/testing/encryption_payload_test.h | 37 + src/charon/testing/event_queue_test.c | 143 + src/charon/testing/event_queue_test.h | 39 + src/charon/testing/generator_test.c | 1410 +++++ src/charon/testing/generator_test.h | 183 + src/charon/testing/hasher_test.c | 170 + src/charon/testing/hasher_test.h | 49 + src/charon/testing/hmac_signer_test.c | 203 + src/charon/testing/hmac_signer_test.h | 46 + src/charon/testing/hmac_test.c | 408 ++ src/charon/testing/hmac_test.h | 49 + src/charon/testing/identification_test.c | 166 + src/charon/testing/identification_test.h | 37 + src/charon/testing/ike_sa_id_test.c | 84 + src/charon/testing/ike_sa_id_test.h | 40 + src/charon/testing/ike_sa_manager_test.c | 185 + src/charon/testing/ike_sa_manager_test.h | 39 + src/charon/testing/ike_sa_test.c | 56 + src/charon/testing/ike_sa_test.h | 37 + src/charon/testing/job_queue_test.c | 132 + src/charon/testing/job_queue_test.h | 40 + src/charon/testing/kernel_interface_test.c | 84 + src/charon/testing/kernel_interface_test.h | 38 + src/charon/testing/leak_detective_test.c | 79 + src/charon/testing/leak_detective_test.h | 38 + src/charon/testing/linked_list_test.c | 241 + src/charon/testing/linked_list_test.h | 74 + src/charon/testing/packet_test.c | 55 + src/charon/testing/packet_test.h | 37 + src/charon/testing/parser_test.c | 963 ++++ src/charon/testing/parser_test.h | 170 + src/charon/testing/policy_test.c | 246 + src/charon/testing/policy_test.h | 42 + src/charon/testing/prf_plus_test.c | 145 + src/charon/testing/prf_plus_test.h | 38 + src/charon/testing/proposal_test.c | 98 + src/charon/testing/proposal_test.h | 42 + src/charon/testing/rsa_test.c | 226 + src/charon/testing/rsa_test.h | 41 + src/charon/testing/scheduler_test.c | 92 + src/charon/testing/scheduler_test.h | 37 + src/charon/testing/send_queue_test.c | 142 + src/charon/testing/send_queue_test.h | 40 + src/charon/testing/sender_test.c | 88 + src/charon/testing/sender_test.h | 37 + src/charon/testing/socket_test.c | 82 + src/charon/testing/socket_test.h | 38 + src/charon/testing/testcases.c | 263 + src/charon/testing/thread_pool_test.c | 41 + src/charon/testing/thread_pool_test.h | 37 + src/ipsec/.cvsignore | 1 + src/ipsec/Makefile | 28 + src/ipsec/distro.txt | 1 + src/ipsec/ipsec.8 | 336 ++ src/ipsec/ipsec.in | 269 + src/openac/Makefile | 154 + 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/.cvsignore | 3 + src/pluto/Makefile | 1095 ++++ src/pluto/PLUTO-CONVENTIONS | 127 + 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/Config.ike_alg | 9 + src/pluto/alg/Makefile | 93 + src/pluto/alg/Makefile.ike_alg_aes | 14 + src/pluto/alg/Makefile.ike_alg_blowfish | 13 + src/pluto/alg/Makefile.ike_alg_serpent | 13 + src/pluto/alg/Makefile.ike_alg_sha2 | 13 + src/pluto/alg/Makefile.ike_alg_twofish | 13 + 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 | 61 + src/pluto/alg/ike_alg_twofish.c | 85 + src/pluto/alg_info.c | 1197 +++++ 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 | 4431 +++++++++++++++ src/pluto/connections.h | 375 ++ src/pluto/constants.c | 1271 +++++ src/pluto/constants.h | 1184 ++++ src/pluto/cookie.c | 67 + src/pluto/cookie.h | 24 + src/pluto/crl.c | 763 +++ src/pluto/crl.h | 87 + src/pluto/crypto.c | 261 + src/pluto/crypto.h | 107 + 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 | 2427 +++++++++ src/pluto/demux.h | 100 + 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 | 459 ++ src/pluto/ike_alg.h | 73 + src/pluto/ipsec.secrets.5 | 175 + src/pluto/ipsec_doi.c | 5649 ++++++++++++++++++++ src/pluto/ipsec_doi.h | 104 + src/pluto/kameipsec.h | 47 + src/pluto/kernel.c | 2997 +++++++++++ src/pluto/kernel.h | 200 + src/pluto/kernel_alg.c | 775 +++ src/pluto/kernel_alg.h | 46 + src/pluto/kernel_netlink.c | 1221 +++++ src/pluto/kernel_netlink.h | 20 + src/pluto/kernel_noklips.c | 126 + src/pluto/kernel_noklips.h | 19 + src/pluto/kernel_pfkey.c | 938 ++++ src/pluto/kernel_pfkey.h | 23 + src/pluto/keys.c | 1404 +++++ src/pluto/keys.h | 110 + 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 | 843 +++ 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 | 798 +++ src/pluto/modecfg.h | 33 + src/pluto/mp_defs.c | 70 + src/pluto/mp_defs.h | 36 + src/pluto/nat_traversal.c | 869 +++ 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 | 75 + 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 | 635 +++ src/pluto/pkcs1.h | 88 + src/pluto/pkcs7.c | 862 +++ src/pluto/pkcs7.h | 51 + src/pluto/pluto-style.el | 4 + src/pluto/pluto.8 | 1649 ++++++ src/pluto/plutomain.c | 696 +++ src/pluto/primegen.c | 593 ++ src/pluto/rcv_info.c | 308 ++ src/pluto/rcv_info.h | 18 + src/pluto/rcv_whack.c | 655 +++ src/pluto/rcv_whack.h | 17 + src/pluto/rnd.c | 250 + src/pluto/rnd.h | 21 + src/pluto/routing.txt | 331 ++ 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 | 1064 ++++ src/pluto/server.h | 60 + 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 | 2402 +++++++++ src/pluto/spdb.h | 113 + src/pluto/state.c | 1007 ++++ src/pluto/state.h | 269 + src/pluto/timer.c | 537 ++ src/pluto/timer.h | 34 + src/pluto/vendor.c | 493 ++ src/pluto/vendor.h | 107 + src/pluto/virtual.c | 338 ++ src/pluto/virtual.h | 31 + src/pluto/whack.c | 1911 +++++++ src/pluto/whack.h | 318 ++ src/pluto/x509.c | 2241 ++++++++ src/pluto/x509.h | 138 + src/scepclient/Makefile | 184 + 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 | 200 + src/starter/README | 104 + src/starter/args.c | 624 +++ src/starter/args.h | 34 + src/starter/cmp.c | 105 + src/starter/cmp.h | 29 + src/starter/confread.c | 861 +++ src/starter/confread.h | 199 + src/starter/exec.c | 54 + src/starter/exec.h | 23 + src/starter/files.h | 54 + src/starter/interfaces.c | 595 +++ src/starter/interfaces.h | 41 + src/starter/invokecharon.c | 174 + src/starter/invokecharon.h | 31 + src/starter/invokepluto.c | 286 + src/starter/invokepluto.h | 28 + src/starter/keywords.c | 235 + src/starter/keywords.h | 164 + src/starter/keywords.txt | 105 + 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.output | 351 ++ src/starter/parser.tab.c | 1666 ++++++ src/starter/parser.tab.h | 72 + src/starter/parser.y | 283 + src/starter/starter.8 | 0 src/starter/starter.c | 610 +++ src/starter/starterstroke.c | 161 + src/starter/starterstroke.h | 27 + src/starter/starterwhack.c | 371 ++ src/starter/starterwhack.h | 32 + 512 files changed, 149427 insertions(+) create mode 100644 src/Makefile create mode 100644 src/Makefile.program create mode 100644 src/_copyright/.cvsignore create mode 100644 src/_copyright/Makefile create mode 100644 src/_copyright/_copyright.8 create mode 100644 src/_copyright/_copyright.c create mode 100644 src/_updown/.cvsignore create mode 100644 src/_updown/Makefile create mode 100644 src/_updown/_updown.8 create mode 100755 src/_updown/_updown.in create mode 100644 src/_updown_espmark/Makefile create mode 100644 src/_updown_espmark/_updown_espmark.8 create mode 100644 src/_updown_espmark/_updown_espmark.in create mode 100644 src/charon/Doxyfile create mode 100644 src/charon/Makefile create mode 100644 src/charon/charon.kdevelop create mode 100644 src/charon/charon/Makefile.charon create mode 100644 src/charon/charon/config/Makefile.config create mode 100755 src/charon/charon/config/configuration.c create mode 100755 src/charon/charon/config/configuration.h create mode 100644 src/charon/charon/config/connections/Makefile.connections create mode 100644 src/charon/charon/config/connections/connection.c create mode 100644 src/charon/charon/config/connections/connection.h create mode 100755 src/charon/charon/config/connections/connection_store.h create mode 100644 src/charon/charon/config/connections/local_connection_store.c create mode 100644 src/charon/charon/config/connections/local_connection_store.h create mode 100644 src/charon/charon/config/credentials/Makefile.credentials create mode 100755 src/charon/charon/config/credentials/credential_store.h create mode 100644 src/charon/charon/config/credentials/local_credential_store.c create mode 100644 src/charon/charon/config/credentials/local_credential_store.h create mode 100644 src/charon/charon/config/policies/Makefile.policies create mode 100644 src/charon/charon/config/policies/local_policy_store.c create mode 100644 src/charon/charon/config/policies/local_policy_store.h create mode 100644 src/charon/charon/config/policies/policy.c create mode 100644 src/charon/charon/config/policies/policy.h create mode 100755 src/charon/charon/config/policies/policy_store.h create mode 100644 src/charon/charon/config/proposal.c create mode 100644 src/charon/charon/config/proposal.h create mode 100644 src/charon/charon/config/traffic_selector.c create mode 100644 src/charon/charon/config/traffic_selector.h create mode 100644 src/charon/charon/daemon.c create mode 100644 src/charon/charon/daemon.h create mode 100644 src/charon/charon/encoding/Makefile.encoding create mode 100644 src/charon/charon/encoding/generator.c create mode 100644 src/charon/charon/encoding/generator.h create mode 100644 src/charon/charon/encoding/message.c create mode 100644 src/charon/charon/encoding/message.h create mode 100644 src/charon/charon/encoding/parser.c create mode 100644 src/charon/charon/encoding/parser.h create mode 100644 src/charon/charon/encoding/payloads/Makefile.payloads create mode 100644 src/charon/charon/encoding/payloads/auth_payload.c create mode 100644 src/charon/charon/encoding/payloads/auth_payload.h create mode 100644 src/charon/charon/encoding/payloads/cert_payload.c create mode 100644 src/charon/charon/encoding/payloads/cert_payload.h create mode 100644 src/charon/charon/encoding/payloads/certreq_payload.c create mode 100644 src/charon/charon/encoding/payloads/certreq_payload.h create mode 100644 src/charon/charon/encoding/payloads/configuration_attribute.c create mode 100644 src/charon/charon/encoding/payloads/configuration_attribute.h create mode 100644 src/charon/charon/encoding/payloads/cp_payload.c create mode 100644 src/charon/charon/encoding/payloads/cp_payload.h create mode 100644 src/charon/charon/encoding/payloads/delete_payload.c create mode 100644 src/charon/charon/encoding/payloads/delete_payload.h create mode 100644 src/charon/charon/encoding/payloads/eap_payload.c create mode 100644 src/charon/charon/encoding/payloads/eap_payload.h create mode 100644 src/charon/charon/encoding/payloads/encodings.c create mode 100644 src/charon/charon/encoding/payloads/encodings.h create mode 100644 src/charon/charon/encoding/payloads/encryption_payload.c create mode 100644 src/charon/charon/encoding/payloads/encryption_payload.h create mode 100644 src/charon/charon/encoding/payloads/id_payload.c create mode 100644 src/charon/charon/encoding/payloads/id_payload.h create mode 100644 src/charon/charon/encoding/payloads/ike_header.c create mode 100644 src/charon/charon/encoding/payloads/ike_header.h create mode 100644 src/charon/charon/encoding/payloads/ke_payload.c create mode 100644 src/charon/charon/encoding/payloads/ke_payload.h create mode 100644 src/charon/charon/encoding/payloads/nonce_payload.c create mode 100644 src/charon/charon/encoding/payloads/nonce_payload.h create mode 100644 src/charon/charon/encoding/payloads/notify_payload.c create mode 100644 src/charon/charon/encoding/payloads/notify_payload.h create mode 100644 src/charon/charon/encoding/payloads/payload.c create mode 100644 src/charon/charon/encoding/payloads/payload.h create mode 100644 src/charon/charon/encoding/payloads/proposal_substructure.c create mode 100644 src/charon/charon/encoding/payloads/proposal_substructure.h create mode 100644 src/charon/charon/encoding/payloads/sa_payload.c create mode 100644 src/charon/charon/encoding/payloads/sa_payload.h create mode 100644 src/charon/charon/encoding/payloads/traffic_selector_substructure.c create mode 100644 src/charon/charon/encoding/payloads/traffic_selector_substructure.h create mode 100644 src/charon/charon/encoding/payloads/transform_attribute.c create mode 100644 src/charon/charon/encoding/payloads/transform_attribute.h create mode 100644 src/charon/charon/encoding/payloads/transform_substructure.c create mode 100644 src/charon/charon/encoding/payloads/transform_substructure.h create mode 100644 src/charon/charon/encoding/payloads/ts_payload.c create mode 100644 src/charon/charon/encoding/payloads/ts_payload.h create mode 100644 src/charon/charon/encoding/payloads/unknown_payload.c create mode 100644 src/charon/charon/encoding/payloads/unknown_payload.h create mode 100644 src/charon/charon/encoding/payloads/vendor_id_payload.c create mode 100644 src/charon/charon/encoding/payloads/vendor_id_payload.h create mode 100644 src/charon/charon/network/Makefile.network create mode 100644 src/charon/charon/network/packet.c create mode 100644 src/charon/charon/network/packet.h create mode 100644 src/charon/charon/network/socket.c create mode 100644 src/charon/charon/network/socket.h create mode 100644 src/charon/charon/queues/Makefile.queues create mode 100644 src/charon/charon/queues/event_queue.c create mode 100644 src/charon/charon/queues/event_queue.h create mode 100644 src/charon/charon/queues/job_queue.c create mode 100644 src/charon/charon/queues/job_queue.h create mode 100644 src/charon/charon/queues/jobs/Makefile.jobs create mode 100644 src/charon/charon/queues/jobs/delete_established_ike_sa_job.c create mode 100644 src/charon/charon/queues/jobs/delete_established_ike_sa_job.h create mode 100644 src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c create mode 100644 src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h create mode 100644 src/charon/charon/queues/jobs/incoming_packet_job.c create mode 100644 src/charon/charon/queues/jobs/incoming_packet_job.h create mode 100644 src/charon/charon/queues/jobs/initiate_ike_sa_job.c create mode 100644 src/charon/charon/queues/jobs/initiate_ike_sa_job.h create mode 100644 src/charon/charon/queues/jobs/job.c create mode 100644 src/charon/charon/queues/jobs/job.h create mode 100644 src/charon/charon/queues/jobs/retransmit_request_job.c create mode 100644 src/charon/charon/queues/jobs/retransmit_request_job.h create mode 100644 src/charon/charon/queues/send_queue.c create mode 100644 src/charon/charon/queues/send_queue.h create mode 100644 src/charon/charon/sa/Makefile.sa create mode 100644 src/charon/charon/sa/authenticator.c create mode 100644 src/charon/charon/sa/authenticator.h create mode 100644 src/charon/charon/sa/child_sa.c create mode 100644 src/charon/charon/sa/child_sa.h create mode 100644 src/charon/charon/sa/ike_sa.c create mode 100644 src/charon/charon/sa/ike_sa.h create mode 100644 src/charon/charon/sa/ike_sa_id.c create mode 100644 src/charon/charon/sa/ike_sa_id.h create mode 100644 src/charon/charon/sa/ike_sa_manager.c create mode 100644 src/charon/charon/sa/ike_sa_manager.h create mode 100644 src/charon/charon/sa/states/Makefile.states create mode 100644 src/charon/charon/sa/states/ike_auth_requested.c create mode 100644 src/charon/charon/sa/states/ike_auth_requested.h create mode 100644 src/charon/charon/sa/states/ike_sa_established.c create mode 100644 src/charon/charon/sa/states/ike_sa_established.h create mode 100644 src/charon/charon/sa/states/ike_sa_init_requested.c create mode 100644 src/charon/charon/sa/states/ike_sa_init_requested.h create mode 100644 src/charon/charon/sa/states/ike_sa_init_responded.c create mode 100644 src/charon/charon/sa/states/ike_sa_init_responded.h create mode 100644 src/charon/charon/sa/states/initiator_init.c create mode 100644 src/charon/charon/sa/states/initiator_init.h create mode 100644 src/charon/charon/sa/states/responder_init.c create mode 100644 src/charon/charon/sa/states/responder_init.h create mode 100644 src/charon/charon/sa/states/state.c create mode 100644 src/charon/charon/sa/states/state.h create mode 100644 src/charon/charon/threads/Makefile.threads create mode 100644 src/charon/charon/threads/kernel_interface.c create mode 100644 src/charon/charon/threads/kernel_interface.h create mode 100644 src/charon/charon/threads/receiver.c create mode 100644 src/charon/charon/threads/receiver.h create mode 100644 src/charon/charon/threads/scheduler.c create mode 100644 src/charon/charon/threads/scheduler.h create mode 100644 src/charon/charon/threads/sender.c create mode 100644 src/charon/charon/threads/sender.h create mode 100755 src/charon/charon/threads/stroke_interface.c create mode 100644 src/charon/charon/threads/stroke_interface.h create mode 100644 src/charon/charon/threads/thread_pool.c create mode 100644 src/charon/charon/threads/thread_pool.h create mode 100644 src/charon/doc/Architecture.txt create mode 100644 src/charon/doc/Known-bugs.txt create mode 100644 src/charon/doc/Todo-list.txt create mode 100644 src/charon/lib/Makefile.lib create mode 100644 src/charon/lib/asn1/Makefile.asn1 create mode 100644 src/charon/lib/asn1/asn1.c create mode 100644 src/charon/lib/asn1/asn1.h create mode 100644 src/charon/lib/asn1/oid.c create mode 100644 src/charon/lib/asn1/oid.h create mode 100644 src/charon/lib/asn1/oid.pl create mode 100644 src/charon/lib/asn1/oid.txt create mode 100755 src/charon/lib/asn1/pem.c create mode 100755 src/charon/lib/asn1/pem.h create mode 100644 src/charon/lib/asn1/ttodata.c create mode 100644 src/charon/lib/asn1/ttodata.h create mode 100644 src/charon/lib/crypto/Makefile.transforms create mode 100644 src/charon/lib/crypto/crypters/Makefile.crypters create mode 100644 src/charon/lib/crypto/crypters/aes_cbc_crypter.c create mode 100644 src/charon/lib/crypto/crypters/aes_cbc_crypter.h create mode 100644 src/charon/lib/crypto/crypters/crypter.c create mode 100644 src/charon/lib/crypto/crypters/crypter.h create mode 100644 src/charon/lib/crypto/diffie_hellman.c create mode 100644 src/charon/lib/crypto/diffie_hellman.h create mode 100644 src/charon/lib/crypto/hashers/Makefile.hashers create mode 100644 src/charon/lib/crypto/hashers/hasher.c create mode 100644 src/charon/lib/crypto/hashers/hasher.h create mode 100644 src/charon/lib/crypto/hashers/md5_hasher.c create mode 100644 src/charon/lib/crypto/hashers/md5_hasher.h create mode 100644 src/charon/lib/crypto/hashers/sha1_hasher.c create mode 100644 src/charon/lib/crypto/hashers/sha1_hasher.h create mode 100644 src/charon/lib/crypto/hmac.c create mode 100644 src/charon/lib/crypto/hmac.h create mode 100644 src/charon/lib/crypto/prf_plus.c create mode 100644 src/charon/lib/crypto/prf_plus.h create mode 100644 src/charon/lib/crypto/prfs/Makefile.prfs create mode 100644 src/charon/lib/crypto/prfs/hmac_prf.c create mode 100644 src/charon/lib/crypto/prfs/hmac_prf.h create mode 100644 src/charon/lib/crypto/prfs/prf.c create mode 100644 src/charon/lib/crypto/prfs/prf.h create mode 100644 src/charon/lib/crypto/rsa/Makefile.rsa create mode 100644 src/charon/lib/crypto/rsa/rsa_private_key.c create mode 100644 src/charon/lib/crypto/rsa/rsa_private_key.h create mode 100644 src/charon/lib/crypto/rsa/rsa_public_key.c create mode 100644 src/charon/lib/crypto/rsa/rsa_public_key.h create mode 100644 src/charon/lib/crypto/signers/Makefile.signers create mode 100644 src/charon/lib/crypto/signers/hmac_signer.c create mode 100644 src/charon/lib/crypto/signers/hmac_signer.h create mode 100644 src/charon/lib/crypto/signers/signer.c create mode 100644 src/charon/lib/crypto/signers/signer.h create mode 100755 src/charon/lib/crypto/x509.c create mode 100755 src/charon/lib/crypto/x509.h create mode 100644 src/charon/lib/definitions.c create mode 100644 src/charon/lib/definitions.h create mode 100644 src/charon/lib/library.c create mode 100644 src/charon/lib/library.h create mode 100644 src/charon/lib/types.c create mode 100644 src/charon/lib/types.h create mode 100644 src/charon/lib/utils/Makefile.utils create mode 100644 src/charon/lib/utils/host.c create mode 100644 src/charon/lib/utils/host.h create mode 100644 src/charon/lib/utils/identification.c create mode 100644 src/charon/lib/utils/identification.h create mode 100644 src/charon/lib/utils/iterator.h create mode 100644 src/charon/lib/utils/leak_detective.c create mode 100644 src/charon/lib/utils/leak_detective.h create mode 100644 src/charon/lib/utils/linked_list.c create mode 100644 src/charon/lib/utils/linked_list.h create mode 100644 src/charon/lib/utils/logger.c create mode 100644 src/charon/lib/utils/logger.h create mode 100644 src/charon/lib/utils/logger_manager.c create mode 100644 src/charon/lib/utils/logger_manager.h create mode 100644 src/charon/lib/utils/randomizer.c create mode 100644 src/charon/lib/utils/randomizer.h create mode 100644 src/charon/lib/utils/tester.c create mode 100644 src/charon/lib/utils/tester.h create mode 100644 src/charon/patches/strongswan-2.7.0.patch create mode 100644 src/charon/scripts/alice-key.der create mode 100644 src/charon/scripts/alice.der create mode 100644 src/charon/scripts/bob-key.der create mode 100644 src/charon/scripts/bob.der create mode 100644 src/charon/scripts/complex1.der create mode 100644 src/charon/scripts/complex2.der create mode 100755 src/charon/scripts/daemon-loop.sh create mode 100755 src/charon/scripts/deleteline create mode 100755 src/charon/scripts/replace create mode 100755 src/charon/scripts/to-alice.sh create mode 100755 src/charon/scripts/to-bob.sh create mode 100644 src/charon/stroke/Makefile.stroke create mode 100644 src/charon/stroke/stroke.c create mode 100644 src/charon/stroke/stroke.h create mode 100644 src/charon/testing/Makefile.testcases create mode 100644 src/charon/testing/aes_cbc_crypter_test.c create mode 100644 src/charon/testing/aes_cbc_crypter_test.h create mode 100644 src/charon/testing/certificate_test.c create mode 100644 src/charon/testing/certificate_test.h create mode 100644 src/charon/testing/child_sa_test.c create mode 100644 src/charon/testing/child_sa_test.h create mode 100644 src/charon/testing/connection_test.c create mode 100644 src/charon/testing/connection_test.h create mode 100644 src/charon/testing/diffie_hellman_test.c create mode 100644 src/charon/testing/diffie_hellman_test.h create mode 100644 src/charon/testing/encryption_payload_test.c create mode 100644 src/charon/testing/encryption_payload_test.h create mode 100644 src/charon/testing/event_queue_test.c create mode 100644 src/charon/testing/event_queue_test.h create mode 100644 src/charon/testing/generator_test.c create mode 100644 src/charon/testing/generator_test.h create mode 100644 src/charon/testing/hasher_test.c create mode 100644 src/charon/testing/hasher_test.h create mode 100644 src/charon/testing/hmac_signer_test.c create mode 100644 src/charon/testing/hmac_signer_test.h create mode 100644 src/charon/testing/hmac_test.c create mode 100644 src/charon/testing/hmac_test.h create mode 100644 src/charon/testing/identification_test.c create mode 100644 src/charon/testing/identification_test.h create mode 100644 src/charon/testing/ike_sa_id_test.c create mode 100644 src/charon/testing/ike_sa_id_test.h create mode 100644 src/charon/testing/ike_sa_manager_test.c create mode 100644 src/charon/testing/ike_sa_manager_test.h create mode 100644 src/charon/testing/ike_sa_test.c create mode 100644 src/charon/testing/ike_sa_test.h create mode 100644 src/charon/testing/job_queue_test.c create mode 100644 src/charon/testing/job_queue_test.h create mode 100644 src/charon/testing/kernel_interface_test.c create mode 100644 src/charon/testing/kernel_interface_test.h create mode 100644 src/charon/testing/leak_detective_test.c create mode 100644 src/charon/testing/leak_detective_test.h create mode 100644 src/charon/testing/linked_list_test.c create mode 100644 src/charon/testing/linked_list_test.h create mode 100644 src/charon/testing/packet_test.c create mode 100644 src/charon/testing/packet_test.h create mode 100644 src/charon/testing/parser_test.c create mode 100644 src/charon/testing/parser_test.h create mode 100644 src/charon/testing/policy_test.c create mode 100644 src/charon/testing/policy_test.h create mode 100644 src/charon/testing/prf_plus_test.c create mode 100644 src/charon/testing/prf_plus_test.h create mode 100644 src/charon/testing/proposal_test.c create mode 100644 src/charon/testing/proposal_test.h create mode 100644 src/charon/testing/rsa_test.c create mode 100644 src/charon/testing/rsa_test.h create mode 100644 src/charon/testing/scheduler_test.c create mode 100644 src/charon/testing/scheduler_test.h create mode 100644 src/charon/testing/send_queue_test.c create mode 100644 src/charon/testing/send_queue_test.h create mode 100644 src/charon/testing/sender_test.c create mode 100644 src/charon/testing/sender_test.h create mode 100644 src/charon/testing/socket_test.c create mode 100644 src/charon/testing/socket_test.h create mode 100644 src/charon/testing/testcases.c create mode 100644 src/charon/testing/thread_pool_test.c create mode 100644 src/charon/testing/thread_pool_test.h create mode 100644 src/ipsec/.cvsignore create mode 100644 src/ipsec/Makefile create mode 100644 src/ipsec/distro.txt create mode 100644 src/ipsec/ipsec.8 create mode 100755 src/ipsec/ipsec.in create mode 100644 src/openac/Makefile 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/.cvsignore create mode 100644 src/pluto/Makefile create mode 100644 src/pluto/PLUTO-CONVENTIONS 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/Config.ike_alg create mode 100644 src/pluto/alg/Makefile create mode 100644 src/pluto/alg/Makefile.ike_alg_aes create mode 100644 src/pluto/alg/Makefile.ike_alg_blowfish create mode 100644 src/pluto/alg/Makefile.ike_alg_serpent create mode 100644 src/pluto/alg/Makefile.ike_alg_sha2 create mode 100644 src/pluto/alg/Makefile.ike_alg_twofish 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_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-style.el 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_info.c create mode 100644 src/pluto/rcv_info.h 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/routing.txt 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/whack.c create mode 100644 src/pluto/whack.h create mode 100644 src/pluto/x509.c create mode 100644 src/pluto/x509.h create mode 100644 src/scepclient/Makefile 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 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/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.output create mode 100644 src/starter/parser.tab.c create mode 100644 src/starter/parser.tab.h create mode 100644 src/starter/parser.y create mode 100644 src/starter/starter.8 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 (limited to 'src') diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..4e3ec6b10 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,37 @@ +# Makefile for the KLIPS interface utilities +# Copyright (C) 1998, 1999 Henry Spencer. +# 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: Makefile,v 1.8 2006/04/17 11:04:45 as Exp $ + +FREESWANSRCDIR=.. +include ${FREESWANSRCDIR}/Makefile.inc + +SUBDIRS=pluto _copyright _updown _updown_espmark ipsec starter openac scepclient + +ifeq ($(USE_IKEV2),true) +SUBDIRS+=charon +endif + +def: + @echo "Please read doc/intro.html or INSTALL before running make" + @false + +# programs + +cleanall distclean mostlyclean realclean install programs checkprograms check clean spotless install_file_list: + @for d in $(SUBDIRS) ; \ + do \ + (cd $$d && $(MAKE) FREESWANSRCDIR=$(FREESWANSRCDIR)/.. $@ ) || exit 1;\ + done; \ + diff --git a/src/Makefile.program b/src/Makefile.program new file mode 100644 index 000000000..6868c258a --- /dev/null +++ b/src/Makefile.program @@ -0,0 +1,150 @@ + +include ${FREESWANSRCDIR}/Makefile.ver + +CFLAGS+=$(USERCOMPILE) -I${KLIPSINC} + +CFLAGS+= -Wall +#CFLAGS+= -Wconversion +#CFLAGS+= -Wmissing-prototypes +CFLAGS+= -Wpointer-arith +CFLAGS+= -Wcast-qual +#CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wstrict-prototypes +#CFLAGS+= -pedantic +#CFLAGS+= -W +#CFLAGS+= -Wwrite-strings +CFLAGS+= -Wbad-function-cast + +# die if there are any warnings +ifndef WERROR +WERROR:= -Werror +endif + +#CFLAGS+= ${WERROR} + +ifneq ($(LD_LIBRARY_PATH),) +LDFLAGS=-L$(LD_LIBRARY_PATH) +endif + +MANDIR8=$(MANTREE)/man8 +MANDIR5=$(MANTREE)/man5 + +ifndef PROGRAMDIR +PROGRAMDIR=${LIBEXECDIR} +endif + +ifndef MANPROGPREFIX +MANPROGPREFIX=ipsec_ +endif + +ifndef CONFDSUBDIR +CONFDSUBDIR=. +endif + +all: $(PROGRAM) + +programs: all + +ifneq ($(PROGRAM),check) +check: $(PROGRAM) +endif + + +ifneq ($(NOINSTALL),true) + +install:: $(PROGRAM) $(CONFFILES) $(EXTRA8MAN) $(EXTRA5MAN) $(EXTRA5PROC) $(LIBFILES) $(CONFDFILES) + @mkdir -p $(PROGRAMDIR) $(MANDIR8) $(MANDIR5) $(LIBDIR) $(CONFDIR) $(CONFDDIR) $(CONFDDIR)/$(CONFDSUBDIR) $(EXAMPLECONFDIR) + @if [ -n "$(PROGRAM)" ]; then $(INSTALL) $(INSTBINFLAGS) $(PROGRAM) $(PROGRAMDIR); fi + @$(foreach f, $(addsuffix .8, $(PROGRAM)), \ + $(INSTALL) $(INSTMANFLAGS) $f $(MANDIR8)/$(MANPROGPREFIX)$f || exit 1; \ + ) + @$(foreach f, $(EXTRA8MAN), \ + $(INSTALL) $(INSTMANFLAGS) $f $(MANDIR8)/ipsec_$f || exit 1; \ + ) + @$(foreach f, $(EXTRA5MAN), \ + $(INSTALL) $(INSTMANFLAGS) $f $(MANDIR5)/$f || exit 1 ;\ + ) + @$(foreach f, $(EXTRA5PROC), \ + $(INSTALL) $(INSTMANFLAGS) $f $(MANDIR5)/ipsec_$f || exit 1 ;\ + ) + @$(foreach f, $(LIBFILES), \ + $(INSTALL) $(INSTCONFFLAGS) $f $(LIBDIR)/$f || exit 1 ;\ + ) + @$(foreach f, $(CONFFILES), \ + if [ ! -f $(CONFDIR)/$f ]; then $(INSTALL) $(INSTCONFFLAGS) $f $(CONFDIR)/$f || exit 1; fi;\ + $(INSTALL) $(INSTCONFFLAGS) $f $(EXAMPLECONFDIR)/$f-sample || exit 1; \ + ) + @$(foreach f, $(CONFDFILES), \ + if [ ! -f $(CONFDDIR)/$(CONFDSUBDIR)/$f ]; then $(INSTALL) $(INSTCONFFLAGS) $f $(CONFDDIR)/$(CONFDSUBDIR)/$f || exit 1; fi;\ + ) + +install_file_list:: + @if [ -n "$(PROGRAM)" ]; then echo $(PROGRAMDIR)/$(PROGRAM); fi + @$(foreach f, $(addsuffix .8, $(PROGRAM)), \ + echo $(MANDIR8)/${MANPROGPREFIX}$f; \ + ) + @$(foreach f, $(EXTRA8MAN), \ + echo $(MANDIR8)/ipsec_$f; \ + ) + @$(foreach f, $(EXTRA5MAN), \ + echo $(MANDIR5)/$f;\ + ) + @$(foreach f, $(EXTRA5PROC), \ + echo $(MANDIR5)/ipsec_$f; \ + ) + @$(foreach f, $(LIBFILES), \ + echo $(LIBDIR)/$f;\ + ) + @$(foreach f, $(CONFFILES), \ + echo $(CONFDIR)/$f;\ + echo $(EXAMPLECONFDIR)/$f-sample;\ + ) + @$(foreach f, $(CONFDFILES), \ + echo $(CONFDDIR)/${CONFDSUBDIR}/$f;\ + ) + +endif + +# cancel the rule that compiles directly +%: %.c + +%: %.o $(OBJS) + $(CC) $(CFLAGS) -o $@ $@.o ${OBJS} $(LDFLAGS) $(LIBS) + +%: %.in ${FREESWANSRCDIR}/Makefile.inc ${FREESWANSRCDIR}/Makefile.ver + cat $< | sed -e "s/xxx/$(IPSECVERSION)/" \ + -e "s:@IPSEC_DIR@:$(FINALBINDIR):" \ + -e "s:@IPSEC_EXECDIR@:$(FINALLIBEXECDIR):" \ + -e "s:@IPSEC_SBINDIR@:$(FINALSBINDIR):" \ + -e "s:@IPSEC_LIBDIR@:$(FINALLIBDIR):" \ + -e "s:@FINALCONFDIR@:$(FINALCONFDIR):" \ + -e "s:@EXAMPLECONFDIR@:$(EXAMPLECONFDIR):" \ + -e "s:@FINALDOCDIR@:$(FINALDOCDIR):" \ + -e "s:@FINALEXAMPLECONFDIR@:$(FINALEXAMPLECONFDIR):" \ + -e "s:@MODULE_GOO_LIST@:$(MODULE_GOO_LIST):" \ + -e "s:@IPSEC_CONFS@:$(FINALCONFDIR):" \ + -e "s:@IPSEC_CONFDDIR@:$(FINALCONFDDIR):" \ + -e "s:@USE_IPROUTE2@:$(USE_IPROUTE2):" \ + -e "s:@IPSEC_FIREWALLTYPE@:$(IPSEC_FIREWALLTYPE):" \ + | cat >$@ + if [ -x $< ]; then chmod +x $@; fi + if [ "${PROGRAM}.in" = $< ]; then chmod +x $@; fi + +cleanall: clean + +distclean: clean + +mostlyclean: clean + +realclean: clean + +clean:: +ifneq ($(strip $(PROGRAM)),) + @if [ -r $(PROGRAM).in ]; then rm -f $(PROGRAM); fi + @if [ -r $(PROGRAM).c ]; then rm -f $(PROGRAM); fi + @if [ -n "$(OBJS)" ]; then rm -f $(PROGRAM); fi +endif + @rm -f *.o + +checkprograms: + diff --git a/src/_copyright/.cvsignore b/src/_copyright/.cvsignore new file mode 100644 index 000000000..23ebcb381 --- /dev/null +++ b/src/_copyright/.cvsignore @@ -0,0 +1 @@ +_copyright diff --git a/src/_copyright/Makefile b/src/_copyright/Makefile new file mode 100644 index 000000000..52c594b68 --- /dev/null +++ b/src/_copyright/Makefile @@ -0,0 +1,44 @@ +# Makefile for miscelaneous programs +# Copyright (C) 2002 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: Makefile,v 1.1 2004/03/15 20:35:27 as Exp $ + +FREESWANSRCDIR=../.. +include ${FREESWANSRCDIR}/Makefile.inc + +PROGRAM=_copyright +PROGRAMDIR=${LIBDIR} +LIBS=${FREESWANLIB} + +include ../Makefile.program + +# +# $Log: Makefile,v $ +# Revision 1.1 2004/03/15 20:35:27 as +# added files from freeswan-2.04-x509-1.5.3 +# +# Revision 1.3 2002/08/02 16:01:07 mcr +# moved user visible programs to $PREFIX/libexec, while moving +# private files to $PREFIX/lib. +# +# Revision 1.2 2002/06/02 22:02:14 mcr +# changed TOPDIR->FREESWANSRCDIR in all Makefiles. +# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the +# kernel sense.) +# +# Revision 1.1 2002/04/24 07:55:32 mcr +# #include patches and Makefiles for post-reorg compilation. +# +# +# + 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/.cvsignore b/src/_updown/.cvsignore new file mode 100644 index 000000000..81e2e4f86 --- /dev/null +++ b/src/_updown/.cvsignore @@ -0,0 +1,2 @@ +_updown +_updown.in diff --git a/src/_updown/Makefile b/src/_updown/Makefile new file mode 100644 index 000000000..e0aaab488 --- /dev/null +++ b/src/_updown/Makefile @@ -0,0 +1,22 @@ +# Makefile for miscelaneous programs +# Copyright (C) 2002 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: Makefile,v 1.3 2006/04/17 06:48:49 as Exp $ + +FREESWANSRCDIR=../.. +include ${FREESWANSRCDIR}/Makefile.inc + +PROGRAM=_updown +PROGRAMDIR=${LIBDIR} + +include ../Makefile.program 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/_updown.in b/src/_updown/_updown.in new file mode 100755 index 000000000..8db74f737 --- /dev/null +++ b/src/_updown/_updown.in @@ -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_espmark/Makefile b/src/_updown_espmark/Makefile new file mode 100644 index 000000000..bd9cd38cb --- /dev/null +++ b/src/_updown_espmark/Makefile @@ -0,0 +1,22 @@ +# Makefile for miscelaneous programs +# Copyright (C) 2002 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: Makefile,v 1.1 2005/04/07 21:34:19 as Exp $ + +FREESWANSRCDIR=../.. +include ${FREESWANSRCDIR}/Makefile.inc + +PROGRAM=_updown_espmark +PROGRAMDIR=${LIBDIR} + +include ../Makefile.program 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/_updown_espmark/_updown_espmark.in b/src/_updown_espmark/_updown_espmark.in new file mode 100644 index 000000000..3627d470d --- /dev/null +++ b/src/_updown_espmark/_updown_espmark.in @@ -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/charon/Doxyfile b/src/charon/Doxyfile new file mode 100644 index 000000000..5ee25a839 --- /dev/null +++ b/src/charon/Doxyfile @@ -0,0 +1,220 @@ +# Doxyfile 1.4.1-KDevelop + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = "charon" +PROJECT_NUMBER = 1.0 +OUTPUT_DIRECTORY = doc/api +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 1 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = NO +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = NO +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST = YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ./ +FILE_PATTERNS = *.h *.txt +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = NO +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = . +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = YES +ENUM_VALUES_PER_LINE = 1 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = YES +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = LEAK_DETECTIVE +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/src/charon/Makefile b/src/charon/Makefile new file mode 100644 index 000000000..b69438b84 --- /dev/null +++ b/src/charon/Makefile @@ -0,0 +1,99 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +FREESWANSRCDIR=../.. +# include strongswan Makefile, if charon sits in its tree +ifeq ($(shell ls $(FREESWANSRCDIR)/Makefile.inc 2>&1), ../../Makefile.inc) + include ${FREESWANSRCDIR}/Makefile.inc +else +# Defaults if not using strongswan defines + USE_LEAK_DETECTIVE?=false + INSTALL=install + INSTBINFLAGS=-b --suffix=.old + LIBEXECDIR=/usr/local/libexec/ipsec + SHAREDLIBDIR=/usr/local/lib +endif + + +BUILD_DIR= ./bin/ + +BINNAMECHARON= $(BUILD_DIR)charon +BINNAMESTROKE= $(BUILD_DIR)stroke +BINNAMETEST= $(BUILD_DIR)run_tests +BINNAMELIB= $(BUILD_DIR)libstrongswan.so + +MAIN_DIR= ./ + +CFLAGS= -Icharon -Ilib -Istroke -fPIC -Wall -g +ifeq ($(USE_LEAK_DETECTIVE),true) + CFLAGS+= -DLEAK_DETECTIVE +endif + +# objects is extended by each included Makefile +CHARON_OBJS= +LIB_OBJS= +TEST_OBJS= + +all : programs + +include $(MAIN_DIR)charon/Makefile.charon +include $(MAIN_DIR)lib/Makefile.lib +include $(MAIN_DIR)stroke/Makefile.stroke +include $(MAIN_DIR)testing/Makefile.testcases + +programs : $(BINNAMECHARON) $(BINNAMESTROKE) + +test : $(BINNAMETEST) + LD_LIBRARY_PATH=$(BUILD_DIR) $(BINNAMETEST) + +run : $(BINNAMECHARON) + LD_LIBRARY_PATH=$(BUILD_DIR) $(BINNAMECHARON) + +apidoc : + doxygen Doxyfile + +build_dir: + mkdir -p $(BUILD_DIR) + +$(BINNAMELIB) : build_dir $(LIB_OBJS) + $(CC) -lpthread -ldl -lgmp -shared $(LIB_OBJS) -o $@ + +$(BINNAMECHARON) : build_dir $(CHARON_OBJS) $(BINNAMELIB) $(BUILD_DIR)daemon.o + $(CC) -L./bin -lstrongswan $(CHARON_OBJS) $(BUILD_DIR)daemon.o -o $@ + +$(BINNAMETEST) : build_dir $(CHARON_OBJS) $(TEST_OBJS) $(BINNAMELIB) $(BUILD_DIR)testcases.o + $(CC) -L./bin -lstrongswan $(LDFLAGS) $(CHARON_OBJS) $(TEST_OBJS) $(BUILD_DIR)testcases.o -o $@ + +$(BINNAMESTROKE) : build_dir $(BINNAMELIB) $(BUILD_DIR)stroke.o + $(CC) $(LDFLAGS) $(CFLAGS) $(BUILD_DIR)stroke.o -o $@ + +install : $(BINNAMECHARON) $(BINNAMESTROKE) + $(INSTALL) $(INSTBINFLAGS) $(BINNAMECHARON) $(BINNAMESTROKE) $(LIBEXECDIR) + $(INSTALL) $(INSTBINFLAGS) $(BINNAMELIB) $(SHAREDLIBDIR) + +install_file_list: + @echo $(LIBEXECDIR)/charon + @echo $(LIBEXECDIR)/stroke + @echo $(SHAREDLIBDIR)/libstrongswan.so + +clean : + rm -fR $(BUILD_DIR) + +cleanall: clean + +distclean: clean + +mostlyclean: clean + +realclean: clean diff --git a/src/charon/charon.kdevelop b/src/charon/charon.kdevelop new file mode 100644 index 000000000..270e815c4 --- /dev/null +++ b/src/charon/charon.kdevelop @@ -0,0 +1,105 @@ + + + + Martin Willi + martin@strongswan.org + $VERSION$ + KDevCustomProject + C + + + + + Source + executable + + + + + + + + + + + + + ada + ada_bugs_gcc + bash + bash_bugs + clanlib + fortran_bugs_gcc + gnome1 + gnustep + gtk + gtk_bugs + haskell + haskell_bugs_ghc + java_bugs_gcc + java_bugs_sun + kde2book + libstdc++ + opengl + pascal_bugs_fp + php + php_bugs + perl + perl_bugs + python + python_bugs + qt-kdev3 + ruby + ruby_bugs + sdl + stl + sw + w3c-dom-level2-html + w3c-svg + w3c-uaag10 + wxwidgets_bugs + + + Guide to the Qt Translation Tools + Qt Assistant Manual + Qt Designer Manual + Qt Reference Documentation + qmake User Guide + + + KDE Libraries (Doxygen) + + + + + + + + + + + + + true + true + true + false + true + true + true + 250 + 400 + 250 + + + + + false + false + + + *.o,*.lo,CVS + false + + + diff --git a/src/charon/charon/Makefile.charon b/src/charon/charon/Makefile.charon new file mode 100644 index 000000000..336495db9 --- /dev/null +++ b/src/charon/charon/Makefile.charon @@ -0,0 +1,25 @@ +# 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. +# +CHARON_DIR= $(MAIN_DIR)charon/ + +$(BUILD_DIR)daemon.o : $(CHARON_DIR)daemon.c $(CHARON_DIR)daemon.h + $(CC) $(CFLAGS) -c -o $@ $< + + +include $(CHARON_DIR)network/Makefile.network +include $(CHARON_DIR)config/Makefile.config +include $(CHARON_DIR)encoding/Makefile.encoding +include $(CHARON_DIR)queues/Makefile.queues +include $(CHARON_DIR)sa/Makefile.sa +include $(CHARON_DIR)threads/Makefile.threads \ No newline at end of file diff --git a/src/charon/charon/config/Makefile.config b/src/charon/charon/config/Makefile.config new file mode 100644 index 000000000..d4638b318 --- /dev/null +++ b/src/charon/charon/config/Makefile.config @@ -0,0 +1,32 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +CONFIG_DIR= $(CHARON_DIR)config/ + + +CHARON_OBJS+= $(BUILD_DIR)traffic_selector.o +$(BUILD_DIR)traffic_selector.o : $(CONFIG_DIR)traffic_selector.c $(CONFIG_DIR)traffic_selector.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)proposal.o +$(BUILD_DIR)proposal.o : $(CONFIG_DIR)proposal.c $(CONFIG_DIR)proposal.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)configuration.o +$(BUILD_DIR)configuration.o : $(CONFIG_DIR)configuration.c $(CONFIG_DIR)configuration.h + $(CC) $(CFLAGS) -c -o $@ $< + +include $(CONFIG_DIR)connections/Makefile.connections +include $(CONFIG_DIR)credentials/Makefile.credentials +include $(CONFIG_DIR)policies/Makefile.policies \ No newline at end of file diff --git a/src/charon/charon/config/configuration.c b/src/charon/charon/config/configuration.c new file mode 100755 index 000000000..eac1bd43a --- /dev/null +++ b/src/charon/charon/config/configuration.c @@ -0,0 +1,112 @@ +/** + * @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 "configuration.h" + +#include + +/** + * First retransmit timeout in milliseconds. + * Timeout value is increasing in each retransmit round. + */ +#define RETRANSMIT_TIMEOUT 3000 + +/** + * Timeout in milliseconds after that a half open IKE_SA gets deleted. + */ +#define HALF_OPEN_IKE_SA_TIMEOUT 30000 + +/** + * Max retransmit count. + * 0 for infinite. The max time a half open IKE_SA is alive is set by + * RETRANSMIT_TIMEOUT. + */ +#define MAX_RETRANSMIT_COUNT 0 + + +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 status_t get_retransmit_timeout (private_configuration_t *this, u_int32_t retransmit_count, u_int32_t *timeout) +{ + int new_timeout = RETRANSMIT_TIMEOUT, i; + if (retransmit_count > MAX_RETRANSMIT_COUNT && MAX_RETRANSMIT_COUNT != 0) + { + return FAILED; + } + + for (i = 0; i < retransmit_count; i++) + { + new_timeout *= 2; + } + + *timeout = new_timeout; + + return SUCCESS; +} + +/** + * 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.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 = (status_t (*) (configuration_t *, u_int32_t retransmit_count, u_int32_t *timeout))get_retransmit_timeout; + this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t *)) get_half_open_ike_sa_timeout; + + return (&this->public); +} diff --git a/src/charon/charon/config/configuration.h b/src/charon/charon/config/configuration.h new file mode 100755 index 000000000..6b741f9fb --- /dev/null +++ b/src/charon/charon/config/configuration.h @@ -0,0 +1,89 @@ +/** + * @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_ + +#include + + +typedef struct configuration_t configuration_t; + +/** + * @brief The interface for various daemon related configs. + * + * @b Constructors: + * - configuration_create() + * + * @ingroup config + */ +struct configuration_t { + + /** + * @brief Returns the retransmit timeout. + * + * The timeout values are managed by the configuration, so + * another backoff algorithm may be implemented here. + * + * @param this calling object + * @param retransmit_count number of times a message was retransmitted so far + * @param[out] timeout the new retransmit timeout in milliseconds + * + * @return + * - FAILED, if the message should not be retransmitted + * - SUCCESS + */ + status_t (*get_retransmit_timeout) (configuration_t *this, u_int32_t retransmit_count, u_int32_t *timeout); + + /** + * @brief Returns the timeout for an half open IKE_SA in ms. + * + * Half open means that the IKE_SA is still in one of the following states: + * - INITIATOR_INIT + * - RESPONDER_INIT + * - IKE_SA_INIT_REQUESTED + * - IKE_SA_INIT_RESPONDED + * - IKE_AUTH_REQUESTED + * + * @param this calling object + * @return timeout in milliseconds (ms) + */ + u_int32_t (*get_half_open_ike_sa_timeout) (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(); + +#endif /*CONFIGURATION_H_*/ diff --git a/src/charon/charon/config/connections/Makefile.connections b/src/charon/charon/config/connections/Makefile.connections new file mode 100644 index 000000000..8fbc983f6 --- /dev/null +++ b/src/charon/charon/config/connections/Makefile.connections @@ -0,0 +1,24 @@ +# 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. +# + +CONNECTIONS_DIR= $(CONFIG_DIR)connections/ + + +CHARON_OBJS+= $(BUILD_DIR)connection.o +$(BUILD_DIR)connection.o : $(CONNECTIONS_DIR)connection.c $(CONNECTIONS_DIR)connection.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)local_connection_store.o +$(BUILD_DIR)local_connection_store.o : $(CONNECTIONS_DIR)local_connection_store.c $(CONNECTIONS_DIR)local_connection_store.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/charon/config/connections/connection.c b/src/charon/charon/config/connections/connection.c new file mode 100644 index 000000000..74e6762b4 --- /dev/null +++ b/src/charon/charon/config/connections/connection.c @@ -0,0 +1,367 @@ +/** + * @file connection.c + * + * @brief Implementation of connection_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "connection.h" + +#include +#include + +/** + * String mappings for auth_method_t. + */ +mapping_t auth_method_m[] = { + {RSA_DIGITAL_SIGNATURE, "RSA"}, + {SHARED_KEY_MESSAGE_INTEGRITY_CODE, "SHARED_KEY"}, + {DSS_DIGITAL_SIGNATURE, "DSS"}, + {MAPPING_END, NULL} +}; + + +typedef struct private_connection_t private_connection_t; + +/** + * Private data of an connection_t object + */ +struct private_connection_t { + + /** + * Public part + */ + connection_t public; + + /** + * Name of the connection + */ + char *name; + + /** + * ID of us + */ + identification_t *my_id; + + /** + * ID of remote peer + */ + identification_t *other_id; + + /** + * Host information of my host. + */ + host_t *my_host; + + /** + * Host information of other host. + */ + host_t *other_host; + + /** + * Method to use for own authentication data + */ + auth_method_t auth_method; + + /** + * Supported proposals + */ + linked_list_t *proposals; +}; + +/** + * Implementation of connection_t.get_name. + */ +static char *get_name (private_connection_t *this) +{ + return this->name; +} + +/** + * Implementation of connection_t.get_my_id. + */ +static identification_t *get_my_id (private_connection_t *this) +{ + return this->my_id; +} + +/** + * Implementation of connection_t.get_other_id. + */ +static identification_t *get_other_id(private_connection_t *this) +{ + return this->other_id; +} + +/** + * Implementation of connection_t.update_my_id + */ +static void update_my_id(private_connection_t *this, identification_t *my_id) +{ + this->my_id->destroy(this->my_id); + this->my_id = my_id; +} + +/** + * Implementation of connection_t.update_other_id + */ +static void update_other_id(private_connection_t *this, identification_t *other_id) +{ + this->other_id->destroy(this->other_id); + this->other_id = other_id; +} + +/** + * 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.update_my_host. + */ +static void update_my_host(private_connection_t *this, host_t *my_host) +{ + this->my_host->destroy(this->my_host); + this->my_host = my_host; +} + +/** + * Implementation of connection_t.update_other_host. + */ +static void update_other_host(private_connection_t *this, host_t *other_host) +{ + this->other_host->destroy(this->other_host); + this->other_host = other_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) +{ + return this->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->has_next(stored_iter)) + { + supplied_iter->reset(supplied_iter); + stored_iter->current(stored_iter, (void**)&stored); + + while (supplied_iter->has_next(supplied_iter)) + { + supplied_iter->current(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.auth_method_t. + */ +static auth_method_t get_auth_method(private_connection_t *this) +{ + return this->auth_method; +} + +/** + * 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; + + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&proposal); + proposal->get_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, &algo); + if (algo) + { + iterator->destroy(iterator); + return algo->algorithm; + } + } + iterator->destroy(iterator); + return MODP_UNDEFINED; +} + +/** + * 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->has_next(prop_iter)) + { + prop_iter->current(prop_iter, (void**)&proposal); + alg_iter = proposal->create_algorithm_iterator(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP); + while (alg_iter->has_next(alg_iter)) + { + alg_iter->current(alg_iter, (void**)&algo); + if (algo->algorithm == dh_group) + { + prop_iter->destroy(prop_iter); + alg_iter->destroy(alg_iter); + return TRUE; + } + } + } + prop_iter->destroy(prop_iter); + alg_iter->destroy(alg_iter); + return FALSE; +} + +/** + * Implementation of connection_t.clone. + */ +static connection_t *clone(private_connection_t *this) +{ + iterator_t *iterator; + proposal_t *proposal; + private_connection_t *clone = (private_connection_t*)connection_create( + this->name, + this->my_host->clone(this->my_host), + this->other_host->clone(this->other_host), + this->my_id->clone(this->my_id), + this->other_id->clone(this->other_id), + this->auth_method); + + /* clone all proposals */ + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&proposal); + proposal = proposal->clone(proposal); + clone->proposals->insert_last(clone->proposals, (void*)proposal); + } + iterator->destroy(iterator); + + return &clone->public; +} + +/** + * Implementation of connection_t.destroy. + */ +static void destroy (private_connection_t *this) +{ + proposal_t *proposal; + + while (this->proposals->remove_last(this->proposals, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + this->proposals->destroy(this->proposals); + + 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); + free(this->name); + free(this); +} + +/** + * Described in header. + */ +connection_t * connection_create(char *name, host_t *my_host, host_t *other_host, identification_t *my_id, identification_t *other_id, auth_method_t auth_method) +{ + private_connection_t *this = malloc_thing(private_connection_t); + + /* public functions */ + this->public.get_name = (char*(*)(connection_t*))get_name; + this->public.get_my_id = (identification_t*(*)(connection_t*))get_my_id; + this->public.get_other_id = (identification_t*(*)(connection_t*))get_other_id; + this->public.get_my_host = (host_t*(*)(connection_t*))get_my_host; + this->public.update_my_host = (void(*)(connection_t*,host_t*))update_my_host; + this->public.update_other_host = (void(*)(connection_t*,host_t*))update_other_host; + this->public.update_my_id = (void(*)(connection_t*,identification_t*))update_my_id; + this->public.update_other_id = (void(*)(connection_t*,identification_t*))update_other_id; + 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_auth_method = (auth_method_t(*)(connection_t*)) get_auth_method; + 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.clone = (connection_t*(*)(connection_t*))clone; + this->public.destroy = (void(*)(connection_t*))destroy; + + /* private variables */ + this->name = strdup(name); + this->my_host = my_host; + this->other_host = other_host; + this->my_id = my_id; + this->other_id = other_id; + this->auth_method = auth_method; + + this->proposals = linked_list_create(); + + return (&this->public); +} diff --git a/src/charon/charon/config/connections/connection.h b/src/charon/charon/config/connections/connection.h new file mode 100644 index 000000000..2cb3c20b8 --- /dev/null +++ b/src/charon/charon/config/connections/connection.h @@ -0,0 +1,283 @@ +/** + * @file connection.h + * + * @brief Interface of connection_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_H_ +#define CONNECTION_H_ + +#include +#include +#include +#include +#include +#include + + +typedef enum auth_method_t auth_method_t; + +/** + * AUTH Method to use. + * + * @ingroup config + */ +enum auth_method_t { + /** + * Computed as specified in section 2.15 of RFC using + * an RSA private key over a PKCS#1 padded hash. + */ + RSA_DIGITAL_SIGNATURE = 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 + */ + SHARED_KEY_MESSAGE_INTEGRITY_CODE = 2, + + /** + * Computed as specified in section 2.15 of RFC using a + * DSS private key over a SHA-1 hash. + */ + DSS_DIGITAL_SIGNATURE = 3, +}; + +/** + * string mappings for auth method. + * + * @ingroup config + */ +extern mapping_t auth_method_m[]; + + +typedef struct connection_t connection_t; + +/** + * @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 ID for this connection. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as identification_t object + */ + identification_t *(*get_my_id) (connection_t *this); + + /** + * @brief Get others ID for this connection. + * + * Object is NOT getting cloned. + * + * @param this calling object + * @return host information as identification_t object + */ + identification_t *(*get_other_id) (connection_t *this); + + /** + * @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 Update address of my host. + * + * It may be necessary to uptdate own address, as it + * is set to the default route (0.0.0.0) in some cases. + * Old host is destroyed, new one NOT cloned. + * + * @param this calling object + * @param my_host new host to set as my_host + */ + void (*update_my_host) (connection_t *this, host_t *my_host); + + /** + * @brief Update address of remote host. + * + * It may be necessary to uptdate remote address, as a + * connection may define %any (0.0.0.0) or a subnet. + * Old host is destroyed, new one NOT cloned. + * + * @param this calling object + * @param my_host new host to set as other_host + */ + void (*update_other_host) (connection_t *this, host_t *other_host); + + /** + * @brief Update own ID. + * + * It may be necessary to uptdate own ID, as it + * is set to %any or to e.g. *@strongswan.org in + * some cases. + * Old ID is destroyed, new one NOT cloned. + * + * @param this calling object + * @param my_id new ID to set as my_id + */ + void (*update_my_id) (connection_t *this, identification_t *my_id); + + /** + * @brief Update others ID. + * + * It may be necessary to uptdate others ID, as it + * is set to %any or to e.g. *@strongswan.org in + * some cases. + * Old ID is destroyed, new one NOT cloned. + * + * @param this calling object + * @param other_id new ID to set as other_id + */ + void (*update_other_id) (connection_t *this, identification_t *other_id); + + /** + * @brief Returns a list of all supported proposals. + * + * Returned list is still owned by connection and MUST NOT + * modified or destroyed. + * + * @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 authentication method to use + * + * @param this calling object + * @return authentication method + */ + auth_method_t (*get_auth_method) (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 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 Clone a connection_t object. + * + * @param this connection to clone + * @return clone of it + */ + connection_t *(*clone) (connection_t *this); + + /** + * @brief Destroys a connection_t object. + * + * @param this calling object + */ + void (*destroy) (connection_t *this); +}; + +/** + * @brief Creates a connection_t object. + * + * Supplied hosts/IDs become owned by connection, so + * do not modify or destroy them after a call to + * connection_create(). Name gets cloned internally. + * + * @param name connection identifier + * @param my_host host_t representing local address + * @param other_host host_t representing remote address + * @param my_id identification_t for me + * @param other_id identification_t for other + * @param auth_method Authentication method to use for our(!) auth data + * @return connection_t object. + * + * @ingroup config + */ +connection_t * connection_create(char *name, + host_t *my_host, host_t *other_host, + identification_t *my_id, + identification_t *other_id, + auth_method_t auth_method); + +#endif /* CONNECTION_H_ */ diff --git a/src/charon/charon/config/connections/connection_store.h b/src/charon/charon/config/connections/connection_store.h new file mode 100755 index 000000000..41fd58e42 --- /dev/null +++ b/src/charon/charon/config/connections/connection_store.h @@ -0,0 +1,112 @@ +/** + * @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_ + +#include +#include + + +typedef struct connection_store_t connection_store_t; + +/** + * @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 IDs. + * + * This call is useful to get a connection which is identified by IDs + * rather than addresses, e.g. for connection setup on user request. + * The returned connection gets created/cloned and therefore must + * be destroyed after usage. + * + * @param this calling object + * @param my_id own ID of connection + * @param other_id others ID of connection + * @return + * - connection_t, if found + * - NULL otherwise + */ + connection_t *(*get_connection_by_ids) (connection_store_t *this, identification_t *my_id, identification_t *other_id); + + /** + * @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 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/charon/config/connections/local_connection_store.c b/src/charon/charon/config/connections/local_connection_store.c new file mode 100644 index 000000000..3f07f0d21 --- /dev/null +++ b/src/charon/charon/config/connections/local_connection_store.c @@ -0,0 +1,228 @@ +/** + * @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; + + /** + * Assigned logger + */ + logger_t *logger; +}; + + +/** + * 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) +{ + iterator_t *iterator; + connection_t *current, *found = NULL; + + this->logger->log(this->logger, CONTROL|LEVEL1, "getting config for hosts %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->has_next(iterator)) + { + host_t *config_my_host, *config_other_host; + + iterator->current(iterator, (void**)¤t); + + config_my_host = current->get_my_host(current); + config_other_host = current->get_other_host(current); + + /* first check if ip is equal */ + if(config_other_host->ip_equals(config_other_host, other_host)) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "config entry with remote host %s", + config_other_host->get_address(config_other_host)); + /* could be right one, check my_host for default route*/ + if (config_my_host->is_default_route(config_my_host)) + { + found = current->clone(current); + break; + } + /* check now if host informations are the same */ + else if (config_my_host->ip_equals(config_my_host,my_host)) + { + found = current->clone(current); + break; + } + + } + /* Then check for wildcard hosts! + * TODO + * actually its only checked if other host with default route can be found! */ + else if (config_other_host->is_default_route(config_other_host)) + { + /* could be right one, check my_host for default route*/ + if (config_my_host->is_default_route(config_my_host)) + { + found = current->clone(current); + break; + } + /* check now if host informations are the same */ + else if (config_my_host->ip_equals(config_my_host,my_host)) + { + found = current->clone(current); + break; + } + } + } + iterator->destroy(iterator); + + /* apply hosts as they are supplied since my_host may be %defaultroute, and other_host may be %any. */ + if (found) + { + found->update_my_host(found, my_host->clone(my_host)); + found->update_other_host(found, other_host->clone(other_host)); + } + + return found; +} + +/** + * Implementation of connection_store_t.get_connection_by_ids. + */ +static connection_t *get_connection_by_ids(private_local_connection_store_t *this, identification_t *my_id, identification_t *other_id) +{ + iterator_t *iterator; + connection_t *current, *found = NULL; + + this->logger->log(this->logger, CONTROL|LEVEL1, "getting config for ids %s - %s", + my_id->get_string(my_id), other_id->get_string(other_id)); + + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->has_next(iterator)) + { + identification_t *config_my_id, *config_other_id; + + iterator->current(iterator, (void**)¤t); + + config_my_id = current->get_my_id(current); + config_other_id = current->get_other_id(current); + + /* first check if ids are equal + * TODO: Add wildcard checks */ + if (config_other_id->equals(config_other_id, other_id) && + config_my_id->equals(config_my_id, my_id)) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "config entry with remote id %s", + config_other_id->get_string(config_other_id)); + found = current->clone(current); + break; + } + } + iterator->destroy(iterator); + + 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; + + iterator = this->connections->create_iterator(this->connections, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t); + if (strcmp(name, current->get_name(current)) == 0) + { + found = current->clone(current); + break; + } + } + iterator->destroy(iterator); + + return found; +} + +/** + * Implementation of connection_store_t.add_connection. + */ +static status_t add_connection(private_local_connection_store_t *this, connection_t *connection) +{ + this->connections->insert_last(this->connections, connection); + return SUCCESS; +} + +/** + * Implementation of connection_store_t.destroy. + */ +static void destroy (private_local_connection_store_t *this) +{ + connection_t *connection; + + while (this->connections->remove_last(this->connections, (void**)&connection) == SUCCESS) + { + connection->destroy(connection); + } + this->connections->destroy(this->connections); + free(this); +} + +/** + * Described in header. + */ +local_connection_store_t * local_connection_store_create() +{ + 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_ids = (connection_t*(*)(connection_store_t*,identification_t*,identification_t*))get_connection_by_ids; + this->public.connection_store.get_connection_by_name = (connection_t*(*)(connection_store_t*,char*))get_connection_by_name; + this->public.connection_store.add_connection = (status_t(*)(connection_store_t*,connection_t*))add_connection; + this->public.connection_store.destroy = (void(*)(connection_store_t*))destroy; + + /* private variables */ + this->connections = linked_list_create(); + this->logger = logger_manager->get_logger(logger_manager, CONFIG); + + return (&this->public); +} diff --git a/src/charon/charon/config/connections/local_connection_store.h b/src/charon/charon/config/connections/local_connection_store.h new file mode 100644 index 000000000..14a0a24ae --- /dev/null +++ b/src/charon/charon/config/connections/local_connection_store.h @@ -0,0 +1,63 @@ +/** + * @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_ + +#include +#include + + +typedef struct local_connection_store_t local_connection_store_t; + +/** + * @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(); + +#endif /* LOCAL_CONNECTION_H_ */ diff --git a/src/charon/charon/config/credentials/Makefile.credentials b/src/charon/charon/config/credentials/Makefile.credentials new file mode 100644 index 000000000..720d56656 --- /dev/null +++ b/src/charon/charon/config/credentials/Makefile.credentials @@ -0,0 +1,20 @@ +# 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. +# + +CREDENTIALS_DIR= $(CONFIG_DIR)credentials/ + + +CHARON_OBJS+= $(BUILD_DIR)local_credential_store.o +$(BUILD_DIR)local_credential_store.o : $(CREDENTIALS_DIR)local_credential_store.c $(CREDENTIALS_DIR)local_credential_store.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/charon/config/credentials/credential_store.h b/src/charon/charon/config/credentials/credential_store.h new file mode 100755 index 000000000..2339469c0 --- /dev/null +++ b/src/charon/charon/config/credentials/credential_store.h @@ -0,0 +1,91 @@ +/** + * @file credential_store.h + * + * @brief Interface credential_store_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 CREDENTIAL_STORE_H_ +#define CREDENTIAL_STORE_H_ + +#include +#include +#include +#include + + +typedef struct credential_store_t credential_store_t; + +/** + * @brief The interface for a credential_store backend. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct credential_store_t { + + /** + * @brief Returns the preshared secret of a specific ID. + * + * The returned chunk must be destroyed by the caller after usage. + * + * @param this calling object + * @param identification identification_t object identifiying the secret. + * @param[out] preshared_secret the preshared secret will be written there. + * @return + * - NOT_FOUND if no preshared secrets for specific ID could be found + * - SUCCESS + * + * @todo We should use two IDs to query shared secrets, since we want to use different + * keys for different peers... + */ + status_t (*get_shared_secret) (credential_store_t *this, identification_t *identification, chunk_t *preshared_secret); + + /** + * @brief Returns the RSA public key of a specific ID. + * + * The returned rsa_public_key_t must be destroyed by the caller after usage. + * + * @param this calling object + * @param identification 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 *identification); + + /** + * @brief Returns the RSA private key of a specific ID. + * + * The returned rsa_private_key_t must be destroyed by the caller after usage. + * + * @param this calling object + * @param identification identification_t object identifiying the key + * @return private key, or NULL if not found + */ + rsa_private_key_t *(*get_rsa_private_key) (credential_store_t *this, identification_t *identification); + + /** + * @brief Destroys a credential_store_t object. + * + * @param this calling object + */ + void (*destroy) (credential_store_t *this); +}; + +#endif /*CREDENTIAL_STORE_H_*/ diff --git a/src/charon/charon/config/credentials/local_credential_store.c b/src/charon/charon/config/credentials/local_credential_store.c new file mode 100644 index 000000000..dc6cb6c50 --- /dev/null +++ b/src/charon/charon/config/credentials/local_credential_store.c @@ -0,0 +1,315 @@ +/** + * @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 "local_credential_store.h" + +#include +#include +#include + + +typedef struct key_entry_t key_entry_t; + +/** + * Private key with an associated ID to find it + */ +struct key_entry_t { + + /** + * ID, as added + */ + identification_t *id; + + /** + * Associated rsa private key + */ + rsa_private_key_t *key; +}; + + +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 key_entry_t's with private keys + */ + linked_list_t *private_keys; + + /** + * list of x509 certificates with public keys + */ + linked_list_t *certificates; + + /** + * Assigned logger + */ + logger_t *logger; +}; + + +/** + * Implementation of credential_store_t.get_shared_secret. + */ +static status_t get_shared_secret(private_local_credential_store_t *this, identification_t *identification, chunk_t *preshared_secret) +{ + return FAILED; +} + +/** + * Implementation of credential_store_t.get_rsa_public_key. + */ +static rsa_public_key_t * get_rsa_public_key(private_local_credential_store_t *this, identification_t *identification) +{ + x509_t *current; + rsa_public_key_t *found = NULL; + iterator_t *iterator; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Looking for public key for %s", + identification->get_string(identification)); + iterator = this->certificates->create_iterator(this->certificates, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t); + identification_t *stored = current->get_subject(current); + this->logger->log(this->logger, CONTROL|LEVEL2, "there is one for %s", + stored->get_string(stored)); + if (identification->equals(identification, stored)) + { + found = current->get_public_key(current); + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implementation of credential_store_t.get_rsa_private_key. + */ +static rsa_private_key_t *get_rsa_private_key(private_local_credential_store_t *this, identification_t *identification) +{ + rsa_private_key_t *found = NULL; + key_entry_t *current; + iterator_t *iterator; + + iterator = this->private_keys->create_iterator(this->private_keys, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t); + if (identification->equals(identification, current->id)) + { + found = current->key->clone(current->key); + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implements local_credential_store_t.load_private_keys + */ +static void load_certificates(private_local_credential_store_t *this, char *path) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + x509_t *cert; + + dir = opendir(path); + if (dir == NULL) { + this->logger->log(this->logger, ERROR, "error opening certificate directory \"%s\"", path); + return; + } + while ((entry = readdir(dir)) != NULL) + { + char file[256]; + 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) + { + cert = x509_create_from_file(file); + if (cert) + { + this->certificates->insert_last(this->certificates, (void*)cert); + this->logger->log(this->logger, CONTROL|LEVEL1, "loaded certificate \"%s\"", file); + } + else + { + this->logger->log(this->logger, ERROR, "certificate \"%s\" invalid, skipped", file); + } + } + } + closedir(dir); +} + +/** + * Query the ID for a private key, by doing a lookup in the certificates + */ +static identification_t *get_id_for_private_key(private_local_credential_store_t *this, rsa_private_key_t *private_key) +{ + iterator_t *iterator; + x509_t *cert; + identification_t *found = NULL; + rsa_public_key_t *public_key; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Getting ID for a private key..."); + + iterator = this->certificates->create_iterator(this->certificates, TRUE); + while (!found && iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&cert); + public_key = cert->get_public_key(cert); + if (public_key) + { + if (private_key->belongs_to(private_key, public_key)) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "found a match"); + found = cert->get_subject(cert); + found = found->clone(found); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "this one did not match"); + } + public_key->destroy(public_key); + } + } + iterator->destroy(iterator); + return found; +} + +/** + * Implements local_credential_store_t.load_private_keys + */ +static void load_private_keys(private_local_credential_store_t *this, char *path) +{ + struct dirent* entry; + struct stat stb; + DIR* dir; + rsa_private_key_t *key; + + dir = opendir(path); + if (dir == NULL) { + this->logger->log(this->logger, ERROR, "error opening private key directory \"%s\"", path); + return; + } + while ((entry = readdir(dir)) != NULL) + { + char file[256]; + 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) + { + key = rsa_private_key_create_from_file(file, NULL); + if (key) + { + key_entry_t *entry; + identification_t *id = get_id_for_private_key(this, key); + if (!id) + { + this->logger->log(this->logger, ERROR, + "no certificate found for private key \"%s\", skipped", file); + key->destroy(key); + continue; + } + entry = malloc_thing(key_entry_t); + entry->key = key; + entry->id = id; + this->private_keys->insert_last(this->private_keys, (void*)entry); + this->logger->log(this->logger, CONTROL|LEVEL1, "loaded private key \"%s\"", file); + } + else + { + this->logger->log(this->logger, ERROR, "private key \"%s\" invalid, skipped", file); + } + } + } + closedir(dir); +} + +/** + * Implementation of credential_store_t.destroy. + */ +static void destroy(private_local_credential_store_t *this) +{ + x509_t *certificate; + key_entry_t *key_entry; + + while (this->certificates->remove_last(this->certificates, (void**)&certificate) == SUCCESS) + { + certificate->destroy(certificate); + } + this->certificates->destroy(this->certificates); + while (this->private_keys->remove_last(this->private_keys, (void**)&key_entry) == SUCCESS) + { + key_entry->id->destroy(key_entry->id); + key_entry->key->destroy(key_entry->key); + free(key_entry); + } + this->private_keys->destroy(this->private_keys); + free(this); +} + +/** + * Described in header. + */ +local_credential_store_t * local_credential_store_create() +{ + private_local_credential_store_t *this = malloc_thing(private_local_credential_store_t); + + this->public.credential_store.get_shared_secret = (status_t(*)(credential_store_t*,identification_t*,chunk_t*))get_shared_secret; + this->public.credential_store.get_rsa_private_key = (rsa_private_key_t*(*)(credential_store_t*,identification_t*))get_rsa_private_key; + this->public.credential_store.get_rsa_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_rsa_public_key; + this->public.load_certificates = (void(*)(local_credential_store_t*,char*))load_certificates; + this->public.load_private_keys = (void(*)(local_credential_store_t*,char*))load_private_keys; + this->public.credential_store.destroy = (void(*)(credential_store_t*))destroy; + + /* private variables */ + this->private_keys = linked_list_create(); + this->certificates = linked_list_create(); + this->logger = logger_manager->get_logger(logger_manager, CONFIG); + + return (&this->public); +} diff --git a/src/charon/charon/config/credentials/local_credential_store.h b/src/charon/charon/config/credentials/local_credential_store.h new file mode 100644 index 000000000..ab9ef88d7 --- /dev/null +++ b/src/charon/charon/config/credentials/local_credential_store.h @@ -0,0 +1,84 @@ +/** + * @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_ + +#include +#include + + +typedef struct local_credential_store_t local_credential_store_t; + +/** + * @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 can be loaded + * from folders. + * Shared secret are not handled yet, so get_shared_secret always returns NOT_FOUND. + * + * @b Constructors: + * - local_credential_store_create() + * + * @ingroup config + */ +struct local_credential_store_t { + + /** + * Implements credential_store_t interface + */ + credential_store_t credential_store; + + /** + * @brief Loads trusted certificates from a folder. + * + * Currently, all keys must be in binary DER format. + * + * @param this calling object + * @param path directory to load certificates from + */ + void (*load_certificates) (local_credential_store_t *this, char *path); + + /** + * @brief Loads RSA private keys from a folder. + * + * Currently, all keys must be unencrypted in binary DER format. Anything + * other gets ignored. Further, a certificate for the specific private + * key must already be loaded to get the ID from. + * + * @param this calling object + * @param path directory to load keys from + */ + void (*load_private_keys) (local_credential_store_t *this, char *path); +}; + +/** + * @brief Creates a local_credential_store_t instance. + * + * @return credential store instance. + * + * @ingroup config + */ +local_credential_store_t *local_credential_store_create(); + +#endif /* LOCAL_CREDENTIAL_H_ */ diff --git a/src/charon/charon/config/policies/Makefile.policies b/src/charon/charon/config/policies/Makefile.policies new file mode 100644 index 000000000..e7ed8ab13 --- /dev/null +++ b/src/charon/charon/config/policies/Makefile.policies @@ -0,0 +1,24 @@ +# 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. +# + +POLICIES_DIR= $(CONFIG_DIR)policies/ + + +CHARON_OBJS+= $(BUILD_DIR)policy.o +$(BUILD_DIR)policy.o : $(POLICIES_DIR)policy.c $(POLICIES_DIR)policy.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)local_policy_store.o +$(BUILD_DIR)local_policy_store.o : $(POLICIES_DIR)local_policy_store.c $(POLICIES_DIR)local_policy_store.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/charon/config/policies/local_policy_store.c b/src/charon/charon/config/policies/local_policy_store.c new file mode 100644 index 000000000..ae02357ea --- /dev/null +++ b/src/charon/charon/config/policies/local_policy_store.c @@ -0,0 +1,136 @@ +/** + * @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 "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; + + /** + * Assigned logger + */ + logger_t *logger; +}; + +/** + * Implementation of policy_store_t.add_policy. + */ +static void add_policy(private_local_policy_store_t *this, policy_t *policy) +{ + this->policies->insert_last(this->policies, (void*)policy); +} + + +/** + * 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) +{ + iterator_t *iterator; + policy_t *current, *found = NULL; + + this->logger->log(this->logger, CONTROL|LEVEL1, "Looking for policy for IDs %s - %s", + my_id ? my_id->get_string(my_id) : "%any", + other_id->get_string(other_id)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void **)¤t); + identification_t *config_my_id = current->get_my_id(current); + identification_t *config_other_id = current->get_other_id(current); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Found one for %s - %s", + config_my_id->get_string(config_my_id), + config_other_id->get_string(config_other_id)); + + /* check other host first */ + if (other_id->belongs_to(other_id, config_other_id)) + { + /* get it if my_id not specified */ + if (my_id->belongs_to(my_id, config_my_id)) + { + found = current->clone(current); + break; + } + } + } + iterator->destroy(iterator); + + /* apply IDs as they are requsted, since they may be configured as %any or such */ + if (found) + { + found->update_my_id(found, my_id->clone(my_id)); + found->update_other_id(found, other_id->clone(other_id)); + } + return found; +} + +/** + * Implementation of policy_store_t.destroy. + */ +static void destroy(private_local_policy_store_t *this) +{ + policy_t *policy; + + while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) + { + policy->destroy(policy); + } + this->policies->destroy(this->policies); + free(this); +} + +/** + * Described in header. + */ +local_policy_store_t *local_policy_store_create() +{ + 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*))get_policy; + this->public.policy_store.destroy = (void(*)(policy_store_t*))destroy; + + /* private variables */ + this->policies = linked_list_create(); + this->logger = logger_manager->get_logger(logger_manager, CONFIG); + + return (&this->public); +} diff --git a/src/charon/charon/config/policies/local_policy_store.h b/src/charon/charon/config/policies/local_policy_store.h new file mode 100644 index 000000000..7ab9e0efd --- /dev/null +++ b/src/charon/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_ + +#include +#include + + +typedef struct local_policy_store_t local_policy_store_t; + +/** + * @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(); + +#endif /* LOCAL_POLICY_STORE_H_ */ diff --git a/src/charon/charon/config/policies/policy.c b/src/charon/charon/config/policies/policy.c new file mode 100644 index 000000000..cff87fc6b --- /dev/null +++ b/src/charon/charon/config/policies/policy.c @@ -0,0 +1,397 @@ +/** + * @file policy.c + * + * @brief Implementation of policy_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "policy.h" + +#include +#include +#include + +typedef struct private_policy_t private_policy_t; + +/** + * Private data of an policy_t object + */ +struct private_policy_t { + + /** + * Public part + */ + policy_t public; + + /** + * id to use to identify us + */ + identification_t *my_id; + + /** + * allowed id for other + */ + identification_t *other_id; + + /** + * 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; + + /** + * select_traffic_selectors for both + */ + linked_list_t *(*select_traffic_selectors) (private_policy_t *,linked_list_t*,linked_list_t*); +}; + +/** + * 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.update_my_id + */ +static void update_my_id(private_policy_t *this, identification_t *my_id) +{ + this->my_id->destroy(this->my_id); + this->my_id = my_id; +} + +/** + * Implementation of policy_t.update_other_id + */ +static void update_other_id(private_policy_t *this, identification_t *other_id) +{ + this->other_id->destroy(this->other_id); + this->other_id = other_id; +} + +/** + * Helper function which does the work for policy_t.update_my_ts and update_other_ts + */ +static void update_ts(linked_list_t* list, host_t *new_host) +{ + traffic_selector_t *ts; + iterator_t *iterator; + + iterator = list->create_iterator(list, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&ts); + ts->update_address_range(ts, new_host); + } + iterator->destroy(iterator); +} + +/** + * Implementation of policy_t.update_my_id + */ +static void update_my_ts(private_policy_t *this, host_t *my_host) +{ + update_ts(this->my_ts, my_host); +} + +/** + * Implementation of policy_t.update_other_ts + */ +static void update_other_ts(private_policy_t *this, host_t *my_host) +{ + update_ts(this->other_ts, my_host); +} + +/** + * Implementation of policy_t.get_my_traffic_selectors + */ +static linked_list_t *get_my_traffic_selectors(private_policy_t *this) +{ + return this->my_ts; +} + +/** + * Implementation of policy_t.get_other_traffic_selectors + */ +static linked_list_t *get_other_traffic_selectors(private_policy_t *this, traffic_selector_t **traffic_selectors[]) +{ + return this->other_ts; +} + +/** + * 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) +{ + return this->select_traffic_selectors(this, this->my_ts, supplied); +} + +/** + * 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) +{ + return this->select_traffic_selectors(this, this->other_ts, supplied); +} +/** + * Implementation of private_policy_t.select_traffic_selectors + */ +static linked_list_t *select_traffic_selectors(private_policy_t *this, linked_list_t *stored, linked_list_t *supplied) +{ + iterator_t *supplied_iter, *stored_iter; + traffic_selector_t *supplied_ts, *stored_ts, *selected_ts; + linked_list_t *selected = linked_list_create(); + + + stored_iter = stored->create_iterator(stored, TRUE); + supplied_iter = supplied->create_iterator(supplied, TRUE); + + /* iterate over all stored selectors */ + while (stored_iter->has_next(stored_iter)) + { + stored_iter->current(stored_iter, (void**)&stored_ts); + + supplied_iter->reset(supplied_iter); + /* iterate over all supplied traffic selectors */ + while (supplied_iter->has_next(supplied_iter)) + { + supplied_iter->current(supplied_iter, (void**)&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); + } + } + } + stored_iter->destroy(stored_iter); + supplied_iter->destroy(supplied_iter); + + return selected; +} + +/** + * Implementation of policy_t.get_proposal_iterator + */ +static linked_list_t *get_proposals(private_policy_t *this) +{ + return this->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->has_next(stored_iter)) + { + supplied_iter->reset(supplied_iter); + stored_iter->current(stored_iter, (void**)&stored); + + while (supplied_iter->has_next(supplied_iter)) + { + supplied_iter->current(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_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); +} + +/** + * Implements policy_t.destroy. + */ +static status_t destroy(private_policy_t *this) +{ + proposal_t *proposal; + traffic_selector_t *traffic_selector; + + + /* delete proposals */ + while(this->proposals->remove_last(this->proposals, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + this->proposals->destroy(this->proposals); + + /* delete traffic selectors */ + while(this->my_ts->remove_last(this->my_ts, (void**)&traffic_selector) == SUCCESS) + { + traffic_selector->destroy(traffic_selector); + } + this->my_ts->destroy(this->my_ts); + + /* delete traffic selectors */ + while(this->other_ts->remove_last(this->other_ts, (void**)&traffic_selector) == SUCCESS) + { + traffic_selector->destroy(traffic_selector); + } + this->other_ts->destroy(this->other_ts); + + /* delete ids */ + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + + free(this); + return SUCCESS; +} + +/** + * Implements policy_t.clone. + */ +static policy_t *clone(private_policy_t *this) +{ + private_policy_t *clone = (private_policy_t*)policy_create(this->my_id->clone(this->my_id), + this->other_id->clone(this->other_id)); + iterator_t *iterator; + proposal_t *proposal; + traffic_selector_t *ts; + + /* clone all proposals */ + iterator = this->proposals->create_iterator(this->proposals, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&proposal); + proposal = proposal->clone(proposal); + clone->proposals->insert_last(clone->proposals, (void*)proposal); + } + iterator->destroy(iterator); + + /* clone all local traffic selectors */ + iterator = this->my_ts->create_iterator(this->my_ts, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&ts); + ts = ts->clone(ts); + clone->my_ts->insert_last(clone->my_ts, (void*)ts); + } + iterator->destroy(iterator); + + /* clone all remote traffic selectors */ + iterator = this->other_ts->create_iterator(this->other_ts, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&ts); + ts = ts->clone(ts); + clone->other_ts->insert_last(clone->other_ts, (void*)ts); + } + iterator->destroy(iterator); + + return &clone->public; +} + +/* + * Described in header-file + */ +policy_t *policy_create(identification_t *my_id, identification_t *other_id) +{ + private_policy_t *this = malloc_thing(private_policy_t); + + /* public functions */ + 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.update_my_id = (void(*)(policy_t*,identification_t*))update_my_id; + this->public.update_other_id = (void(*)(policy_t*,identification_t*))update_other_id; + this->public.update_my_ts = (void(*)(policy_t*,host_t*))update_my_ts; + this->public.update_other_ts = (void(*)(policy_t*,host_t*))update_other_ts; + this->public.get_my_traffic_selectors = (linked_list_t*(*)(policy_t*))get_my_traffic_selectors; + this->public.select_my_traffic_selectors = (linked_list_t*(*)(policy_t*,linked_list_t*))select_my_traffic_selectors; + this->public.get_other_traffic_selectors = (linked_list_t*(*)(policy_t*))get_other_traffic_selectors; + this->public.select_other_traffic_selectors = (linked_list_t*(*)(policy_t*,linked_list_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.clone = (policy_t*(*)(policy_t*))clone; + this->public.destroy = (void(*)(policy_t*))destroy; + + /* apply init values */ + this->my_id = my_id; + this->other_id = other_id; + + /* init private members*/ + this->select_traffic_selectors = select_traffic_selectors; + 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/charon/config/policies/policy.h b/src/charon/charon/config/policies/policy.h new file mode 100644 index 000000000..78cda1e8b --- /dev/null +++ b/src/charon/charon/config/policies/policy.h @@ -0,0 +1,249 @@ +/** + * @file policy.h + * + * @brief Interface of policy_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_H_ +#define POLICY_H_ + +#include +#include +#include +#include +#include + + +typedef struct policy_t policy_t; + +/** + * @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 own id to use for identification. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return own id + */ + identification_t *(*get_my_id) (policy_t *this); + + /** + * @brief Get id of communication partner. + * + * Returned object is not getting cloned. + * + * @param this calling object + * @return other id + */ + identification_t *(*get_other_id) (policy_t *this); + + /** + * @brief Update own ID. + * + * It may be necessary to uptdate own ID, as it + * is set to %any or to e.g. *@strongswan.org in + * some cases. + * Old ID is destroyed, new one NOT cloned. + * + * @param this calling object + * @param my_id new ID to set as my_id + */ + void (*update_my_id) (policy_t *this, identification_t *my_id); + + /** + * @brief Update others ID. + * + * It may be necessary to uptdate others ID, as it + * is set to %any or to e.g. *@strongswan.org in + * some cases. + * Old ID is destroyed, new one NOT cloned. + * + * @param this calling object + * @param other_id new ID to set as other_id + */ + void (*update_other_id) (policy_t *this, identification_t *other_id); + + /** + * @brief Update own address in traffic selectors. + * + * Update own 0.0.0.0 address in traffic selectors + * with supplied one. The size of the subnet will be + * set to /32. + * + * @param this calling object + * @param my_host new address to set in traffic selectors + */ + void (*update_my_ts) (policy_t *this, host_t *my_host); + + /** + * @brief Update others address in traffic selectors. + * + * Update remote 0.0.0.0 address in traffic selectors + * with supplied one. The size of the subnet will be + * set to /32. + * + * @param this calling object + * @param other_host new address to set in traffic selectors + */ + void (*update_other_ts) (policy_t *this, host_t *other_host); + + /** + * @brief Get configured traffic selectors for our site. + * + * Returns a list with all traffic selectors for the local + * site. List and items MUST NOT be freed nor modified. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_my_traffic_selectors) (policy_t *this); + + /** + * @brief Get configured traffic selectors for others site. + * + * Returns a list with all traffic selectors for the remote + * site. List and items MUST NOT be freed nor modified. + * + * @param this calling object + * @return list with traffic selectors + */ + linked_list_t *(*get_other_traffic_selectors) (policy_t *this); + + /** + * @brief Select traffic selectors from a supplied list for local site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * + * @param this calling object + * @param supplied linked list with traffic selectors + * @return list containing the selected traffic selectors + */ + linked_list_t *(*select_my_traffic_selectors) (policy_t *this, linked_list_t *supplied); + + /** + * @brief Select traffic selectors from a supplied list for remote site. + * + * Resulted list and traffic selectors must be destroyed after usage. + * + * @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); + + /** + * @brief Get the list of internally stored proposals. + * + * Rembember: policy_t does store proposals for AH/ESP, + * IKE proposals are in the connection_t + * + * @warning List and Items are still owned by policy and MUST NOT + * be manipulated or freed! + * + * @param this calling object + * @return lists with proposals + */ + linked_list_t *(*get_proposals) (policy_t *this); + + /** + * @brief Select a proposal from a supplied list. + * + * @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, proposal is owned by policy. + * + * @warning Do not add while other threads are reading. + * + * @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, proposal is owned by policy. + * + * @warning Do not add while other threads are reading. + * + * @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. + * + * @warning Do not add while other threads are reading. + * + * @param this calling object + * @param proposal proposal to add + */ + void (*add_proposal) (policy_t *this, proposal_t *proposal); + + /** + * @brief Clone a policy. + * + * @param this policy to clone + * @return clone of it + */ + policy_t *(*clone) (policy_t *this); + + /** + * @brief Destroys the policy object + * + * @param this calling object + */ + void (*destroy) (policy_t *this); +}; + +/** + * @brief Create a configuration object for IKE_AUTH and later. + * + * @param my_id identification_t for ourselves + * @param other_id identification_t for the remote guy + * @return policy_t object + * + * @ingroup config + */ +policy_t *policy_create(identification_t *my_id, identification_t *other_id); + +#endif /* POLICY_H_ */ diff --git a/src/charon/charon/config/policies/policy_store.h b/src/charon/charon/config/policies/policy_store.h new file mode 100755 index 000000000..651dea634 --- /dev/null +++ b/src/charon/charon/config/policies/policy_store.h @@ -0,0 +1,76 @@ +/** + * @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_ + +#include +#include + + +typedef struct policy_store_t policy_store_t; + +/** + * @brief The interface for a store of policy_t's. + * + * @b Constructors: + * - stroke_create() + * + * @ingroup config + */ +struct policy_store_t { + + /** + * @brief Returns a policy identified by two IDs. + * + * The returned policy gets created/cloned and therefore must be + * destroyed by the caller. + * + * @param this calling object + * @param my_id own ID of the policy + * @param other_id others ID of the policy + * @return + * - matching policy_t, if found + * - NULL otherwise + */ + policy_t *(*get_policy) (policy_store_t *this, identification_t *my_id, identification_t *other_id); + + /** + * @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 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/charon/config/proposal.c b/src/charon/charon/config/proposal.c new file mode 100644 index 000000000..cb71a756a --- /dev/null +++ b/src/charon/charon/config/proposal.c @@ -0,0 +1,642 @@ +/** + * @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 + + +/** + * String mappings for protocol_id_t. + */ +mapping_t protocol_id_m[] = { + {PROTO_NONE, "PROTO_NONE"}, + {PROTO_IKE, "PROTO_IKE"}, + {PROTO_AH, "PROTO_AH"}, + {PROTO_ESP, "PROTO_ESP"}, + {MAPPING_END, NULL} +}; + +/** + * String mappings for transform_type_t. + */ +mapping_t transform_type_m[] = { + {UNDEFINED_TRANSFORM_TYPE, "UNDEFINED_TRANSFORM_TYPE"}, + {ENCRYPTION_ALGORITHM, "ENCRYPTION_ALGORITHM"}, + {PSEUDO_RANDOM_FUNCTION, "PSEUDO_RANDOM_FUNCTION"}, + {INTEGRITY_ALGORITHM, "INTEGRITY_ALGORITHM"}, + {DIFFIE_HELLMAN_GROUP, "DIFFIE_HELLMAN_GROUP"}, + {EXTENDED_SEQUENCE_NUMBERS, "EXTENDED_SEQUENCE_NUMBERS"}, + {MAPPING_END, NULL} +}; + +/** + * String mappings for extended_sequence_numbers_t. + */ +mapping_t extended_sequence_numbers_m[] = { + {NO_EXT_SEQ_NUMBERS, "NO_EXT_SEQ_NUMBERS"}, + {EXT_SEQ_NUMBERS, "EXT_SEQ_NUMBERS"}, + {MAPPING_END, NULL} +}; + + +typedef struct protocol_proposal_t protocol_proposal_t; + +/** + * substructure which holds all data algos for a specific protocol + */ +struct protocol_proposal_t { + /** + * 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 + */ + chunk_t spi; +}; + + +typedef struct private_proposal_t private_proposal_t; + +/** + * Private data of an proposal_t object + */ +struct private_proposal_t { + + /** + * Public part + */ + proposal_t public; + + /** + * number of this proposal, as used in the payload + */ + u_int8_t number; + + /** + * list of protocol_proposal_t's + */ + linked_list_t *protocol_proposals; +}; + +/** + * Look up a protocol_proposal, or create one if necessary... + */ +static protocol_proposal_t *get_protocol_proposal(private_proposal_t *this, protocol_id_t proto, bool create) +{ + protocol_proposal_t *proto_proposal = NULL, *current_proto_proposal;; + iterator_t *iterator; + + /* find our protocol in the proposals */ + iterator = this->protocol_proposals->create_iterator(this->protocol_proposals, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t_proto_proposal); + if (current_proto_proposal->protocol == proto) + { + proto_proposal = current_proto_proposal; + break; + } + } + iterator->destroy(iterator); + + if (!proto_proposal && create) + { + /* nope, create a new one */ + proto_proposal = malloc_thing(protocol_proposal_t); + proto_proposal->protocol = proto; + proto_proposal->encryption_algos = linked_list_create(); + proto_proposal->integrity_algos = linked_list_create(); + proto_proposal->prf_algos = linked_list_create(); + proto_proposal->dh_groups = linked_list_create(); + proto_proposal->esns = linked_list_create(); + if (proto == PROTO_IKE) + { + proto_proposal->spi.len = 8; + } + else + { + proto_proposal->spi.len = 4; + } + proto_proposal->spi.ptr = malloc(proto_proposal->spi.len); + /* add to the list */ + this->protocol_proposals->insert_last(this->protocol_proposals, (void*)proto_proposal); + } + return proto_proposal; +} + +/** + * Add algorithm/keysize to a algorithm list + */ +static void add_algo(linked_list_t *list, u_int8_t algo, size_t key_size) +{ + algorithm_t *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, protocol_id_t proto, transform_type_t type, u_int16_t algo, size_t key_size) +{ + protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, TRUE); + + switch (type) + { + case ENCRYPTION_ALGORITHM: + add_algo(proto_proposal->encryption_algos, algo, key_size); + break; + case INTEGRITY_ALGORITHM: + add_algo(proto_proposal->integrity_algos, algo, key_size); + break; + case PSEUDO_RANDOM_FUNCTION: + add_algo(proto_proposal->prf_algos, algo, key_size); + break; + case DIFFIE_HELLMAN_GROUP: + add_algo(proto_proposal->dh_groups, algo, 0); + break; + case EXTENDED_SEQUENCE_NUMBERS: + add_algo(proto_proposal->esns, algo, 0); + break; + default: + break; + } +} + +/** + * Implements proposal_t.get_algorithm. + */ +static bool get_algorithm(private_proposal_t *this, protocol_id_t proto, transform_type_t type, algorithm_t** algo) +{ + linked_list_t * list; + protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); + + if (proto_proposal == NULL) + { + return FALSE; + } + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = proto_proposal->encryption_algos; + break; + case INTEGRITY_ALGORITHM: + list = proto_proposal->integrity_algos; + break; + case PSEUDO_RANDOM_FUNCTION: + list = proto_proposal->prf_algos; + break; + case DIFFIE_HELLMAN_GROUP: + list = proto_proposal->dh_groups; + break; + case EXTENDED_SEQUENCE_NUMBERS: + list = proto_proposal->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, protocol_id_t proto, transform_type_t type) +{ + protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); + if (proto_proposal == NULL) + { + return NULL; + } + + switch (type) + { + case ENCRYPTION_ALGORITHM: + return proto_proposal->encryption_algos->create_iterator(proto_proposal->encryption_algos, TRUE); + case INTEGRITY_ALGORITHM: + return proto_proposal->integrity_algos->create_iterator(proto_proposal->integrity_algos, TRUE); + case PSEUDO_RANDOM_FUNCTION: + return proto_proposal->prf_algos->create_iterator(proto_proposal->prf_algos, TRUE); + case DIFFIE_HELLMAN_GROUP: + return proto_proposal->dh_groups->create_iterator(proto_proposal->dh_groups, TRUE); + case EXTENDED_SEQUENCE_NUMBERS: + return proto_proposal->esns->create_iterator(proto_proposal->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->has_next(first_iter)) + { + first_iter->current(first_iter, (void**)&first_alg); + second_iter->reset(second_iter); + while (second_iter->has_next(second_iter)) + { + second_iter->current(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; + iterator_t *iterator; + protocol_proposal_t *this_prop, *other_prop; + protocol_id_t proto; + bool add; + u_int64_t spi; + + /* empty proposal? no match */ + if (this->protocol_proposals->get_count(this->protocol_proposals) == 0 || + other->protocol_proposals->get_count(other->protocol_proposals) == 0) + { + return NULL; + } + /* they MUST have the same amount of protocols */ + if (this->protocol_proposals->get_count(this->protocol_proposals) != + other->protocol_proposals->get_count(other->protocol_proposals)) + { + return NULL; + } + + selected = proposal_create(this->number); + + /* iterate over supplied proposals */ + iterator = other->protocol_proposals->create_iterator(other->protocol_proposals, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&other_prop); + /* get the proposal with the same protocol */ + proto = other_prop->protocol; + this_prop = get_protocol_proposal(this, proto, FALSE); + + if (this_prop == NULL) + { + iterator->destroy(iterator); + selected->destroy(selected); + return NULL; + } + + /* select encryption algorithm */ + if (select_algo(this_prop->encryption_algos, other_prop->encryption_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, proto, ENCRYPTION_ALGORITHM, algo, key_size); + } + } + else + { + iterator->destroy(iterator); + selected->destroy(selected); + return NULL; + } + /* select integrity algorithm */ + if (select_algo(this_prop->integrity_algos, other_prop->integrity_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, proto, INTEGRITY_ALGORITHM, algo, key_size); + } + } + else + { + iterator->destroy(iterator); + selected->destroy(selected); + return NULL; + } + /* select prf algorithm */ + if (select_algo(this_prop->prf_algos, other_prop->prf_algos, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, proto, PSEUDO_RANDOM_FUNCTION, algo, key_size); + } + } + else + { + iterator->destroy(iterator); + selected->destroy(selected); + return NULL; + } + /* select a DH-group */ + if (select_algo(this_prop->dh_groups, other_prop->dh_groups, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, proto, DIFFIE_HELLMAN_GROUP, algo, 0); + } + } + else + { + iterator->destroy(iterator); + selected->destroy(selected); + return NULL; + } + /* select if we use ESNs */ + if (select_algo(this_prop->esns, other_prop->esns, &add, &algo, &key_size)) + { + if (add) + { + selected->add_algorithm(selected, proto, EXTENDED_SEQUENCE_NUMBERS, algo, 0); + } + } + else + { + iterator->destroy(iterator); + selected->destroy(selected); + return NULL; + } + } + iterator->destroy(iterator); + + /* apply spis from "other" */ + spi = other->public.get_spi(&(other->public), PROTO_AH); + if (spi) + { + selected->set_spi(selected, PROTO_AH, spi); + } + spi = other->public.get_spi(&(other->public), PROTO_ESP); + if (spi) + { + selected->set_spi(selected, PROTO_ESP, spi); + } + + /* everything matched, return new proposal */ + return selected; +} + +/** + * Implements proposal_t.get_number. + */ +static u_int8_t get_number(private_proposal_t *this) +{ + return this->number; +} + +/** + * Implements proposal_t.get_protocols. + */ +static void get_protocols(private_proposal_t *this, protocol_id_t ids[2]) +{ + iterator_t *iterator = this->protocol_proposals->create_iterator(this->protocol_proposals, TRUE); + u_int i = 0; + + ids[0] = PROTO_NONE; + ids[1] = PROTO_NONE; + while (iterator->has_next(iterator)) + { + protocol_proposal_t *proto_prop; + iterator->current(iterator, (void**)&proto_prop); + ids[i++] = proto_prop->protocol; + if (i>1) + { + /* should not happen, but who knows */ + break; + } + } + iterator->destroy(iterator); +} + +/** + * Implements proposal_t.set_spi. + */ +static void set_spi(private_proposal_t *this, protocol_id_t proto, u_int64_t spi) +{ + protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); + if (proto_proposal) + { + if (proto == PROTO_AH || proto == PROTO_ESP) + { + *((u_int32_t*)proto_proposal->spi.ptr) = (u_int32_t)spi; + } + else + { + *((u_int64_t*)proto_proposal->spi.ptr) = spi; + } + } +} + +/** + * Implements proposal_t.get_spi. + */ +static u_int64_t get_spi(private_proposal_t *this, protocol_id_t proto) +{ + protocol_proposal_t *proto_proposal = get_protocol_proposal(this, proto, FALSE); + if (proto_proposal) + { + if (proto == PROTO_AH || proto == PROTO_ESP) + { + return (u_int64_t)*((u_int32_t*)proto_proposal->spi.ptr); + } + else + { + return *((u_int64_t*)proto_proposal->spi.ptr); + } + } + return 0; +} + +/** + * 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->has_next(iterator)) + { + iterator->current(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->number); + + iterator_t *iterator = this->protocol_proposals->create_iterator(this->protocol_proposals, TRUE); + while (iterator->has_next(iterator)) + { + protocol_proposal_t *proto_prop, *clone_proto_prop; + iterator->current(iterator, (void**)&proto_prop); + + clone_proto_prop = get_protocol_proposal(clone, proto_prop->protocol, TRUE); + memcpy(clone_proto_prop->spi.ptr, proto_prop->spi.ptr, clone_proto_prop->spi.len); + + clone_algo_list(proto_prop->encryption_algos, clone_proto_prop->encryption_algos); + clone_algo_list(proto_prop->integrity_algos, clone_proto_prop->integrity_algos); + clone_algo_list(proto_prop->prf_algos, clone_proto_prop->prf_algos); + clone_algo_list(proto_prop->dh_groups, clone_proto_prop->dh_groups); + clone_algo_list(proto_prop->esns, clone_proto_prop->esns); + } + iterator->destroy(iterator); + + return &clone->public; +} + +/** + * Frees all list items and destroys the list + */ +static void free_algo_list(linked_list_t *list) +{ + algorithm_t *algo; + + while(list->get_count(list) > 0) + { + list->remove_last(list, (void**)&algo); + free(algo); + } + list->destroy(list); +} + +/** + * Implements proposal_t.destroy. + */ +static void destroy(private_proposal_t *this) +{ + while(this->protocol_proposals->get_count(this->protocol_proposals) > 0) + { + protocol_proposal_t *proto_prop; + this->protocol_proposals->remove_last(this->protocol_proposals, (void**)&proto_prop); + + free_algo_list(proto_prop->encryption_algos); + free_algo_list(proto_prop->integrity_algos); + free_algo_list(proto_prop->prf_algos); + free_algo_list(proto_prop->dh_groups); + free_algo_list(proto_prop->esns); + + free(proto_prop->spi.ptr); + free(proto_prop); + } + this->protocol_proposals->destroy(this->protocol_proposals); + + free(this); +} + +/* + * Describtion in header-file + */ +proposal_t *proposal_create(u_int8_t number) +{ + private_proposal_t *this = malloc_thing(private_proposal_t); + + this->public.add_algorithm = (void (*)(proposal_t*,protocol_id_t,transform_type_t,u_int16_t,size_t))add_algorithm; + this->public.create_algorithm_iterator = (iterator_t* (*)(proposal_t*,protocol_id_t,transform_type_t))create_algorithm_iterator; + this->public.get_algorithm = (bool (*)(proposal_t*,protocol_id_t,transform_type_t,algorithm_t**))get_algorithm; + this->public.select = (proposal_t* (*)(proposal_t*,proposal_t*))select_proposal; + this->public.get_number = (u_int8_t (*)(proposal_t*))get_number; + this->public.get_protocols = (void(*)(proposal_t *this, protocol_id_t ids[2]))get_protocols; + this->public.set_spi = (void(*)(proposal_t*,protocol_id_t,u_int64_t spi))set_spi; + this->public.get_spi = (u_int64_t(*)(proposal_t*,protocol_id_t))get_spi; + this->public.clone = (proposal_t*(*)(proposal_t*))clone; + this->public.destroy = (void(*)(proposal_t*))destroy; + + /* init private members*/ + this->number = number; + this->protocol_proposals = linked_list_create(); + + return (&this->public); +} diff --git a/src/charon/charon/config/proposal.h b/src/charon/charon/config/proposal.h new file mode 100644 index 000000000..48e3ad8d5 --- /dev/null +++ b/src/charon/charon/config/proposal.h @@ -0,0 +1,269 @@ +/** + * @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_ + +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef enum protocol_id_t protocol_id_t; + +/** + * Protocol ID of a proposal. + * + * @ingroup config + */ +enum protocol_id_t { + PROTO_NONE = 0, + PROTO_IKE = 1, + PROTO_AH = 2, + PROTO_ESP = 3, +}; + +/** + * String mappings for protocol_id_t. + * + * @ingroup config + */ +extern mapping_t protocol_id_m[]; + + +typedef enum transform_type_t transform_type_t; + +/** + * Type of a transform, as in IKEv2 RFC 3.3.2. + * + * @ingroup payloads + */ +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 +}; + +/** + * String mappings for transform_type_t. + * + * @ingroup payloads + */ +extern mapping_t transform_type_m[]; + + +typedef enum extended_sequence_numbers_t extended_sequence_numbers_t; + +/** + * Extended sequence numbers, as in IKEv2 RFC 3.3.2. + * + * @ingroup payloads + */ +enum extended_sequence_numbers_t { + NO_EXT_SEQ_NUMBERS = 0, + EXT_SEQ_NUMBERS = 1 +}; + +/** + * String mappings for extended_sequence_numbers_t. + * + * @ingroup payloads + */ +extern mapping_t extended_sequence_numbers_m[]; + + +typedef struct algorithm_t algorithm_t; + +/** + * 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, or zero if not needed + */ + u_int16_t key_size; +}; + +typedef struct proposal_t proposal_t; + +/** + * @brief Stores a set of algorithms used for an SA. + * + * A proposal stores algorithms for a specific + * protocol. It can store algorithms for more than + * one protocol (e.g. AH and ESP). Then the proposal + * means both protocols must be used. + * A proposal may contain more than one algorithm + * of the same kind. ONE of them can be selected. + * + * @warning This class is NOT thread-save! + * + * @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. + * + * @warning Do not add while other threads are reading. + * + * @param this calling object + * @param proto desired protocol + * @param type kind of algorithm + * @param alg identifier for algorithm + * @param key_size key size to use + */ + void (*add_algorithm) (proposal_t *this, protocol_id_t proto, transform_type_t type, u_int16_t alg, size_t key_size); + + /** + * @brief Get an iterator over algorithms for a specifc protocol/algo type. + * + * @param this calling object + * @param proto desired protocol + * @param type kind of algorithm + * @return iterator over algorithms + */ + iterator_t *(*create_algorithm_iterator) (proposal_t *this, protocol_id_t proto, 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 proto desired protocol + * @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, protocol_id_t proto, 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 number set on construction. + * + * @param this calling object + * @return number + */ + u_int8_t (*get_number) (proposal_t *this); + + /** + * @brief Get the protocol ids in the proposals. + * + * With AH and ESP, there could be two protocols in one + * proposal. + * + * @param this calling object + * @param ids array of protocol ids, + */ + void (*get_protocols) (proposal_t *this, protocol_id_t ids[2]); + + /** + * @brief Get the spi for a specific protocol. + * + * @param this calling object + * @param proto AH/ESP + * @return spi for proto + */ + u_int64_t (*get_spi) (proposal_t *this, protocol_id_t proto); + + /** + * @brief Set the spi for a specific protocol. + * + * @param this calling object + * @param proto AH/ESP + * @param spi spi to set for proto + */ + void (*set_spi) (proposal_t *this, protocol_id_t proto, 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 and/or ESP. + * + * Since the order of multiple proposals is important for + * key derivation, we must assign them numbers as they + * appear in the raw payload. Numbering starts at 1. + * + * @param number number of the proposal, as in the payload + * @return proposal_t object + * + * @ingroup config + */ +proposal_t *proposal_create(u_int8_t number); + +#endif /* PROPOSAL_H_ */ diff --git a/src/charon/charon/config/traffic_selector.c b/src/charon/charon/config/traffic_selector.c new file mode 100644 index 000000000..81272659a --- /dev/null +++ b/src/charon/charon/config/traffic_selector.c @@ -0,0 +1,425 @@ +/** + * @file traffic_selector.c + * + * @brief Implementation of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "traffic_selector.h" + +#include +#include +#include +#include + +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; + + /** + * begin of address range, host order + */ + union { + u_int32_t from_addr_ipv4; + }; + + /** + * end of address range, host order + */ + union { + u_int32_t to_addr_ipv4; + }; + + /** + * begin of port range + */ + u_int16_t from_port; + + /** + * end of port range + */ + u_int16_t to_port; +}; + +/** + * 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); + +/** + * 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 == TS_IPV4_ADDR_RANGE) && + (other->type == TS_IPV4_ADDR_RANGE) && + (this->protocol == other->protocol)) + { + u_int32_t from_addr, to_addr; + u_int16_t from_port, to_port; + private_traffic_selector_t *new_ts; + + /* calculate the maximum address range allowed for both */ + from_addr = max(this->from_addr_ipv4, other->from_addr_ipv4); + to_addr = min(this->to_addr_ipv4, other->to_addr_ipv4); + if (from_addr > to_addr) + { + /* no match */ + return NULL; + } + + /* 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) + { + /* no match */ + return NULL; + } + + /* got a match, return it */ + new_ts = traffic_selector_create(this->protocol, this->type, from_port, to_port); + new_ts->from_addr_ipv4 = from_addr; + new_ts->to_addr_ipv4 = to_addr; + new_ts->type = TS_IPV4_ADDR_RANGE; + return &(new_ts->public); + } + return NULL; +} + +/** + * Implements traffic_selector_t.get_from_address. + */ +static chunk_t get_from_address(private_traffic_selector_t *this) +{ + chunk_t from_addr = CHUNK_INITIALIZER; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + u_int32_t network; + from_addr.len = sizeof(network); + from_addr.ptr = malloc(from_addr.len); + /* chunk must contain network order, convert! */ + network = htonl(this->from_addr_ipv4); + memcpy(from_addr.ptr, &network, from_addr.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + break; + } + } + return from_addr; +} + +/** + * Implements traffic_selector_t.get_to_address. + */ +static chunk_t get_to_address(private_traffic_selector_t *this) +{ + chunk_t to_addr = CHUNK_INITIALIZER; + + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + u_int32_t network; + to_addr.len = sizeof(network); + to_addr.ptr = malloc(to_addr.len); + /* chunk must contain network order, convert! */ + network = htonl(this->to_addr_ipv4); + memcpy(to_addr.ptr, &network, to_addr.len); + break; + } + case TS_IPV6_ADDR_RANGE: + { + break; + } + } + return to_addr; +} + +/** + * 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.get_netmask. + */ +static u_int8_t get_netmask(private_traffic_selector_t *this) +{ + switch (this->type) + { + case TS_IPV4_ADDR_RANGE: + { + u_int32_t from, to, bit; + from = htonl(this->from_addr_ipv4); + to = htonl(this->to_addr_ipv4); + for (bit = 0; bit < 32; bit++) + { + if ((1<get_family(host) == AF_INET && + this->type == TS_IPV4_ADDR_RANGE) + { + if (this->from_addr_ipv4 == 0) + { + chunk_t from = host->get_address_as_chunk(host); + this->from_addr_ipv4 = ntohl(*((u_int32_t*)from.ptr)); + this->to_addr_ipv4 = this->from_addr_ipv4; + chunk_free(&from); + } + } +} + +/** + * Implements traffic_selector_t.clone. + */ +static traffic_selector_t *clone(private_traffic_selector_t *this) +{ + private_traffic_selector_t *clone = traffic_selector_create(this->protocol, this->type, this->from_port, this->to_port); + clone->type = this->type; + switch (clone->type) + { + case TS_IPV4_ADDR_RANGE: + { + clone->from_addr_ipv4 = this->from_addr_ipv4; + clone->to_addr_ipv4 = this->to_addr_ipv4; + return &(clone->public); + } + case TS_IPV6_ADDR_RANGE: + default: + { + free(this); + return NULL; + } + } +} + +/** + * 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_addr, int16_t from_port, chunk_t 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 (from_addr.len != 4 || to_addr.len != 4) + { + free(this); + return NULL; + } + /* chunk contains network order, convert! */ + this->from_addr_ipv4 = ntohl(*((u_int32_t*)from_addr.ptr)); + this->to_addr_ipv4 = ntohl(*((u_int32_t*)to_addr.ptr)); + break; + } + case TS_IPV6_ADDR_RANGE: + 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) +{ + private_traffic_selector_t *this = traffic_selector_create(0, 0, 0, 65535); + + switch (net->get_family(net)) + { + case AF_INET: + { + chunk_t from; + + this->type = TS_IPV4_ADDR_RANGE; + from = net->get_address_as_chunk(net); + this->from_addr_ipv4 = ntohl(*((u_int32_t*)from.ptr)); + if (this->from_addr_ipv4 == 0) + { + /* use /32 for 0.0.0.0 */ + this->to_addr_ipv4 = 0xFFFFFF; + } + else + { + this->to_addr_ipv4 = this->from_addr_ipv4 | ((1 << (32 - netbits)) - 1); + } + chunk_free(&from); + break; + } + case AF_INET6: + default: + { + free(this); + return NULL; + } + } + 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); + + /* public functions */ + this->public.get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset; + this->public.destroy = (void(*)(traffic_selector_t*))destroy; + + this->type = type; + switch (type) + { + case TS_IPV4_ADDR_RANGE: + { + if (inet_aton(from_addr, (struct in_addr*)&(this->from_addr_ipv4)) == 0) + { + free(this); + return NULL; + } + if (inet_aton(to_addr, (struct in_addr*)&(this->to_addr_ipv4)) == 0) + { + free(this); + return NULL; + } + /* convert to host order, inet_aton has network order */ + this->from_addr_ipv4 = ntohl(this->from_addr_ipv4); + this->to_addr_ipv4 = ntohl(this->to_addr_ipv4); + break; + } + case TS_IPV6_ADDR_RANGE: + { + free(this); + return NULL; + } + } + + 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.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.get_netmask = (u_int8_t(*)(traffic_selector_t*))get_netmask; + this->public.update_address_range = (void(*)(traffic_selector_t*,host_t*))update_address_range; + 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; + + return this; +} diff --git a/src/charon/charon/config/traffic_selector.h b/src/charon/charon/config/traffic_selector.h new file mode 100644 index 000000000..5ac5bdeb1 --- /dev/null +++ b/src/charon/charon/config/traffic_selector.h @@ -0,0 +1,258 @@ +/** + * @file traffic_selector.h + * + * @brief Interface of traffic_selector_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TRAFFIC_SELECTOR_H_ +#define TRAFFIC_SELECTOR_H_ + +#include +#include + +typedef enum ts_type_t ts_type_t; + +/** + * 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 +}; + +/** + * string mappings for ts_type_t + */ +extern mapping_t ts_type_m[]; + + +typedef struct traffic_selector_t traffic_selector_t; + +/** + * @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. + * + * Data is in network order and represents the address. + * Size depends on protocol. + * + * Resulting chunk data is allocated and must be freed! + * + * @param this calling 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. + * + * Data is in network order and represents the address. + * Size depends on protocol. + * + * Resulting chunk data is allocated and must be freed! + * + * @param this calling 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 calling 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 calling object + * @return port + */ + u_int16_t (*get_to_port) (traffic_selector_t *this); + + /** + * @brief Get the type of the traffic selector. + * + * @param this calling obect + * @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 calling obect + * @return protocol id + */ + u_int8_t (*get_protocol) (traffic_selector_t *this); + + /** + * @brief Get the netmask of the address range. + * + * Returns the number of bits associated to the subnet. + * (As the "24" in "192.168.0.0/24"). This is approximated + * if the address range is not a complete subnet! Since Linux + * does not support full IP address ranges (yet), we can't do this + * (much) better. + * + * @param this calling obect + * @return netmask as "bits for subnet" + */ + u_int8_t (*get_netmask) (traffic_selector_t *this); + + /** + * @brief Update the address of a traffic selector. + * + * Update the address range of a traffic selector, + * if the current address is 0.0.0.0. The new address range + * starts from the supplied address and also ends there + * (which means it is a one-host-address-range ;-). + * + * @param this calling obect + * @param host host_t specifying the address range + */ + void (*update_address_range) (traffic_selector_t *this, host_t* host); + + /** + * @brief Destroys the ts object + * + * @param this calling 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 + * - NULL if invalid address input/protocol + * + * @ingroup config + */ +traffic_selector_t *traffic_selector_create_from_bytes(u_int8_t protocol, ts_type_t type, chunk_t from_address, 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. + * + * @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); + +#endif /* TRAFFIC_SELECTOR_H_ */ diff --git a/src/charon/charon/daemon.c b/src/charon/charon/daemon.c new file mode 100644 index 000000000..4b0ea54e8 --- /dev/null +++ b/src/charon/charon/daemon.c @@ -0,0 +1,390 @@ +/** + * @file daemon.c + * + * @brief Implementation of daemon_t and main of IKEv2-Daemon. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "daemon.h" + +#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; + + /** + * A logger_t object assigned for daemon things. + */ + logger_t *logger; + + /** + * Signal set used for signal handling. + */ + sigset_t signal_set; + + /** + * The thread_id of main-thread. + */ + pthread_t main_thread_id; + + /** + * Main loop function. + * + * @param this calling object + */ + void (*run) (private_daemon_t *this); + + /** + * Initialize the daemon. + * + * @param this calling object + */ + void (*initialize) (private_daemon_t *this); + + /** + * Destroy the daemon. + * + * @param this calling object + */ + void (*destroy) (private_daemon_t *this); +}; + +/** + * One and only instance of the daemon. + */ +daemon_t *charon; + +/** + * Implementation of private_daemon_t.run. + */ +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) + { + this->logger->log(this->logger, ERROR, "Error %d when waiting for signal", error); + return; + } + switch (signal_number) + { + case SIGHUP: + { + this->logger->log(this->logger, CONTROL, "Signal of type SIGHUP received. Do nothing"); + break; + } + case SIGINT: + { + this->logger->log(this->logger, CONTROL, "Signal of type SIGINT received. Exit main loop"); + return; + } + case SIGTERM: + this->logger->log(this->logger, CONTROL, "Signal of type SIGTERM received. Exit main loop"); + return; + default: + { + this->logger->log(this->logger, CONTROL, "Unknown signal %d received. Do nothing", signal_number); + break; + } + } + } +} + +/** + * Implementation of daemon_t.kill. + */ +static void kill_daemon(private_daemon_t *this, char *reason) +{ + /* we send SIGTERM, so the daemon can cleanly shut down */ + this->logger->log(this->logger, CONTROL, "Killing daemon: %s", reason); + if (this->main_thread_id == pthread_self()) + { + /* initialization failed, terminate daemon */ + this->destroy(this); + unlink(PID_FILE); + exit(-1); + } + else + { + this->logger->log(this->logger, CONTROL, "sending SIGTERM to ourself", reason); + kill(0, SIGTERM); + /* thread must die, since he produced a ciritcal failure and can't continue */ + pthread_exit(NULL); + } +} + +/** + * Implementation of private_daemon_t.initialize. + */ +static void initialize(private_daemon_t *this) +{ + local_credential_store_t* cred_store; + + this->public.configuration = configuration_create(); + this->public.socket = socket_create(IKEV2_UDP_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.send_queue = send_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*)(cred_store = local_credential_store_create()); + + /* load keys & certs */ + cred_store->load_certificates(cred_store, CERTIFICATE_DIR); + cred_store->load_private_keys(cred_store, PRIVATE_KEY_DIR); + + + /* 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); +} + +/** + * Destory all initiated objects + */ +static void destroy(private_daemon_t *this) +{ + if (this->public.ike_sa_manager != NULL) + { + this->public.ike_sa_manager->destroy(this->public.ike_sa_manager); + } + if (this->public.kernel_interface != NULL) + { + this->public.kernel_interface->destroy(this->public.kernel_interface); + } + if (this->public.receiver != NULL) + { + this->public.receiver->destroy(this->public.receiver); + } + if (this->public.scheduler != NULL) + { + this->public.scheduler->destroy(this->public.scheduler); + } + if (this->public.sender != NULL) + { + this->public.sender->destroy(this->public.sender); + } + if (this->public.thread_pool != NULL) + { + this->public.thread_pool->destroy(this->public.thread_pool); + } + if (this->public.job_queue != NULL) + { + this->public.job_queue->destroy(this->public.job_queue); + } + if (this->public.event_queue != NULL) + { + this->public.event_queue->destroy(this->public.event_queue); + } + if (this->public.send_queue != NULL) + { + this->public.send_queue->destroy(this->public.send_queue); + } + if (this->public.socket != NULL) + { + this->public.socket->destroy(this->public.socket); + } + if (this->public.configuration != NULL) + { + this->public.configuration->destroy(this->public.configuration); + } + if (this->public.credentials != NULL) + { + this->public.credentials->destroy(this->public.credentials); + } + if (this->public.connections != NULL) + { + this->public.connections->destroy(this->public.connections); + } + if (this->public.policies != NULL) + { + this->public.policies->destroy(this->public.policies); + } + if (this->public.stroke != NULL) + { + this->public.stroke->destroy(this->public.stroke); + } + free(this); +} + +void signal_handler(int signal) +{ + void *array[20]; + size_t size; + char **strings; + size_t i; + logger_t *logger; + + size = backtrace(array, 20); + strings = backtrace_symbols(array, size); + logger = logger_manager->get_logger(logger_manager, DAEMON); + + logger->log(logger, ERROR, "Thread %u received SIGSEGV. Dumping %d frames from stack:", pthread_self(), size); + + for (i = 0; i < size; i++) + { + logger->log(logger, ERROR, " %s", strings[i]); + } + free (strings); + logger->log(logger, ERROR, "Killing ourself hard after SIGSEGV"); + kill(getpid(), SIGKILL); +} + +/** + * @brief Create the daemon. + * + * @return created daemon_t + */ +private_daemon_t *daemon_create() +{ + private_daemon_t *this = malloc_thing(private_daemon_t); + struct sigaction action; + + /* assign methods */ + this->run = run; + this->destroy = destroy; + this->initialize = initialize; + 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.send_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->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; + if (sigaction(SIGSEGV, &action, NULL) == -1) + { + this->logger->log(this->logger, ERROR, "signal handler setup for SIGSEGV failed"); + } + return this; +} + +/** + * Main function, manages the daemon. + */ +int main(int argc, char *argv[]) +{ + private_daemon_t *private_charon; + FILE *pid_file; + struct stat stb; + int i; + + /* trivial argument parsing */ + for (i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--use-syslog") == 0) + { + logger_manager->set_output(logger_manager, ALL_LOGGERS, NULL); + } + } + private_charon = daemon_create(); + charon = (daemon_t*)private_charon; + + private_charon->logger = logger_manager->get_logger(logger_manager, DAEMON); + + /* initialize daemon */ + private_charon->initialize(private_charon); + + /* check/setup PID file */ + if (stat(PID_FILE, &stb) == 0) + { + private_charon->logger->log(private_charon->logger, ERROR, + "charon already running (\""PID_FILE"\" exists)"); + private_charon->destroy(private_charon); + exit(-1); + } + pid_file = fopen(PID_FILE, "w"); + if (pid_file) + { + fprintf(pid_file, "%d\n", getpid()); + fclose(pid_file); + } + + /* run daemon */ + private_charon->run(private_charon); + + /* normal termination, cleanup and exit */ + private_charon->destroy(private_charon); + unlink(PID_FILE); + + return 0; +} + + diff --git a/src/charon/charon/daemon.h b/src/charon/charon/daemon.h new file mode 100644 index 000000000..5aee21fdb --- /dev/null +++ b/src/charon/charon/daemon.h @@ -0,0 +1,324 @@ +/** + * @file daemon.h + * + * @brief Interface of daemon_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 DAEMON_H_ +#define DAEMON_H_ + +#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 | +------+ +------------+ +------+ + +----+-------+ + | +------+ + | | S Q | + | | e u | + | | n e | + +------------| d u | + | - e | + +--+---+ + @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 send-queue. 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 states states + * + * Varius states in which an IKE SA can be. + * + * @ingroup sa + */ + +/** + * @defgroup threads threads + * + * Threaded classes, which will do their job alone. + * + * @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 + +/** + * PID file, in which charon stores its process id + * + * @ingroup charon + */ +#define PID_FILE "/var/run/charon.pid" + +/** + * Directory of IPsec relevant files + * + * @ingroup charon + */ +#define IPSEC_DIR "/etc/ipsec.d" + +/** + * Directory for private keys + * + * @ingroup charon + */ +#define PRIVATE_KEY_DIR IPSEC_DIR "/private" + +/** + * Directory for trusted certificates + * + * @ingroup charon + */ +#define CERTIFICATE_DIR IPSEC_DIR "/certs" + + +typedef struct daemon_t daemon_t; + +/** + * @brief Main class of daemon, contains some globals. + * + * @ingroup charon + */ +struct daemon_t { + /** + * A socket_t instance. + */ + socket_t *socket; + + /** + * A send_queue_t instance. + */ + send_queue_t *send_queue; + + /** + * 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; + + /** + * 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/charon/encoding/Makefile.encoding b/src/charon/charon/encoding/Makefile.encoding new file mode 100644 index 000000000..ccdb42f79 --- /dev/null +++ b/src/charon/charon/encoding/Makefile.encoding @@ -0,0 +1,30 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +ENCODING_DIR= $(CHARON_DIR)encoding/ + +CHARON_OBJS+= $(BUILD_DIR)generator.o +$(BUILD_DIR)generator.o : $(ENCODING_DIR)generator.c $(ENCODING_DIR)generator.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)parser.o +$(BUILD_DIR)parser.o : $(ENCODING_DIR)parser.c $(ENCODING_DIR)parser.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)message.o +$(BUILD_DIR)message.o : $(ENCODING_DIR)message.c $(ENCODING_DIR)message.h + $(CC) $(CFLAGS) -c -o $@ $< + + +include $(ENCODING_DIR)payloads/Makefile.payloads \ No newline at end of file diff --git a/src/charon/charon/encoding/generator.c b/src/charon/charon/encoding/generator.c new file mode 100644 index 000000000..ba12190dd --- /dev/null +++ b/src/charon/charon/encoding/generator.c @@ -0,0 +1,1077 @@ +/** + * @file generator.c + * + * @brief Implementation of generator_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "generator.h" + +#include +#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; + + /** + * Associated Logger. + */ + logger_t *logger; +}; + +/** + * 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: + this->logger->log(this->logger, ERROR, "U_INT Type %s is not supported", + mapping_find(encoding_type_m,int_type)); + + return; + } + /* U_INT Types of multiple then 8 bits must be aligned */ + if (((number_of_bits % 8) == 0) && (this->current_bit != 0)) + { + this->logger->log(this->logger, ERROR, "U_INT Type %s is not 8 Bit aligned", + mapping_find(encoding_type_m,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; + this->logger->log(this->logger, RAW|LEVEL2, " => %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; + this->logger->log(this->logger, RAW|LEVEL2, " => %d", *(this->out_position)); + this->out_position++; + this->current_bit = 0; + + } + else + { + this->logger->log(this->logger, ERROR, "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)); + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR, "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 = htons(*((u_int16_t*)(this->data_struct + offset))); + /* last bit must be unset */ + int16_val = int16_val & 0xFF7F; + + int16_val = int16_val | attribute_format_flag; + this->logger->log(this->logger, RAW|LEVEL2, " => %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))); + this->logger->log_bytes(this->logger, RAW|LEVEL2, " =>", (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))); + this->logger->log_bytes(this->logger, RAW|LEVEL2, " =>", (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)); + this->logger->log_bytes(this->logger, RAW|LEVEL2, " => (low)", (void*)&int32_val_low, sizeof(int32_val_low)); + this->logger->log_bytes(this->logger, RAW|LEVEL2, " => (high)", (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)); + this->logger->log_bytes(this->logger, RAW|LEVEL2, " =>", (void*)(this->data_struct + offset), sizeof(u_int64_t)); + break; + } + default: + { + this->logger->log(this->logger, ERROR, "U_INT Type %s is not supported", mapping_find(encoding_type_m,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)) + { + this->logger->log(this->logger, ERROR, "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) + { + this->logger->log(this->logger, ERROR, + "Reserved field cannot be written cause allignement 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; + + + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR, "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); + + this->logger->log_chunk(this->logger, RAW|LEVEL2, " =>", *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)); + + this->logger->log(this->logger, CONTROL|LEVEL3, "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; + + this->logger->log_chunk(this->logger, RAW|LEVEL3, "generated data of this generator", *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; + + this->logger->log(this->logger, CONTROL|LEVEL1, "generating payload of type %s", + mapping_find(payload_type_m,payload_type)); + + /* each payload has its own encoding rules */ + payload->get_encoding_rules(payload,&rules,&rule_count); + + for (i = 0; i < rule_count;i++) + { + this->logger->log(this->logger, CONTROL|LEVEL2, " generating rule %d %s", + i, mapping_find(encoding_type_m,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_MESSAGE: + { + 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_MESSAGE: + 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; + /* create forward iterator */ + iterator = proposals->create_iterator(proposals,TRUE); + /* every proposal is processed (iterative call )*/ + while (iterator->has_next(iterator)) + { + payload_t *current_proposal; + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + iterator->current(iterator,(void **)¤t_proposal); + + 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; + + /* create forward iterator */ + iterator = transforms->create_iterator(transforms,TRUE); + while (iterator->has_next(iterator)) + { + payload_t *current_transform; + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + iterator->current(iterator,(void **)¤t_transform); + + 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; + /* create forward iterator */ + iterator = transform_attributes->create_iterator(transform_attributes,TRUE); + while (iterator->has_next(iterator)) + { + payload_t *current_attribute; + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + iterator->current(iterator,(void **)¤t_attribute); + + 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; + /* create forward iterator */ + iterator = configuration_attributes->create_iterator(configuration_attributes,TRUE); + while (iterator->has_next(iterator)) + { + payload_t *current_attribute; + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + iterator->current(iterator,(void **)¤t_attribute); + + 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) + { + this->logger->log(this->logger, CONTROL|LEVEL3, "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; + /* create forward iterator */ + iterator = traffic_selectors->create_iterator(traffic_selectors,TRUE); + /* every proposal is processed (iterative call )*/ + while (iterator->has_next(iterator)) + { + payload_t *current_traffic_selector_substructure; + u_int32_t before_generate_position_offset; + u_int32_t after_generate_position_offset; + + iterator->current(iterator,(void **)¤t_traffic_selector_substructure); + + 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: + this->logger->log(this->logger, ERROR, "field type %s is not supported", + mapping_find(encoding_type_m,rules[i].type)); + return; + } + } + this->logger->log(this->logger, CONTROL|LEVEL2, "generating %s payload finished.", + mapping_find(payload_type_m, payload_type)); + this->logger->log_bytes(this->logger, RAW|LEVEL3, "generated data for this payload", + 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; + this->logger = logger_manager->get_logger(logger_manager, GENERATOR); + + return &(this->public); +} diff --git a/src/charon/charon/encoding/generator.h b/src/charon/charon/encoding/generator.h new file mode 100644 index 000000000..717d32b73 --- /dev/null +++ b/src/charon/charon/encoding/generator.h @@ -0,0 +1,101 @@ +/** + * @file generator.h + * + * @brief Interface of generator_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 GENERATOR_H_ +#define GENERATOR_H_ + +#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 + + +typedef struct generator_t generator_t; + +/** + * @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(); + +#endif /*GENERATOR_H_*/ diff --git a/src/charon/charon/encoding/message.c b/src/charon/charon/encoding/message.c new file mode 100644 index 000000000..a57315272 --- /dev/null +++ b/src/charon/charon/encoding/message.c @@ -0,0 +1,1251 @@ +/** + * @file message.c + * + * @brief Implementation of message_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "message.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Max number of notify payloads per IKEv2 Message + */ +#define MAX_NOTIFY_PAYLOADS 10 + + +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}, +}; + +/** + * 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}, +}; + +/** + * Message rule for IKE_AUTH from initiator. + */ +static payload_rule_t ike_auth_i_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE}, + {ID_INITIATOR,1,1,TRUE,FALSE}, + {CERTIFICATE,0,1,TRUE,FALSE}, + {CERTIFICATE_REQUEST,0,1,TRUE,FALSE}, + {ID_RESPONDER,0,1,TRUE,FALSE}, + {AUTHENTICATION,1,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}, +}; + +/** + * Message rule for IKE_AUTH from responder. + */ +static payload_rule_t ike_auth_r_payload_rules[] = { + {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,TRUE}, + {CERTIFICATE,0,1,TRUE,FALSE}, + {ID_RESPONDER,1,1,TRUE,FALSE}, + {AUTHENTICATION,1,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}, +}; + + +/** + * 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}, + +}; + +/** + * 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}, +}; + + +/** + * 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} +}; + + +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; + + /** + * Assigned logger. + */ + logger_t *logger; + + /** + * Sets the private message_rule member to the rule which + * applies to this message. Must be called before get_payload_rule(). + * + * @param this calling object + * @return + * - SUCCESS + * - NOT_FOUND if no message rule applies to this message. + */ + status_t (*set_message_rule) (private_message_t *this); + + /** + * Gets the payload_rule_t for a specific message_rule_t and payload type. + * + * @param this calling object + * @param payload_type payload type + * @param[out] payload_rule returned payload_rule_t + * @return + * - SUCCESS + * - NOT_FOUND if payload not defined in current message rule + * - INVALID_STATE if message rule is not set via set_message_rule() + */ + status_t (*get_payload_rule) (private_message_t *this, payload_type_t payload_type, payload_rule_t **payload_rule); + + /** + * Encrypts all payloads which has to get encrypted. + * + * Can also be called with messages not containing encrypted content. + * + * @param this calling object + * @param crypter crypter_t object + * @param signer signer_t object + * @return + * - SUCCESS + * - INVALID_STATE if no crypter/signer supplied but needed + */ + status_t (*encrypt_payloads) (private_message_t *this,crypter_t *crypter, signer_t* signer); + + /** + * Decrypts encrypted contents, and checks if a payload is encrypted if it has to be. + * + * @param this calling object + * @param crypter crypter_t object + * @param signer signer_t object + * @return + * - SUCCESS + * - FAILED if decryption not successfull + * - INVALID_STATE if no crypter/signer supplied but needed + */ + status_t (*decrypt_payloads) (private_message_t *this,crypter_t *crypter, signer_t* signer); + + /** + * Verifies the message. Checks for payloads count. + * + * @param calling object + * @return + * - SUCCESS if message valid, or + * - FAILED if message does not align with message rules. + */ + status_t (*verify) (private_message_t *this); +}; + +/** + * 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) +{ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); +} + +/** + * Implementation of message_t.get_ike_sa_id. + */ +static status_t get_ike_sa_id (private_message_t *this,ike_sa_id_t **ike_sa_id) +{ + if (this->ike_sa_id == NULL) + { + return FAILED; + } + *ike_sa_id = this->ike_sa_id->clone(this->ike_sa_id); + return SUCCESS; +} + +/** + * 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_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; +} + +/** + * Implementation of message_t.add_payload. + */ +static void add_payload(private_message_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->first_payload = payload->get_type(payload); + } + payload->set_next_type(payload, NO_PAYLOAD); + this->payloads->insert_last(this->payloads, (void*)payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Added payload of type %s to message", + mapping_find(payload_type_m, payload->get_type(payload))); +} + +/** + * 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_destination. + */ +static iterator_t *get_payload_iterator(private_message_t *this) +{ + return this->payloads->create_iterator(this->payloads, TRUE); +} + + +/** + * 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; + + this->logger->log(this->logger, CONTROL, "Generating message of type %s, contains %d payloads", + mapping_find(exchange_type_m,this->exchange_type), + this->payloads->get_count(this->payloads)); + + if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Exchange type %s is not defined", + mapping_find(exchange_type_m,this->exchange_type)); + return INVALID_STATE; + } + + if (this->packet->get_source(this->packet) == NULL || + this->packet->get_destination(this->packet) == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, "%s not defined", + !this->packet->get_source(this->packet) ? "source" : "destination"); + return INVALID_STATE; + } + + /* set the rules for this messge */ + status = this->set_message_rule(this); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "No message rules specified for a %s %s", + mapping_find(exchange_type_m,this->exchange_type), + this->is_request ? "request" : "response"); + return NOT_SUPPORTED; + } + + + /* going to encrypt all content which have to be encrypted */ + status = this->encrypt_payloads(this, crypter, signer); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Could not encrypt payloads"); + 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->has_next(iterator)) + { + iterator->current(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) + { + this->logger->log(this->logger, CONTROL | LEVEL1, "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); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Message of type %s generated successfully", + mapping_find(exchange_type_m,this->exchange_type)); + return SUCCESS; +} + +/** + * Implementation of message_t.get_packet. + */ +static packet_t *get_packet (private_message_t *this) +{ + return this->packet->clone(this->packet); +} + +/** + * Implementation of message_t.get_packet_data. + */ +static chunk_t get_packet_data (private_message_t *this) +{ + 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; + + + this->logger->log(this->logger, CONTROL|LEVEL1, "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) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Header could not be parsed"); + return status; + + } + + /* verify payload */ + status = ike_header->payload_interface.verify(&(ike_header->payload_interface)); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR | LEVEL1, "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)); + + this->logger->log(this->logger, CONTROL, "Parsed a %s %s", + mapping_find(exchange_type_m, this->exchange_type), + this->is_request ? "request" : "response"); + + ike_header->destroy(ike_header); + + /* get the rules for this messge */ + status = this->set_message_rule(this); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "No message rules specified for a %s %s", + mapping_find(exchange_type_m,this->exchange_type), + this->is_request ? "request" : "response"); + } + + return status; +} + +/** + * 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; + + this->logger->log(this->logger, CONTROL|LEVEL1, "Parsing body of message, first payload is %s", + mapping_find(payload_type_m, current_payload_type)); + + /* parse payload for payload, while there are more available */ + while ((current_payload_type != NO_PAYLOAD)) + { + payload_t *current_payload; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Start parsing a %s payload", + mapping_find(payload_type_m, current_payload_type)); + + /* parse current payload */ + status = this->parser->parse_payload(this->parser,current_payload_type,(payload_t **) ¤t_payload); + + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Payload type %s could not be parsed", + mapping_find(payload_type_m,current_payload_type)); + return status; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "Verify payload of type %s", + mapping_find(payload_type_m, current_payload_type)); + + /* verify it, stop parsig if its invalid */ + status = current_payload->verify(current_payload); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "%s payload verification failed", + mapping_find(payload_type_m,current_payload_type)); + current_payload->destroy(current_payload); + status = VERIFY_ERROR; + return status; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "%s payload verified. Adding to payload list", + mapping_find(payload_type_m, 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) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "%s payload found. Stop parsing", + mapping_find(payload_type_m, current_payload_type)); + break; + } + + /* get next payload type */ + current_payload_type = current_payload->get_next_type(current_payload); + } + + if (current_payload_type == ENCRYPTED) + status = this->decrypt_payloads(this,crypter,signer); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Could not decrypt payloads"); + return status; + } + + status = this->verify(this); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Verification of message failed"); + } + + this->logger->log(this->logger, CONTROL, "Message %s %s contains %d payloads", + mapping_find(exchange_type_m, this->exchange_type), + this->is_request ? "request" : "response", + this->payloads->get_count(this->payloads)); + + return status; +} + +/** + * Implementation of private_message_t.verify. + */ +static status_t verify(private_message_t *this) +{ + int i; + iterator_t *iterator; + size_t total_found_payloads = 0; + + this->logger->log(this->logger, CONTROL|LEVEL1, "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->has_next(iterator)) + { + payload_t *current_payload; + payload_type_t current_payload_type; + + iterator->current(iterator,(void **)¤t_payload); + 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)) + { + this->logger->log(this->logger, ERROR|LEVEL1, "%s (%d) is not supported, but its critical!", + mapping_find(payload_type_m, current_payload_type), 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++; + this->logger->log(this->logger, CONTROL|LEVEL2, "Found payload of type %s", + mapping_find(payload_type_m, 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "Payload of type %s more than %d times (%d) occured in current message", + mapping_find(payload_type_m, current_payload_type), + this->message_rule->payload_rules[i].max_occurence, found_payloads); + iterator->destroy(iterator); + return FAILED; + } + } + } + + if (found_payloads < this->message_rule->payload_rules[i].min_occurence) + { + this->logger->log(this->logger, ERROR|LEVEL1, "Payload of type %s not occured %d times (%d)", + mapping_find(payload_type_m, this->message_rule->payload_rules[i].payload_type), + this->message_rule->payload_rules[i].min_occurence, found_payloads); + iterator->destroy(iterator); + return FAILED; + } + 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 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; + status_t status; + + iterator = this->payloads->create_iterator(this->payloads,TRUE); + + /* process each payload and decrypt a encryption payload */ + while(iterator->has_next(iterator)) + { + payload_rule_t *payload_rule; + payload_type_t current_payload_type; + payload_t *current_payload; + + /* get current payload */ + iterator->current(iterator,(void **)¤t_payload); + + /* needed to check */ + current_payload_type = current_payload->get_type(current_payload); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Process payload of type %s", + mapping_find(payload_type_m,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; + + this->logger->log(this->logger, CONTROL | LEVEL2, "Found an encryption payload"); + + if (payload_number != this->payloads->get_count(this->payloads)) + { + /* encrypted payload is not last one */ + this->logger->log(this->logger, ERROR | LEVEL1, "Encrypted payload is not last payload"); + iterator->destroy(iterator); + return FAILED; + } + /* decrypt */ + encryption_payload->set_transforms(encryption_payload, crypter, signer); + this->logger->log(this->logger, CONTROL | LEVEL1, "Verify signature of encryption payload"); + status = encryption_payload->verify_signature(encryption_payload, this->packet->get_data(this->packet)); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR | LEVEL1, "encryption payload signature invalid"); + iterator->destroy(iterator); + return status; + } + this->logger->log(this->logger, CONTROL | LEVEL2, "Decrypt content of encryption payload"); + status = encryption_payload->decrypt(encryption_payload); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Encrypted payload could not be decrypted and parsed: %s", + mapping_find(status_m, status)); + iterator->destroy(iterator); + return status; + } + + /* 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) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "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); + this->logger->log(this->logger, CONTROL | LEVEL1, "Insert unencrypted payload of type %s at end of list.", + mapping_find(payload_type_m,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) + { + /* get the ruleset for found payload */ + status = this->get_payload_rule(this, current_payload_type, &payload_rule); + if (status != SUCCESS) + { + /* payload is not allowed */ + this->logger->log(this->logger, ERROR | LEVEL1, "Payload type %s not allowed",mapping_find(payload_type_m,current_payload_type)); + iterator->destroy(iterator); + return status; + } + + /* 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 */ + this->logger->log(this->logger, ERROR | LEVEL1, "Payload type %s should be %s!", + mapping_find(payload_type_m,current_payload_type), + (payload_rule->encrypted) ? "encrypted" : "not encrypted"); + iterator->destroy(iterator); + return FAILED; + } + } + /* 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.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) + { + this->logger->log(this->logger, CONTROL | LEVEL1, "Message doesn't have to be encrypted"); + /* message contains no content to encrypt */ + return SUCCESS; + } + + this->logger->log(this->logger, CONTROL | LEVEL2, "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(); + + this->logger->log(this->logger, CONTROL | LEVEL2, "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); + this->logger->log(this->logger, CONTROL | LEVEL3, "Get rule for payload %s", + mapping_find(payload_type_m,current_payload->get_type(current_payload))); + + status = this->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)) + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Payload %s has to get encrypted", + mapping_find(payload_type_m,current_payload->get_type(current_payload))); + to_encrypt = TRUE; + } + else if (status != SUCCESS) + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Payload %s not defined for exchange type %s. Handle it anyway", + mapping_find(payload_type_m,current_payload->get_type(current_payload)), + mapping_find(exchange_type_m,this->exchange_type)); + } + + if (to_encrypt) + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Insert payload %s to encryption payload", + mapping_find(payload_type_m,current_payload->get_type(current_payload))); + + encryption_payload->add_payload(encryption_payload,current_payload); + } + else + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Insert payload %s as payload wich does not have to be encrypted", + mapping_find(payload_type_m,current_payload->get_type(current_payload))); + this->public.add_payload(&(this->public), (payload_t*)encryption_payload); + } + } + + status = SUCCESS; + this->logger->log(this->logger, CONTROL | LEVEL2, "Set transforms for encryption payload "); + encryption_payload->set_transforms(encryption_payload,crypter,signer); + this->logger->log(this->logger, CONTROL | LEVEL1, "Encrypt all payloads of encrypted payload"); + status = encryption_payload->encrypt(encryption_payload); + this->logger->log(this->logger, CONTROL | LEVEL2, "Add encrypted payload to payload list"); + this->public.add_payload(&(this->public), (payload_t*)encryption_payload); + + all_payloads->destroy(all_payloads); + + return status; +} + + +/** + * Implementation of message_t.destroy. + */ +static void destroy (private_message_t *this) +{ + iterator_t *iterator; + + this->logger->log(this->logger, CONTROL|LEVEL3, "Going to destroy message_t object"); + + this->packet->destroy(this->packet); + + if (this->ike_sa_id != NULL) + { + this->ike_sa_id->destroy(this->ike_sa_id); + } + + iterator = this->payloads->create_iterator(this->payloads, TRUE); + while (iterator->has_next(iterator)) + { + payload_t *payload; + iterator->current(iterator, (void**)&payload); + this->logger->log(this->logger, CONTROL|LEVEL3, "Destroying payload of type %s", + mapping_find(payload_type_m, payload->get_type(payload))); + payload->destroy(payload); + } + iterator->destroy(iterator); + this->payloads->destroy(this->payloads); + 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_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 = (status_t(*)(message_t*, ike_sa_id_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.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.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 functions */ + this->set_message_rule = set_message_rule; + this->get_payload_rule = get_payload_rule; + this->encrypt_payloads = encrypt_payloads; + this->decrypt_payloads = decrypt_payloads; + this->verify = verify; + + /* 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)); + + this->logger = logger_manager->get_logger(logger_manager, MESSAGE); + + return (&this->public); +} + +/* + * Described in Header. + */ +message_t *message_create() +{ + return message_create_from_packet(NULL); +} + +/* + * Described in Header. + */ +message_t *message_create_notify_reply(host_t *source, host_t *destination, exchange_type_t exchange_type, bool original_initiator,ike_sa_id_t *ike_sa_id,notify_message_type_t notify_type) +{ + message_t *message = message_create_from_packet(NULL); + notify_payload_t *payload; + + message->set_source(message, source->clone(source)); + message->set_destination(message, destination->clone(destination)); + message->set_exchange_type(message, exchange_type); + message->set_request(message, FALSE); + message->set_message_id(message,0); + message->set_ike_sa_id(message, ike_sa_id); + + payload = notify_payload_create_from_protocol_and_type(PROTO_IKE, notify_type); + message->add_payload(message,(payload_t *) payload); + + return message; +} diff --git a/src/charon/charon/encoding/message.h b/src/charon/charon/encoding/message.h new file mode 100644 index 000000000..e3a72f439 --- /dev/null +++ b/src/charon/charon/encoding/message.h @@ -0,0 +1,367 @@ +/** + * @file message.h + * + * @brief Interface of message_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 MESSAGE_H_ +#define MESSAGE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct message_t message_t; + +/** + * @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 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. + * + * @warning ike_sa_id gets cloned internaly and + * so can be destroyed afterwards. + * + * @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. + * + * @warning The returned ike_sa_id is a clone of the internal one. + * So it has to be destroyed by the caller. + * + * @param this message_t object + * @param ike_sa_id pointer to ike_sa_id pointer which will be set + * @return + * - SUCCESS + * - FAILED if no ike_sa_id is set + */ + status_t (*get_ike_sa_id) (message_t *this,ike_sa_id_t **ike_sa_id); + + /** + * @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 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 header could be parsed + * - NOT_SUPPORTED if ciritcal unknown payloads found + * - FAILED if message type is not suppported! + * - PARSE_ERROR if corrupted/invalid data found + * - VERIFY_ERROR if verification of some payload 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. + * + * @param this message_t object + * @param crypter crypter to use when a payload must be encrypted + * @param signer signer to build a mac + * @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); + + /** + * 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); + + /** + * 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(); + +/** + * @brief Creates an message_t object of type reply containing a notify payload. + * + * @return message_t object + * + * @ingroup encoding + */ +message_t *message_create_notify_reply(host_t *source, host_t *destination, exchange_type_t exchange_type, bool original_initiator,ike_sa_id_t *ike_sa_id,notify_message_type_t notify_type); + +#endif /*MESSAGE_H_*/ diff --git a/src/charon/charon/encoding/parser.c b/src/charon/charon/encoding/parser.c new file mode 100644 index 000000000..a589e9bde --- /dev/null +++ b/src/charon/charon/encoding/parser.c @@ -0,0 +1,1065 @@ +/** + * @file parser.c + * + * @brief Implementation of parser_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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 +#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 recursivly 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; + + /** + * Assigned logger_t object. + */ + logger_t *logger; +}; + +/** + * 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, + 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: + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, + this->rules[rule_number].type), this->bit_pos); + return PARSE_ERROR; + } + + if (output_pos != NULL) + { + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, + this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, + this->rules[rule_number].type), this->bit_pos); + return PARSE_ERROR; + } + + /* caller interested in result ? */ + if (output_pos != NULL) + { + *output_pos = *(this->byte_pos); + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, + this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos != 1) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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; + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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)); + + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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)); + + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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)); + + this->logger->log_bytes(this->logger, RAW|LEVEL2, " =>", (void*)output_pos, 8); + } + 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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); + + this->logger->log_bytes(this->logger, RAW|LEVEL2, " =>", (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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " not enough input to parse rule %d %s", + rule_number, mapping_find(encoding_type_m, 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; + } + + this->logger->log(this->logger, RAW|LEVEL2, " => %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) + { + this->logger->log(this->logger, ERROR|LEVEL1, " invalid length for rule %d %s", + rule_number, mapping_find(encoding_type_m, this->rules[rule_number].type)); + return PARSE_ERROR; + } + + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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; + this->logger->log(this->logger, CONTROL|LEVEL1, " %d bytes left, parsing recursivly %s", + length, mapping_find(payload_type_m, payload_type)); + status = this->public.parse_payload((parser_t*)this, payload_type, &payload); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, " parsing of a %s substructure failed", + mapping_find(payload_type_m, 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) + { + this->logger->log(this->logger, ERROR, " not enough input (%d bytes) to parse rule %d %s", + length, rule_number, mapping_find(encoding_type_m, this->rules[rule_number].type)); + return PARSE_ERROR; + } + if (this->bit_pos) + { + this->logger->log(this->logger, ERROR, " found rule %d %s on bitpos %d", + rule_number, mapping_find(encoding_type_m, 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; + this->logger->log_bytes(this->logger, RAW|LEVEL2, " =>", (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, spi_size, attribute_length; + u_int16_t ts_type; + bool attribute_format; + int rule_number; + encoding_rule_t *rule; + + /* create instance of the payload to parse */ + pld = payload_create(payload_type); + + this->logger->log(this->logger, CONTROL|LEVEL1, "parsing %s payload, %d bytes left", + mapping_find(payload_type_m, payload_type), + this->input_roof-this->byte_pos); + + this->logger->log_bytes(this->logger, RAW|LEVEL3, "parsing payload from", this->byte_pos, + this->input_roof-this->byte_pos); + + if (pld->get_type(pld) == UNKNOWN_PAYLOAD) + { + this->logger->log(this->logger, ERROR|LEVEL1, " payload type %d is unknown, handling as %s", + payload_type, mapping_find(payload_type_m, 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]); + this->logger->log(this->logger, CONTROL|LEVEL2, " parsing rule %d %s", + rule_number, mapping_find(encoding_type_m, 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_MESSAGE: + { + 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: + { + this->logger->log(this->logger, ERROR, " no rule to parse rule %d %s (%d)", rule_number, mapping_find(encoding_type_m, rule->type), rule->type); + pld->destroy(pld); + return PARSE_ERROR; + } + } + /* process next rulue */ + rule++; + } + + *payload = pld; + this->logger->log(this->logger, CONTROL|LEVEL2, "parsing %s payload finished.", + mapping_find(payload_type_m, 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->logger = logger_manager->get_logger(logger_manager, PARSER); + + 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/charon/encoding/parser.h b/src/charon/charon/encoding/parser.h new file mode 100644 index 000000000..216fac9b7 --- /dev/null +++ b/src/charon/charon/encoding/parser.h @@ -0,0 +1,95 @@ +/** + * @file parser.h + * + * @brief Interface of parser_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PARSER_H_ +#define PARSER_H_ + +#include +#include +#include + + +typedef struct parser_t parser_t; + +/** + * @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/charon/encoding/payloads/Makefile.payloads b/src/charon/charon/encoding/payloads/Makefile.payloads new file mode 100644 index 000000000..61d920907 --- /dev/null +++ b/src/charon/charon/encoding/payloads/Makefile.payloads @@ -0,0 +1,108 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +PAYLOADS_DIR= $(ENCODING_DIR)payloads/ + +CHARON_OBJS+= $(BUILD_DIR)encodings.o +$(BUILD_DIR)encodings.o : $(PAYLOADS_DIR)encodings.c $(PAYLOADS_DIR)encodings.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_header.o +$(BUILD_DIR)ike_header.o : $(PAYLOADS_DIR)ike_header.c $(PAYLOADS_DIR)ike_header.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ke_payload.o +$(BUILD_DIR)ke_payload.o : $(PAYLOADS_DIR)ke_payload.c $(PAYLOADS_DIR)ke_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)nonce_payload.o +$(BUILD_DIR)nonce_payload.o : $(PAYLOADS_DIR)nonce_payload.c $(PAYLOADS_DIR)nonce_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)notify_payload.o +$(BUILD_DIR)notify_payload.o : $(PAYLOADS_DIR)notify_payload.c $(PAYLOADS_DIR)notify_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)id_payload.o +$(BUILD_DIR)id_payload.o : $(PAYLOADS_DIR)id_payload.c $(PAYLOADS_DIR)id_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)auth_payload.o +$(BUILD_DIR)auth_payload.o : $(PAYLOADS_DIR)auth_payload.c $(PAYLOADS_DIR)auth_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)cert_payload.o +$(BUILD_DIR)cert_payload.o : $(PAYLOADS_DIR)cert_payload.c $(PAYLOADS_DIR)cert_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)certreq_payload.o +$(BUILD_DIR)certreq_payload.o : $(PAYLOADS_DIR)certreq_payload.c $(PAYLOADS_DIR)certreq_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)delete_payload.o +$(BUILD_DIR)delete_payload.o : $(PAYLOADS_DIR)delete_payload.c $(PAYLOADS_DIR)delete_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)vendor_id_payload.o +$(BUILD_DIR)vendor_id_payload.o : $(PAYLOADS_DIR)vendor_id_payload.c $(PAYLOADS_DIR)vendor_id_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)cp_payload.o +$(BUILD_DIR)cp_payload.o : $(PAYLOADS_DIR)cp_payload.c $(PAYLOADS_DIR)cp_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)configuration_attribute.o +$(BUILD_DIR)configuration_attribute.o : $(PAYLOADS_DIR)configuration_attribute.c $(PAYLOADS_DIR)configuration_attribute.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)eap_payload.o +$(BUILD_DIR)eap_payload.o : $(PAYLOADS_DIR)eap_payload.c $(PAYLOADS_DIR)eap_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)unknown_payload.o +$(BUILD_DIR)unknown_payload.o : $(PAYLOADS_DIR)unknown_payload.c $(PAYLOADS_DIR)unknown_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ts_payload.o +$(BUILD_DIR)ts_payload.o : $(PAYLOADS_DIR)ts_payload.c $(PAYLOADS_DIR)ts_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)traffic_selector_substructure.o +$(BUILD_DIR)traffic_selector_substructure.o : $(PAYLOADS_DIR)traffic_selector_substructure.c $(PAYLOADS_DIR)traffic_selector_substructure.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)payload.o +$(BUILD_DIR)payload.o : $(PAYLOADS_DIR)payload.c $(PAYLOADS_DIR)payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)proposal_substructure.o +$(BUILD_DIR)proposal_substructure.o : $(PAYLOADS_DIR)proposal_substructure.c $(PAYLOADS_DIR)proposal_substructure.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)sa_payload.o +$(BUILD_DIR)sa_payload.o : $(PAYLOADS_DIR)sa_payload.c $(PAYLOADS_DIR)sa_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)transform_attribute.o +$(BUILD_DIR)transform_attribute.o : $(PAYLOADS_DIR)transform_attribute.c $(PAYLOADS_DIR)transform_attribute.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)transform_substructure.o +$(BUILD_DIR)transform_substructure.o : $(PAYLOADS_DIR)transform_substructure.c $(PAYLOADS_DIR)transform_substructure.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)encryption_payload.o +$(BUILD_DIR)encryption_payload.o : $(PAYLOADS_DIR)encryption_payload.c $(PAYLOADS_DIR)encryption_payload.h + $(CC) $(CFLAGS) -c -o $@ $< + diff --git a/src/charon/charon/encoding/payloads/auth_payload.c b/src/charon/charon/encoding/payloads/auth_payload.c new file mode 100644 index 000000000..cc7c4bfb1 --- /dev/null +++ b/src/charon/charon/encoding/payloads/auth_payload.c @@ -0,0 +1,265 @@ +/** + * @file auth_payload.h + * + * @brief Implementation of auth_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/auth_payload.h b/src/charon/charon/encoding/payloads/auth_payload.h new file mode 100644 index 000000000..e099cdfef --- /dev/null +++ b/src/charon/charon/encoding/payloads/auth_payload.h @@ -0,0 +1,122 @@ +/** + * @file auth_payload.h + * + * @brief Interface of auth_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 AUTH_PAYLOAD_H_ +#define AUTH_PAYLOAD_H_ + +#include +#include +#include + +/** + * Length of a auth payload without the auth data in bytes. + * + * @ingroup payloads + */ +#define AUTH_PAYLOAD_HEADER_LENGTH 8 + + +typedef struct auth_payload_t auth_payload_t; + +/** + * @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(); + + +#endif /* AUTH_PAYLOAD_H_ */ diff --git a/src/charon/charon/encoding/payloads/cert_payload.c b/src/charon/charon/encoding/payloads/cert_payload.c new file mode 100644 index 000000000..146d42eda --- /dev/null +++ b/src/charon/charon/encoding/payloads/cert_payload.c @@ -0,0 +1,279 @@ +/** + * @file cert_payload.c + * + * @brief Implementation of cert_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "cert_payload.h" + + +/** + * String mappings for cert_encoding_t. + */ +mapping_t cert_encoding_m[] = { + {PKCS7_WRAPPED_X509_CERTIFICATE, "PKCS7_WRAPPED_X509_CERTIFICATE"}, + {PGP_CERTIFICATE, "PGP_CERTIFICATE"}, + {DNS_SIGNED_KEY, "DNS_SIGNED_KEY"}, + {X509_CERTIFICATE_SIGNATURE, "X509_CERTIFICATE_SIGNATURE"}, + {KERBEROS_TOKEN, "KERBEROS_TOKEN"}, + {CERTIFICATE_REVOCATION_LIST, "CERTIFICATE_REVOCATION_LIST"}, + {AUTHORITY_REVOCATION_LIST, "AUTHORITY_REVOCATION_LIST"}, + {SPKI_CERTIFICATE, "SPKI_CERTIFICATE"}, + {X509_CERTIFICATE_ATTRIBUTE, "X509_CERTIFICATE_ATTRIBUTE"}, + {RAW_SA_KEY, "RAW_SA_KEY"}, + {HASH_AND_URL_X509_CERTIFICATE, "HASH_AND_URL_X509_CERTIFICATE"}, + {HASH_AND_URL_X509_BUNDLE, "HASH_AND_URL_X509_BUNDLE"}, + {MAPPING_END, NULL} +}; + + +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 >= 14) && (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_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/cert_payload.h b/src/charon/charon/encoding/payloads/cert_payload.h new file mode 100644 index 000000000..9148cfd31 --- /dev/null +++ b/src/charon/charon/encoding/payloads/cert_payload.h @@ -0,0 +1,155 @@ +/** + * @file cert_payload.h + * + * @brief Interface of cert_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 CERT_PAYLOAD_H_ +#define CERT_PAYLOAD_H_ + +#include +#include + +/** + * Length of a cert payload without the cert data in bytes. + * + * @ingroup payloads + */ +#define CERT_PAYLOAD_HEADER_LENGTH 5 + + +typedef enum cert_encoding_t cert_encoding_t; + +/** + * @brief Certificate encoding, as described in IKEv2 RFC section 3.6 + * + * @ingroup payloads + */ +enum cert_encoding_t { + PKCS7_WRAPPED_X509_CERTIFICATE = 1, + PGP_CERTIFICATE = 2, + DNS_SIGNED_KEY = 3, + X509_CERTIFICATE_SIGNATURE = 4, + KERBEROS_TOKEN = 6, + CERTIFICATE_REVOCATION_LIST = 7, + AUTHORITY_REVOCATION_LIST = 8, + SPKI_CERTIFICATE = 9, + X509_CERTIFICATE_ATTRIBUTE = 10, + RAW_SA_KEY = 11, + HASH_AND_URL_X509_CERTIFICATE = 12, + HASH_AND_URL_X509_BUNDLE = 13 +}; + +/** + * string mappings for cert_encoding_t. + * + * @ingroup payloads + */ +extern mapping_t cert_encoding_m[]; + + +typedef struct cert_payload_t cert_payload_t; + +/** + * @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(); + + +#endif /* CERT_PAYLOAD_H_ */ diff --git a/src/charon/charon/encoding/payloads/certreq_payload.c b/src/charon/charon/encoding/payloads/certreq_payload.c new file mode 100644 index 000000000..cdab82be4 --- /dev/null +++ b/src/charon/charon/encoding/payloads/certreq_payload.c @@ -0,0 +1,259 @@ +/** + * @file certreq_payload.c + * + * @brief Implementation of certreq_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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 >= 14) && (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_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/certreq_payload.h b/src/charon/charon/encoding/payloads/certreq_payload.h new file mode 100644 index 000000000..3e88e7ffe --- /dev/null +++ b/src/charon/charon/encoding/payloads/certreq_payload.h @@ -0,0 +1,125 @@ +/** + * @file certreq_payload.h + * + * @brief Interface of certreq_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 CERTREQ_PAYLOAD_H_ +#define CERTREQ_PAYLOAD_H_ + +#include +#include +#include + +/** + * Length of a CERTREQ payload without the CERTREQ data in bytes. + * + * @ingroup payloads + */ +#define CERTREQ_PAYLOAD_HEADER_LENGTH 5 + + +typedef struct certreq_payload_t certreq_payload_t; + +/** + * @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(); + + +#endif /* CERTREQ_PAYLOAD_H_ */ diff --git a/src/charon/charon/encoding/payloads/configuration_attribute.c b/src/charon/charon/encoding/payloads/configuration_attribute.c new file mode 100644 index 000000000..489d7f372 --- /dev/null +++ b/src/charon/charon/encoding/payloads/configuration_attribute.c @@ -0,0 +1,282 @@ +/** + * @file configuration_attribute.c + * + * @brief Implementation of configuration_attribute_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "configuration_attribute.h" + +#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; +}; + +/** + * String mappings for configuration_attribute_type_t. + */ +mapping_t configuration_attribute_type_m[] = { + {INTERNAL_IP4_ADDRESS, "INTERNAL_IP4_ADDRESS"}, + {INTERNAL_IP4_NETMASK, "INTERNAL_IP4_NETMASK"}, + {INTERNAL_IP4_DNS, "INTERNAL_IP4_DNS"}, + {INTERNAL_IP4_NBNS, "INTERNAL_IP4_NBNS"}, + {INTERNAL_ADDRESS_EXPIRY, "INTERNAL_ADDRESS_EXPIRY"}, + {INTERNAL_IP4_DHCP, "INTERNAL_IP4_DHCP"}, + {APPLICATION_VERSION, "APPLICATION_VERSION"}, + {INTERNAL_IP6_ADDRESS, "INTERNAL_IP6_ADDRESS"}, + {INTERNAL_IP6_DNS, "INTERNAL_IP6_DNS"}, + {INTERNAL_IP6_NBNS, "INTERNAL_IP6_NBNS"}, + {INTERNAL_IP6_DHCP, "INTERNAL_IP6_DHCP"}, + {INTERNAL_IP4_SUBNET, "INTERNAL_IP4_SUBNET"}, + {SUPPORTED_ATTRIBUTES, "SUPPORTED_ATTRIBUTES"}, + {INTERNAL_IP6_SUBNET, "INTERNAL_IP6_SUBNET"}, + {MAPPING_END, NULL} +}; + + +/** + * 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) +{ + 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: + case APPLICATION_VERSION: + case INTERNAL_IP6_ADDRESS: + case INTERNAL_IP6_DNS: + case INTERNAL_IP6_NBNS: + case INTERNAL_IP6_DHCP: + case INTERNAL_IP4_SUBNET: + case SUPPORTED_ATTRIBUTES: + case INTERNAL_IP6_SUBNET: + { + /* Attribute types are not checked in here */ + break; + } + default: + return FAILED; + } + + if (this->attribute_length != this->attribute_value.len) + { + 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_attribute_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_attribute_type. + */ +static u_int16_t get_attribute_type (private_configuration_attribute_t *this) +{ + return this->attribute_type; +} + +/** + * Implementation of configuration_attribute_t.get_attribute_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_attribute_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type; + this->public.get_attribute_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type; + this->public.get_attribute_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_INITIALIZER; + this->attribute_length = 0; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/configuration_attribute.h b/src/charon/charon/encoding/payloads/configuration_attribute.h new file mode 100644 index 000000000..5b6b4f473 --- /dev/null +++ b/src/charon/charon/encoding/payloads/configuration_attribute.h @@ -0,0 +1,149 @@ +/** + * @file configuration_attribute.h + * + * @brief Interface of configuration_attribute_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_ATTRIBUTE_H_ +#define CONFIGURATION_ATTRIBUTE_H_ + +#include +#include + + + +/** + * Configuration attribute header length in bytes. + * + * @ingroup payloads + */ +#define CONFIGURATION_ATTRIBUTE_HEADER_LENGTH 4 + + +typedef enum configuration_attribute_type_t configuration_attribute_type_t; + +/** + * 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 +}; + +/** + * String mappings for configuration_attribute_type_t. + * + * @ingroup payloads + */ +extern mapping_t configuration_attribute_type_m[]; + +typedef struct configuration_attribute_t configuration_attribute_t; + +/** + * @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_attribute_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_attribute_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_attribute_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(); + +#endif /* CONFIGURATION_ATTRIBUTE_H_*/ diff --git a/src/charon/charon/encoding/payloads/cp_payload.c b/src/charon/charon/encoding/payloads/cp_payload.c new file mode 100644 index 000000000..583488382 --- /dev/null +++ b/src/charon/charon/encoding/payloads/cp_payload.c @@ -0,0 +1,305 @@ +/** + * @file cp_payload.c + * + * @brief Implementation of cp_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "cp_payload.h" + +#include +#include + + +/** + * String mappings for config_type_t. + */ +mapping_t config_type_m[] = { + {CFG_REQUEST, "CFG_REQUEST"}, + {CFG_REPLY, "CFG_REPLY"}, + {CFG_SET, "CFG_SET"}, + {CFG_ACK, "CFG_ACK"}, + {MAPPING_END, NULL} +}; + + +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; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_cp_payload_t object + */ + void (*compute_length) (private_cp_payload_t *this); +}; + +/** + * 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; + + iterator = this->attributes->create_iterator(this->attributes,TRUE); + + while(iterator->has_next(iterator)) + { + configuration_attribute_t *attribute; + iterator->current(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; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_cp_payload_t *this) +{ + this->compute_length(this); + return this->payload_length; +} + +/** + * Implementation of cp_payload_t.create_configuration_attribute_iterator. + */ +static iterator_t *create_configuration_attribute_iterator (private_cp_payload_t *this,bool forward) +{ + return this->attributes->create_iterator(this->attributes,forward); +} + +/** + * 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); + this->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 private_cp_payload_t.compute_length. + */ +static void compute_length (private_cp_payload_t *this) +{ + iterator_t *iterator; + size_t length = CP_PAYLOAD_HEADER_LENGTH; + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while (iterator->has_next(iterator)) + { + payload_t *current_attribute; + iterator->current(iterator,(void **) ¤t_attribute); + length += current_attribute->get_length(current_attribute); + } + iterator->destroy(iterator); + + this->payload_length = length; +} + +/** + * Implementation of payload_t.destroy and cp_payload_t.destroy. + */ +static status_t destroy(private_cp_payload_t *this) +{ + /* all attributes are getting destroyed */ + while (this->attributes->get_count(this->attributes) > 0) + { + configuration_attribute_t *current_attribute; + this->attributes->remove_last(this->attributes,(void **)¤t_attribute); + current_attribute->destroy(current_attribute); + } + this->attributes->destroy(this->attributes); + + free(this); + + return SUCCESS; +} + +/* + * 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_configuration_attribute_iterator = (iterator_t* (*) (cp_payload_t *,bool)) create_configuration_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; + + + /* private functions */ + this->compute_length = compute_length; + + /* 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/charon/encoding/payloads/cp_payload.h b/src/charon/charon/encoding/payloads/cp_payload.h new file mode 100644 index 000000000..eb8076446 --- /dev/null +++ b/src/charon/charon/encoding/payloads/cp_payload.h @@ -0,0 +1,138 @@ +/** + * @file cp_payload.h + * + * @brief Interface of cp_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 CP_PAYLOAD_H_ +#define CP_PAYLOAD_H_ + +#include +#include +#include +#include + +/** + * CP_PAYLOAD length in bytes without any proposal substructure. + * + * @ingroup payloads + */ +#define CP_PAYLOAD_HEADER_LENGTH 8 + + +typedef enum config_type_t config_type_t; + +/** + * Config Type of an Configuration Payload. + * + * @ingroup payloads + */ +enum config_type_t { + CFG_REQUEST = 1, + CFG_REPLY = 2, + CFG_SET = 3, + CFG_ACK = 4, +}; + +/** + * string mappings for config_type_t. + * + * @ingroup payloads + */ +extern mapping_t config_type_m[]; + + +typedef struct cp_payload_t cp_payload_t; + +/** + * @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. + * + * @warning The created iterator has to get destroyed by the caller! + * + * @warning 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 + * @param[in] forward iterator direction (TRUE: front to end) + * @return created iterator_t object + */ + iterator_t *(*create_configuration_attribute_iterator) (cp_payload_t *this, bool forward); + + /** + * @brief Adds a configuration_attribute_t object to this object. + * + * @warning 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(); + +#endif /*CP_PAYLOAD_H_*/ diff --git a/src/charon/charon/encoding/payloads/delete_payload.c b/src/charon/charon/encoding/payloads/delete_payload.c new file mode 100644 index 000000000..28e78800f --- /dev/null +++ b/src/charon/charon/encoding/payloads/delete_payload.c @@ -0,0 +1,322 @@ +/** + * @file delete_payload.c + * + * @brief Implementation of delete_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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; +}; + +/** + * 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) +{ + if ((this->protocol_id == 0) || + (this->protocol_id > 3)) + { + /* reserved IDs */ + return FAILED; + } + if (this->spis.len != (this->spi_count * this->spi_size)) + { + return FAILED; + } + if ((this->protocol_id == PROTO_IKE) && (this->spis.len != 0)) + { + /* IKE deletion has no spi assigned! */ + 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.set_protocol_id. + */ +static void set_protocol_id (private_delete_payload_t *this, protocol_id_t protocol_id) +{ + this->protocol_id = protocol_id; +} + +/** + * 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.set_spi_size. + */ +static void set_spi_size (private_delete_payload_t *this, u_int8_t spi_size) +{ + this->spi_size = spi_size; +} + +/** + * Implementation of delete_payload_t.get_spi_size. + */ +static u_int8_t get_spi_size (private_delete_payload_t *this) +{ + return (this->spi_size); +} + +/** + * Implementation of delete_payload_t.set_spi_count. + */ +static void set_spi_count (private_delete_payload_t *this, u_int16_t spi_count) +{ + this->spi_count = spi_count; +} + +/** + * Implementation of delete_payload_t.get_spi_count. + */ +static u_int16_t get_spi_count (private_delete_payload_t *this) +{ + return (this->spi_count); +} + + +/** + * Implementation of delete_payload_t.set_spis. + */ +static void set_spis (private_delete_payload_t *this, chunk_t spis) +{ + if (this->spis.ptr != NULL) + { + chunk_free(&(this->spis)); + } + this->spis.ptr = clalloc(spis.ptr,spis.len); + this->spis.len = spis.len; + this->payload_length = DELETE_PAYLOAD_HEADER_LENGTH + this->spis.len; +} + +/** + * Implementation of delete_payload_t.get_spis. + */ +static chunk_t get_spis (private_delete_payload_t *this) +{ + return (this->spis); +} + +/** + * Implementation of delete_payload_t.get_spis_clone. + */ +static chunk_t get_spis_clone (private_delete_payload_t *this) +{ + chunk_t cloned_spis; + if (this->spis.ptr == NULL) + { + return (this->spis); + } + cloned_spis.ptr = clalloc(this->spis.ptr,this->spis.len); + cloned_spis.len = this->spis.len; + return cloned_spis; +} + +/** + * 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)); + } + + free(this); +} + +/* + * Described in header + */ +delete_payload_t *delete_payload_create() +{ + 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.set_protocol_id = (void (*) (delete_payload_t *,protocol_id_t)) set_protocol_id; + this->public.get_protocol_id = (protocol_id_t (*) (delete_payload_t *)) get_protocol_id; + this->public.set_spi_size = (void (*) (delete_payload_t *,u_int8_t)) set_spi_size; + this->public.get_spi_size = (u_int8_t (*) (delete_payload_t *)) get_spi_size; + this->public.set_spi_count = (void (*) (delete_payload_t *,u_int16_t)) set_spi_count; + this->public.get_spi_count = (u_int16_t (*) (delete_payload_t *)) get_spi_count; + this->public.set_spis = (void (*) (delete_payload_t *,chunk_t)) set_spis; + this->public.get_spis_clone = (chunk_t (*) (delete_payload_t *)) get_spis_clone; + this->public.get_spis = (chunk_t (*) (delete_payload_t *)) get_spis; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length =DELETE_PAYLOAD_HEADER_LENGTH; + this->protocol_id = PROTO_NONE; + this->spi_size = 0; + this->spi_count = 0; + this->spis = CHUNK_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/delete_payload.h b/src/charon/charon/encoding/payloads/delete_payload.h new file mode 100644 index 000000000..71a6317d4 --- /dev/null +++ b/src/charon/charon/encoding/payloads/delete_payload.h @@ -0,0 +1,156 @@ +/** + * @file delete_payload.h + * + * @brief Interface of delete_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_PAYLOAD_H_ +#define DELETE_PAYLOAD_H_ + +#include +#include +#include + +/** + * Length of a delete payload without the SPI in bytes. + * + * @ingroup payloads + */ +#define DELETE_PAYLOAD_HEADER_LENGTH 8 + + + +typedef struct delete_payload_t delete_payload_t; + +/** + * @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 Set the protocol ID. + * + * @param this calling delete_payload_t object + * @param protocol_id protocol ID + */ + void (*set_protocol_id) (delete_payload_t *this, protocol_id_t protocol_id); + + /** + * @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 Set the SPI size. + * + * + * @param this calling delete_payload_t object + * @param spi_size SPI size + */ + void (*set_spi_size) (delete_payload_t *this, u_int8_t spi_size); + + /** + * @brief Get the SPI size. + * + * @param this calling delete_payload_t object + * @return SPI size + */ + u_int8_t (*get_spi_size) (delete_payload_t *this); + + /** + * @brief Set the SPI count. + * + * @param this calling delete_payload_t object + * @param spi_count SPI count + */ + void (*set_spi_count) (delete_payload_t *this, u_int16_t spi_count); + + /** + * @brief Get the SPI count. + * + * @param this calling delete_payload_t object + * @return Number of SPI's + */ + u_int16_t (*get_spi_count) (delete_payload_t *this); + + /** + * @brief Set the SPI's. + * + * Data are getting cloned. + * + * @param this calling delete_payload_t object + * @param data SPI's as chunk_t + */ + void (*set_spis) (delete_payload_t *this, chunk_t spis); + + /** + * @brief Get the SPI's. + * + * Returned data are a copy of the internal one. + * + * @param this calling delete_payload_t object + * @return SPI's chunk_t + */ + chunk_t (*get_spis_clone) (delete_payload_t *this); + + /** + * @brief Get the SPI's. + * + * Returned data are NOT copied. + * + * @param this calling delete_payload_t object + * @return SPI's as chunk_t + */ + chunk_t (*get_spis) (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. + * + * @return delete_payload_t object + * + * @ingroup payloads + */ +delete_payload_t *delete_payload_create(); + + +#endif /* DELETE_PAYLOAD_H_ */ diff --git a/src/charon/charon/encoding/payloads/eap_payload.c b/src/charon/charon/encoding/payloads/eap_payload.c new file mode 100644 index 000000000..2a0e17679 --- /dev/null +++ b/src/charon/charon/encoding/payloads/eap_payload.c @@ -0,0 +1,227 @@ +/** + * @file eap_payload.c + * + * @brief Implementation of eap_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_payload.h" + + +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; + + /** + * The contained message. + */ + chunk_t message; +}; + +/** + * 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)}, + /* some eap data bytes, length is defined in PAYLOAD_LENGTH */ + { EAP_MESSAGE, offsetof(private_eap_payload_t, message) } +}; + +/* + 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 ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ EAP Message ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Implementation of payload_t.verify. + */ +static status_t verify(private_eap_payload_t *this) +{ + 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.set_message. + */ +static void set_message (private_eap_payload_t *this, chunk_t message) +{ + if (this->message.ptr != NULL) + { + chunk_free(&(this->message)); + } + this->message.ptr = clalloc(message.ptr,message.len); + this->message.len = message.len; + this->payload_length = EAP_PAYLOAD_HEADER_LENGTH + this->message.len; +} + +/** + * Implementation of eap_payload_t.get_message. + */ +static chunk_t get_message (private_eap_payload_t *this) +{ + return (this->message); +} + +/** + * Implementation of eap_payload_t.get_data_clone. + */ +static chunk_t get_message_clone (private_eap_payload_t *this) +{ + chunk_t cloned_message; + if (this->message.ptr == NULL) + { + return (this->message); + } + cloned_message.ptr = clalloc(this->message.ptr,this->message.len); + cloned_message.len = this->message.len; + return cloned_message; +} + +/** + * Implementation of payload_t.destroy and eap_payload_t.destroy. + */ +static void destroy(private_eap_payload_t *this) +{ + if (this->message.ptr != NULL) + { + chunk_free(&(this->message)); + } + + 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.set_message = (void (*) (eap_payload_t *,chunk_t)) set_message; + this->public.get_message_clone = (chunk_t (*) (eap_payload_t *)) get_message_clone; + this->public.get_message = (chunk_t (*) (eap_payload_t *)) get_message; + + /* private variables */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = EAP_PAYLOAD_HEADER_LENGTH; + this->message = CHUNK_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/eap_payload.h b/src/charon/charon/encoding/payloads/eap_payload.h new file mode 100644 index 000000000..5e5a0c6d8 --- /dev/null +++ b/src/charon/charon/encoding/payloads/eap_payload.h @@ -0,0 +1,105 @@ +/** + * @file eap_payload.h + * + * @brief Interface of eap_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_PAYLOAD_H_ +#define EAP_PAYLOAD_H_ + +#include +#include + +/** + * Length of a EAP payload without the EAP Message in bytes. + * + * @ingroup payloads + */ +#define EAP_PAYLOAD_HEADER_LENGTH 4 + + +typedef struct eap_payload_t eap_payload_t; + +/** + * @brief Class representing an IKEv2 EAP payload. + * + * The EAP payload format is described in RFC section 3.16. + * + * @b Constructors: + * - eap_payload_create() + * + * @todo Implement functionality for this payload + * + * @ingroup payloads + */ +struct eap_payload_t { + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * @brief Set the EAP Message. + * + * Data are getting cloned. + * + * @param this calling eap_payload_t object + * @param message EAP message as chunk_t + */ + void (*set_message) (eap_payload_t *this, chunk_t message); + + /** + * @brief Get the EAP message. + * + * Returned data are a copy of the internal one. + * + * @param this calling eap_payload_t object + * @return EAP message as chunk_t + */ + chunk_t (*get_message_clone) (eap_payload_t *this); + + /** + * @brief Get the EAP message. + * + * Returned data are NOT copied. + * + * @param this calling eap_payload_t object + * @return EAP message as chunk_t + */ + chunk_t (*get_message) (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(); + + +#endif /* EAP_PAYLOAD_H_ */ diff --git a/src/charon/charon/encoding/payloads/encodings.c b/src/charon/charon/encoding/payloads/encodings.c new file mode 100644 index 000000000..da39467a9 --- /dev/null +++ b/src/charon/charon/encoding/payloads/encodings.c @@ -0,0 +1,68 @@ +/** + * @file encodings.c + * + * @brief String mappings of encoding_type_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "encodings.h" + + +mapping_t encoding_type_m[] = { + {U_INT_4, "U_INT_4"}, + {U_INT_8, "U_INT_8"}, + {U_INT_16, "U_INT_16"}, + {U_INT_32, "U_INT_32"}, + {U_INT_64, "U_INT_64"}, + {IKE_SPI, "IKE_SPI"}, + {RESERVED_BIT, "RESERVED_BIT"}, + {RESERVED_BYTE, "RESERVED_BYTE"}, + {FLAG, "FLAG"}, + {PAYLOAD_LENGTH, "PAYLOAD_LENGTH"}, + {HEADER_LENGTH, "HEADER_LENGTH"}, + {SPI_SIZE, "SPI_SIZE"}, + {SPI, "SPI"}, + {KEY_EXCHANGE_DATA, "KEY_EXCHANGE_DATA"}, + {NOTIFICATION_DATA, "NOTIFICATION_DATA"}, + {PROPOSALS, "PROPOSALS"}, + {TRANSFORMS, "TRANSFORMS"}, + {TRANSFORM_ATTRIBUTES, "TRANSFORM_ATTRIBUTES"}, + {ATTRIBUTE_FORMAT, "ATTRIBUTE_FORMAT"}, + {ATTRIBUTE_TYPE, "ATTRIBUTE_TYPE"}, + {ATTRIBUTE_LENGTH_OR_VALUE, "ATTRIBUTE_LENGTH_OR_VALUE"}, + {ATTRIBUTE_VALUE, "ATTRIBUTE_VALUE"}, + {NONCE_DATA, "NONCE_DATA"}, + {ID_DATA, "ID_DATA"}, + {AUTH_DATA, "AUTH_DATA"}, + {ENCRYPTED_DATA, "ENCRYPTED_DATA"}, + {TS_TYPE, "TS_TYPE"}, + {ADDRESS, "ADDRESS"}, + {TRAFFIC_SELECTORS, "TRAFFIC_SELECTORS"}, + {CERT_DATA, "CERT_DATA"}, + {CERTREQ_DATA, "CERTREQ_DATA"}, + {SPIS, "SPIS"}, + {VID_DATA, "VID_DATA"}, + {VID_DATA, "VID_DATA"}, + {CONFIGURATION_ATTRIBUTES, "CONFIGURATION_ATTRIBUTES"}, + {CONFIGURATION_ATTRIBUTE_LENGTH, "CONFIGURATION_ATTRIBUTE_LENGTH"}, + {CONFIGURATION_ATTRIBUTE_VALUE, "CONFIGURATION_ATTRIBUTE_VALUE"}, + {EAP_MESSAGE, "EAP_MESSAGE"}, + {UNKNOWN_DATA,"UNKNOWN_DATA"}, + {MAPPING_END, NULL} +}; diff --git a/src/charon/charon/encoding/payloads/encodings.h b/src/charon/charon/encoding/payloads/encodings.h new file mode 100644 index 000000000..e30e1c215 --- /dev/null +++ b/src/charon/charon/encoding/payloads/encodings.h @@ -0,0 +1,540 @@ +/** + * @file encodings.h + * + * @brief Definition of encoding_type_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 ENCODINGS_H_ +#define ENCODINGS_H_ + +#include +#include + + +typedef enum encoding_type_t encoding_type_t; + +/** + * @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_MESSAGE, + + /** + * 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, +}; + +/** + * mappings to map encoding_type_t's to strings + * + * @ingroup payloads + */ +extern mapping_t encoding_type_m[]; + + +typedef struct encoding_rule_t encoding_rule_t; + +/** + * 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/charon/encoding/payloads/encryption_payload.c b/src/charon/charon/encoding/payloads/encryption_payload.c new file mode 100644 index 000000000..e0ca74ff4 --- /dev/null +++ b/src/charon/charon/encoding/payloads/encryption_payload.c @@ -0,0 +1,702 @@ +/** + * @file encryption_payload.c + * + * @brief Implementation of encryption_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "encryption_payload.h" + +#include +#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; + + /** + * logger for this payload, uses MESSAGE context + */ + logger_t *logger; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_encryption_payload_t object + */ + void (*compute_length) (private_encryption_payload_t *this); + + /** + * @brief Generate payloads (unencrypted) in chunk decrypted. + * + * @param this calling private_encryption_payload_t object + */ + void (*generate) (private_encryption_payload_t *this); + + /** + * @brief Parse payloads from a (unencrypted) chunk. + * + * @param this calling private_encryption_payload_t object + */ + status_t (*parse) (private_encryption_payload_t *this); +}; + +/** + * 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*/ +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_encryption_payload_t *this) +{ + 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); + this->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); +} + + +/** + * 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) + { + this->logger->log(this->logger, ERROR, "could not encrypt, signer/crypter not set"); + return INVALID_STATE; + } + + /* for random data in iv and padding */ + randomizer = randomizer_create(); + + + /* build payload chunk */ + this->generate(this); + + this->logger->log(this->logger, CONTROL|LEVEL2, "encrypting payloads"); + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data to encrypt", 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; + } + + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data before encryption with padding", 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "encryption failed"); + free(iv.ptr); + return status; + } + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data after encryption", 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); + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data after encryption with IV and (invalid) signature", this->encrypted); + + 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; + + + this->logger->log(this->logger, CONTROL|LEVEL2, "decrypting encryption payload"); + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data before decryption with IV and (invalid) signature", this->encrypted); + + + if (this->signer == NULL || this->crypter == NULL) + { + this->logger->log(this->logger, ERROR, "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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "could not decrypt, invalid input"); + return FAILED; + } + + /* free previus data, if any */ + free(this->decrypted.ptr); + + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data before decryption", concatenated); + + status = this->crypter->decrypt(this->crypter, concatenated, iv, &(this->decrypted)); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "could not decrypt, decryption failed"); + return FAILED; + } + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data after decryption with padding", 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "decryption failed, invalid padding length found. Invalid key?"); + /* decryption failed :-/ */ + return FAILED; + } + + /* free padding */ + this->decrypted.ptr = realloc(this->decrypted.ptr, this->decrypted.len); + this->logger->log_chunk(this->logger, RAW|LEVEL2, "data after decryption without padding", this->decrypted); + this->logger->log(this->logger, CONTROL|LEVEL2, "decryption successful, trying to parse content"); + return (this->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) + { + this->logger->log(this->logger, ERROR, "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; + this->logger->log(this->logger, CONTROL|LEVEL2, "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) + { + this->logger->log(this->logger, ERROR, "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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "signature verification failed"); + return FAILED; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "signature verification successful"); + return SUCCESS; +} + +/** + * Implementation of private_encryption_payload_t.generate. + */ +static void generate(private_encryption_payload_t *this) +{ + payload_t *current_payload, *next_payload; + generator_t *generator; + iterator_t *iterator; + + /* recalculate length before generating */ + this->compute_length(this); + + /* create iterator */ + iterator = this->payloads->create_iterator(this->payloads, TRUE); + + /* get first payload */ + if (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)¤t_payload); + this->next_payload = current_payload->get_type(current_payload); + } + else + { + /* no paylads? */ + this->logger->log(this->logger, CONTROL|LEVEL1, "generating contained payloads, but no available"); + free(this->decrypted.ptr); + this->decrypted = CHUNK_INITIALIZER; + iterator->destroy(iterator); + return; + } + + generator = generator_create(); + + /* build all payload, except last */ + while(iterator->has_next(iterator)) + { + iterator->current(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); + this->logger->log(this->logger, CONTROL|LEVEL1, "successfully generated content in encrpytion payload"); +} + +/** + * Implementation of private_encryption_payload_t.parse. + */ +static status_t parse(private_encryption_payload_t *this) +{ + parser_t *parser; + status_t status; + payload_type_t current_payload_type; + + /* check if there is decrypted data */ + if (this->decrypted.ptr == NULL) + { + this->logger->log(this->logger, ERROR, "unable to parse, no input!"); + return INVALID_STATE; + } + + /* 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) + { + this->logger->log(this->logger, ERROR|LEVEL1, "%s verification failed: %s", + mapping_find(payload_type_m,current_payload->get_type(current_payload)), + mapping_find(status_m, status)); + 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); + this->logger->log(this->logger, CONTROL|LEVEL1, "succesfully parsed content of encryption payload"); + return SUCCESS; +} + +/** + * Implementation of private_encryption_payload_t.compute_length. + */ +static void compute_length(private_encryption_payload_t *this) +{ + iterator_t *iterator; + size_t block_size, length = 0; + iterator = this->payloads->create_iterator(this->payloads, TRUE); + + /* count payload length */ + while (iterator->has_next(iterator)) + { + payload_t *current_payload; + iterator->current(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.destroy. + */ +static void destroy(private_encryption_payload_t *this) +{ + /* all proposals are getting destroyed */ + while (this->payloads->get_count(this->payloads) > 0) + { + payload_t *current_payload; + this->payloads->remove_last(this->payloads,(void **)¤t_payload); + current_payload->destroy(current_payload); + } + this->payloads->destroy(this->payloads); + 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; + + /* private functions */ + this->compute_length = compute_length; + this->generate = generate; + this->parse = parse; + this->logger = logger_manager->get_logger(logger_manager, ENCRYPTION_PAYLOAD); + + /* set default values of the fields */ + this->critical = FALSE; + this->next_payload = NO_PAYLOAD; + this->payload_length = ENCRYPTION_PAYLOAD_HEADER_LENGTH; + this->encrypted = CHUNK_INITIALIZER; + this->decrypted = CHUNK_INITIALIZER; + this->signer = NULL; + this->crypter = NULL; + this->payloads = linked_list_create(); + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/encryption_payload.h b/src/charon/charon/encoding/payloads/encryption_payload.h new file mode 100644 index 000000000..77be246c5 --- /dev/null +++ b/src/charon/charon/encoding/payloads/encryption_payload.h @@ -0,0 +1,196 @@ +/** + * @file encryption_payload.h + * + * @brief Interface of encryption_payload_t. + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 ENCRYPTION_PAYLOAD_H_ +#define ENCRYPTION_PAYLOAD_H_ + +#include +#include +#include +#include +#include + +/** + * Encrpytion payload length in bytes without IV and following data. + * + * @ingroup payloads + */ +#define ENCRYPTION_PAYLOAD_HEADER_LENGTH 4 + + +typedef struct encryption_payload_t encryption_payload_t; + +/** + * @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(); + + +#endif /*ENCRYPTION_PAYLOAD_H_*/ diff --git a/src/charon/charon/encoding/payloads/id_payload.c b/src/charon/charon/encoding/payloads/id_payload.c new file mode 100644 index 000000000..6a8d7738d --- /dev/null +++ b/src/charon/charon/encoding/payloads/id_payload.c @@ -0,0 +1,320 @@ +/** + * @file id_payload.h + * + * @brief Interface of id_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "id_payload.h" + +#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 */ + 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_INITIALIZER; + 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/charon/encoding/payloads/id_payload.h b/src/charon/charon/encoding/payloads/id_payload.h new file mode 100644 index 000000000..c35b44d59 --- /dev/null +++ b/src/charon/charon/encoding/payloads/id_payload.h @@ -0,0 +1,172 @@ +/** + * @file id_payload.h + * + * @brief Interface of id_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 ID_PAYLOAD_H_ +#define ID_PAYLOAD_H_ + +#include +#include +#include + +/** + * Length of a id payload without the data in bytes. + * + * @ingroup payloads + */ +#define ID_PAYLOAD_HEADER_LENGTH 8 + + +typedef struct id_payload_t id_payload_t; + +/** + * 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/charon/encoding/payloads/ike_header.c b/src/charon/charon/encoding/payloads/ike_header.c new file mode 100644 index 000000000..ad46d3d29 --- /dev/null +++ b/src/charon/charon/encoding/payloads/ike_header.c @@ -0,0 +1,408 @@ +/** + * @file ike_header.c + * + * @brief Implementation of ike_header_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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. + */ + +/* 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; +}; + +/** + * Mappings used to get strings for exchange_type_t. + */ +mapping_t exchange_type_m[] = { + {EXCHANGE_TYPE_UNDEFINED, "EXCHANGE_TYPE_UNDEFINED"}, + {IKE_SA_INIT, "IKE_SA_INIT"}, + {IKE_AUTH, "IKE_AUTH"}, + {CREATE_CHILD_SA, "CREATE_CHILD_SA"}, + {INFORMATIONAL, "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/charon/encoding/payloads/ike_header.h b/src/charon/charon/encoding/payloads/ike_header.h new file mode 100644 index 000000000..ec55f0e18 --- /dev/null +++ b/src/charon/charon/encoding/payloads/ike_header.h @@ -0,0 +1,261 @@ +/** + * @file ike_header.h + * + * @brief Interface of ike_header_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_HEADER_H_ +#define IKE_HEADER_H_ + +#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 + +typedef enum exchange_type_t exchange_type_t; + +/** + * @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 +}; + +/** + * string mappings for exchange_type_t + * + * @ingroup payloads + */ +extern mapping_t exchange_type_m[]; + + +typedef struct ike_header_t ike_header_t; + +/** + * @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(); + +#endif /*IKE_HEADER_H_*/ diff --git a/src/charon/charon/encoding/payloads/ke_payload.c b/src/charon/charon/encoding/payloads/ke_payload.c new file mode 100644 index 000000000..0c92e033d --- /dev/null +++ b/src/charon/charon/encoding/payloads/ke_payload.c @@ -0,0 +1,276 @@ +/** + * @file ke_payload.c + * + * @brief Implementation of ke_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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. + */ + diffie_hellman_group_t dh_group_number; + + /** + * Key Exchange Data of this KE payload. + */ + chunk_t key_exchange_data; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_ke_payload_t object + */ + void (*compute_length) (private_ke_payload_t *this); +}; + +/** + * 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; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_ke_payload_t *this) +{ + this->compute_length(this); + return this->payload_length; +} + +/** + * Implementation of private_ke_payload_t.compute_length. + */ +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 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.ptr = clalloc(key_exchange_data.ptr,key_exchange_data.len); + + this->key_exchange_data.len = key_exchange_data.len; + this->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; + + /* private functions */ + this->compute_length = compute_length; + + /* 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.ptr = NULL; + this->key_exchange_data.len = 0; + this->dh_group_number = 0; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/ke_payload.h b/src/charon/charon/encoding/payloads/ke_payload.h new file mode 100644 index 000000000..982d29754 --- /dev/null +++ b/src/charon/charon/encoding/payloads/ke_payload.h @@ -0,0 +1,110 @@ +/** + * @file ke_payload.h + * + * @brief Interface of ke_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 KE_PAYLOAD_H_ +#define KE_PAYLOAD_H_ + +#include +#include +#include +#include +/** + * KE payload length in bytes without any key exchange data. + * + * @ingroup payloads + */ +#define KE_PAYLOAD_HEADER_LENGTH 8 + + +typedef struct ke_payload_t ke_payload_t; + +/** + * @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(); + + +#endif /*KE_PAYLOAD_H_*/ diff --git a/src/charon/charon/encoding/payloads/nonce_payload.c b/src/charon/charon/encoding/payloads/nonce_payload.c new file mode 100644 index 000000000..a7528fbfb --- /dev/null +++ b/src/charon/charon/encoding/payloads/nonce_payload.c @@ -0,0 +1,241 @@ +/** + * @file nonce_payload.h + * + * @brief Implementation of nonce_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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. + */ + +/* 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; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_nonce_payload_t object + */ + void (*compute_length) (private_nonce_payload_t *this); +}; + +/** + * 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; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_nonce_payload_t *this) +{ + this->compute_length(this); + return this->payload_length; +} + +/** + * Implementation of private_id_payload_t.compute_length. + */ +static void compute_length(private_nonce_payload_t *this) +{ + this->payload_length = NONCE_PAYLOAD_HEADER_LENGTH + this->nonce.len; +} + +/** + * 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 functions */ + this->compute_length = compute_length; + + /* 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/charon/encoding/payloads/nonce_payload.h b/src/charon/charon/encoding/payloads/nonce_payload.h new file mode 100644 index 000000000..366dfec15 --- /dev/null +++ b/src/charon/charon/encoding/payloads/nonce_payload.h @@ -0,0 +1,89 @@ +/** + * @file nonce_payload.h + * + * @brief Interface of nonce_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 NONCE_PAYLOAD_H_ +#define NONCE_PAYLOAD_H_ + +#include +#include + +/** + * Length of a nonce payload without a nonce in bytes. + * + * @ingroup payloads + */ +#define NONCE_PAYLOAD_HEADER_LENGTH 4 + +typedef struct nonce_payload_t nonce_payload_t; + +/** + * 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(); + + +#endif /*NONCE_PAYLOAD_H_*/ diff --git a/src/charon/charon/encoding/payloads/notify_payload.c b/src/charon/charon/encoding/payloads/notify_payload.c new file mode 100644 index 000000000..43d0c5322 --- /dev/null +++ b/src/charon/charon/encoding/payloads/notify_payload.c @@ -0,0 +1,441 @@ +/** + * @file notify_payload.c + * + * @brief Implementation of notify_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "notify_payload.h" + +#include +#include + +/** + * String mappings for notify_message_type_t. + */ +mapping_t notify_message_type_m[] = { + {UNSUPPORTED_CRITICAL_PAYLOAD, "UNSUPPORTED_CRITICAL_PAYLOAD"}, + {INVALID_IKE_SPI, "INVALID_IKE_SPI"}, + {INVALID_MAJOR_VERSION, "INVALID_MAJOR_VERSION"}, + {INVALID_SYNTAX, "INVALID_SYNTAX"}, + {INVALID_MESSAGE_ID, "INVALID_MESSAGE_ID"}, + {INVALID_SPI, "INVALID_SPI"}, + {NO_PROPOSAL_CHOSEN, "NO_PROPOSAL_CHOSEN"}, + {INVALID_KE_PAYLOAD, "INVALID_KE_PAYLOAD"}, + {AUTHENTICATION_FAILED, "AUTHENTICATION_FAILED"}, + {SINGLE_PAIR_REQUIRED, "SINGLE_PAIR_REQUIRED"}, + {NO_ADDITIONAL_SAS, "NO_ADDITIONAL_SAS"}, + {INTERNAL_ADDRESS_FAILURE, "INTERNAL_ADDRESS_FAILURE"}, + {FAILED_CP_REQUIRED, "FAILED_CP_REQUIRED"}, + {TS_UACCEPTABLE, "TS_UACCEPTABLE"}, + {INVALID_SELECTORS, "INVALID_SELECTORS"}, + {INITIAL_CONTACT, "INITIAL_CONTACT"}, + {SET_WINDOW_SIZE, "SET_WINDOW_SIZE"}, + {MAPPING_END, NULL} +}; + +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_message_type; + + /** + * Security parameter index (spi). + */ + chunk_t spi; + + /** + * Notification data. + */ + chunk_t notification_data; + + /** + * Assigned logger + */ + logger_t *logger; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_ke_payload_t object + */ + void (*compute_length) (private_notify_payload_t *this); +}; + +/** + * 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_message_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) +{ + if (this->protocol_id > 3) + { + /* reserved for future use */ + return FAILED; + } + + /* TODO: Check all kinds of notify */ + + if (this->notify_message_type == INVALID_KE_PAYLOAD) + { + /* check notification data */ + diffie_hellman_group_t dh_group; + if (this->notification_data.len != 2) + { + 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: + this->logger->log(this->logger, ERROR, "Bad DH group (%d)", dh_group); + return FAILED; + } + } + 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; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_notify_payload_t *this) +{ + this->compute_length(this); + return this->payload_length; +} + +/** + * Implementation of private_notify_payload_t.compute_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 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_message_type. + */ +static u_int16_t get_notify_message_type(private_notify_payload_t *this) +{ + return this->notify_message_type; +} + +/** + * Implementation of notify_payload_t.set_notify_message_type. + */ +static void set_notify_message_type(private_notify_payload_t *this, u_int16_t notify_message_type) +{ + this->notify_message_type = notify_message_type; +} + +/** + * Implementation of notify_payload_t.get_spi. + */ +static chunk_t get_spi(private_notify_payload_t *this) +{ + return (this->spi); +} + +/** + * Implementation of notify_payload_t.set_spi. + */ +static void set_spi(private_notify_payload_t *this, chunk_t spi) +{ + /* destroy existing data first */ + if (this->spi.ptr != NULL) + { + /* free existing value */ + free(this->spi.ptr); + this->spi.ptr = NULL; + this->spi.len = 0; + + } + + this->spi.ptr = clalloc(spi.ptr,spi.len); + + this->spi.len = spi.len; + this->spi_size = spi.len; + this->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) +{ + /* destroy existing data first */ + if (this->notification_data.ptr != NULL) + { + /* free existing value */ + free(this->notification_data.ptr); + this->notification_data.ptr = NULL; + this->notification_data.len = 0; + + } + + this->notification_data.ptr = clalloc(notification_data.ptr,notification_data.len); + this->notification_data.len = notification_data.len; + this->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) +{ + if (this->notification_data.ptr != NULL) + { + free(this->notification_data.ptr); + } + if (this->spi.ptr != NULL) + { + free(this->spi.ptr); + } + + 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_message_type = (u_int16_t (*) (notify_payload_t *)) get_notify_message_type; + this->public.set_notify_message_type = (void (*) (notify_payload_t *,u_int16_t)) set_notify_message_type; + this->public.get_spi = (chunk_t (*) (notify_payload_t *)) get_spi; + this->public.set_spi = (void (*) (notify_payload_t *,chunk_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; + + /* private functions */ + this->compute_length = compute_length; + + /* 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_message_type = 0; + this->spi.ptr = NULL; + this->spi.len = 0; + this->spi_size = 0; + this->notification_data.ptr = NULL; + this->notification_data.len = 0; + this->logger = logger_manager->get_logger(logger_manager, PAYLOAD); + + return (&(this->public)); +} + +/* + * Described in header. + */ +notify_payload_t *notify_payload_create_from_protocol_and_type(protocol_id_t protocol_id, notify_message_type_t notify_message_type) +{ + notify_payload_t *notify = notify_payload_create(); + + notify->set_notify_message_type(notify,notify_message_type); + notify->set_protocol_id(notify,protocol_id); + + return notify; +} diff --git a/src/charon/charon/encoding/payloads/notify_payload.h b/src/charon/charon/encoding/payloads/notify_payload.h new file mode 100644 index 000000000..093f99144 --- /dev/null +++ b/src/charon/charon/encoding/payloads/notify_payload.h @@ -0,0 +1,200 @@ +/** + * @file notify_payload.h + * + * @brief Interface of notify_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 NOTIFY_PAYLOAD_H_ +#define NOTIFY_PAYLOAD_H_ + +#include +#include +#include +#include + +/** + * Notify payload length in bytes without any spi and notification data. + * + * @ingroup payloads + */ +#define NOTIFY_PAYLOAD_HEADER_LENGTH 8 + +typedef enum notify_message_type_t notify_message_type_t; + + +/** + * @brief Notify message types. + * + * See IKEv2 RFC 3.10.1. + * + * @ingroup payloads + */ +enum notify_message_type_t { + 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_UACCEPTABLE = 38, + INVALID_SELECTORS = 39, + + INITIAL_CONTACT = 16384, + SET_WINDOW_SIZE = 16385 +}; + +/** + * String mappings for notify_message_type_t. + * + * @ingroup payloads + */ +extern mapping_t notify_message_type_m[]; + + +typedef struct notify_payload_t notify_payload_t; + +/** + * @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 + */ + u_int16_t (*get_notify_message_type) (notify_payload_t *this); + + /** + * @brief Sets notify message type of this payload. + * + * @param this calling notify_payload_t object + * @param notify_message_type notify message type to set + */ + void (*set_notify_message_type) (notify_payload_t *this, u_int16_t notify_message_type); + + /** + * @brief Returns the currently set spi of this payload. + * + * @warning Returned data are not copied. + * + * @param this calling notify_payload_t object + * @return chunk_t pointing to the value + */ + chunk_t (*get_spi) (notify_payload_t *this); + + /** + * @brief Sets the spi of this payload. + * + * @warning Value is getting copied. + * + * @param this calling notify_payload_t object + * @param spi chunk_t pointing to the value to set + */ + void (*set_spi) (notify_payload_t *this, chunk_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(); + +/** + * @brief Creates an notify_payload_t object of specific type for specific protocol id. + * + * @param protocol_id protocol id (IKE, AH or ESP) + * @param notify_message_type notify type (see notify_message_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_message_type_t notify_message_type); + + +#endif /*NOTIFY_PAYLOAD_H_*/ diff --git a/src/charon/charon/encoding/payloads/payload.c b/src/charon/charon/encoding/payloads/payload.c new file mode 100644 index 000000000..b89e80a53 --- /dev/null +++ b/src/charon/charon/encoding/payloads/payload.c @@ -0,0 +1,131 @@ +/** + * @file payload.c + * + * @brief Generic constructor to the payload_t interface. + * + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "payload.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * build the mappings for payload_type_t + */ +mapping_t payload_type_m[] = { + {NO_PAYLOAD, "NO_PAYLOAD"}, + {SECURITY_ASSOCIATION, "SECURITY_ASSOCIATION"}, + {KEY_EXCHANGE, "KEY_EXCHANGE"}, + {ID_INITIATOR, "ID_INITIATOR"}, + {ID_RESPONDER, "ID_RESPONDER"}, + {CERTIFICATE, "CERTIFICATE"}, + {CERTIFICATE_REQUEST, "CERTIFICATE_REQUEST"}, + {AUTHENTICATION, "AUTHENTICATION"}, + {NONCE, "NONCE"}, + {NOTIFY, "NOTIFY"}, + {DELETE, "DELETE"}, + {VENDOR_ID, "VENDOR_ID"}, + {TRAFFIC_SELECTOR_INITIATOR, "TRAFFIC_SELECTOR_INITIATOR"}, + {TRAFFIC_SELECTOR_RESPONDER, "TRAFFIC_SELECTOR_RESPONDER"}, + {ENCRYPTED, "ENCRYPTED"}, + {CONFIGURATION, "CONFIGURATION"}, + {EXTENSIBLE_AUTHENTICATION, "EXTENSIBLE_AUTHENTICATION"}, + {HEADER, "HEADER"}, + {PROPOSAL_SUBSTRUCTURE, "PROPOSAL_SUBSTRUCTURE"}, + {TRANSFORM_SUBSTRUCTURE, "TRANSFORM_SUBSTRUCTURE"}, + {TRANSFORM_ATTRIBUTE, "TRANSFORM_ATTRIBUTE"}, + {TRAFFIC_SELECTOR_SUBSTRUCTURE, "TRAFFIC_SELECTOR_SUBSTRUCTURE"}, + {CONFIGURATION_ATTRIBUTE,"CONFIGURATION_ATTRIBUTE"}, + {UNKNOWN_PAYLOAD,"UNKNOWN_PAYLOAD"}, + {MAPPING_END, NULL} +}; + +/* + * 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(); + 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/charon/encoding/payloads/payload.h b/src/charon/charon/encoding/payloads/payload.h new file mode 100644 index 000000000..fc3457832 --- /dev/null +++ b/src/charon/charon/encoding/payloads/payload.h @@ -0,0 +1,279 @@ +/** + * @file payload.h + * + * @brief Interface payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PAYLOAD_H_ +#define PAYLOAD_H_ + +#include +#include +#include + + +typedef enum payload_type_t payload_type_t; + +/** + * @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, +}; + + +/** + * String mappings for payload_type_t. + */ +extern mapping_t payload_type_m[]; + + +typedef struct payload_t payload_t; + +/** + * @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 instanciate. + * + * @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/charon/encoding/payloads/proposal_substructure.c b/src/charon/charon/encoding/payloads/proposal_substructure.c new file mode 100644 index 000000000..cb3c695b2 --- /dev/null +++ b/src/charon/charon/encoding/payloads/proposal_substructure.c @@ -0,0 +1,629 @@ +/** + * @file proposal_substructure.h + * + * @brief Implementation of proposal_substructure_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_substructure.h" + +#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; + + /** + * @brief Computes the length of this substructure. + * + * @param this calling private_proposal_substructure_t object + */ + void (*compute_length) (private_proposal_substructure_t *this); +}; + +/** + * 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; + + if ((this->next_payload != NO_PAYLOAD) && (this->next_payload != 2)) + { + /* must be 0 or 2 */ + return FAILED; + } + if (this->transforms_count != this->transforms->get_count(this->transforms)) + { + /* must be the same! */ + return FAILED; + } + + if ((this->protocol_id == 0) || (this->protocol_id >= 4)) + { + /* reserved are not supported */ + return FAILED; + } + + iterator = this->transforms->create_iterator(this->transforms,TRUE); + + while(iterator->has_next(iterator)) + { + payload_t *current_transform; + iterator->current(iterator,(void **)¤t_transform); + + status = current_transform->verify(current_transform); + if (status != SUCCESS) + { + 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) +{ +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_proposal_substructure_t *this) +{ + 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); + this->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; + this->compute_length(this); + } + + this->spi.ptr = clalloc(spi.ptr,spi.len); + this->spi.len = spi.len; + this->spi_size = spi.len; + this->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_info_for_transform_type. + */ +static status_t get_info_for_transform_type (private_proposal_substructure_t *this,transform_type_t type, u_int16_t *transform_id, u_int16_t *key_length) +{ + iterator_t *iterator; + status_t status; + u_int16_t found_transform_id; + u_int16_t found_key_length; + + iterator = this->transforms->create_iterator(this->transforms,TRUE); + + while (iterator->has_next(iterator)) + { + transform_substructure_t *current_transform; + status = iterator->current(iterator,(void **) ¤t_transform); + if (status != SUCCESS) + { + break; + } + if (current_transform->get_transform_type(current_transform) == type) + { + /* now get data for specific type */ + found_transform_id = current_transform->get_transform_id(current_transform); + status = current_transform->get_key_length(current_transform,&found_key_length); + *transform_id = found_transform_id; + *key_length = found_key_length; + iterator->destroy(iterator); + return status; + } + } + iterator->destroy(iterator); + return NOT_FOUND; +} + +/** + * Implementation of private_proposal_substructure_t.compute_length. + */ +static void compute_length (private_proposal_substructure_t *this) +{ + iterator_t *iterator; + size_t transforms_count = 0; + size_t length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH; + iterator = this->transforms->create_iterator(this->transforms,TRUE); + while (iterator->has_next(iterator)) + { + payload_t * current_transform; + iterator->current(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 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.add_to_proposal. + */ +void add_to_proposal(private_proposal_substructure_t *this, proposal_t *proposal) +{ + iterator_t *iterator = this->transforms->create_iterator(this->transforms, TRUE); + u_int32_t spi; + + + while (iterator->has_next(iterator)) + { + transform_substructure_t *transform; + transform_type_t transform_type; + u_int16_t transform_id; + u_int16_t key_length = 0; + + iterator->current(iterator, (void**)&transform); + + 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, this->protocol_id, transform_type, transform_id, key_length); + } + iterator->destroy(iterator); + + spi = *((u_int32_t*)this->spi.ptr); + + proposal->set_spi(proposal, this->protocol_id, spi); +} + +/** + * Implementation of proposal_substructure_t.clone. + */ +static private_proposal_substructure_t* clone(private_proposal_substructure_t *this) +{ + private_proposal_substructure_t * new_clone; + iterator_t *transforms; + + new_clone = (private_proposal_substructure_t *) proposal_substructure_create(); + + new_clone->next_payload = this->next_payload; + new_clone->proposal_number = this->proposal_number; + new_clone->protocol_id = this->protocol_id; + new_clone->spi_size = this->spi_size; + if (this->spi.ptr != NULL) + { + new_clone->spi.ptr = clalloc(this->spi.ptr,this->spi.len); + new_clone->spi.len = this->spi.len; + } + + transforms = this->transforms->create_iterator(this->transforms,FALSE); + + while (transforms->has_next(transforms)) + { + transform_substructure_t *current_transform; + transform_substructure_t *current_transform_clone; + + transforms->current(transforms,(void **) ¤t_transform); + + current_transform_clone = current_transform->clone(current_transform); + + new_clone->public.add_transform_substructure(&(new_clone->public),current_transform_clone); + } + + transforms->destroy(transforms); + + return new_clone; +} + +/** + * Implements payload_t's and proposal_substructure_t's destroy function. + * See #payload_s.destroy or proposal_substructure_s.destroy for description. + */ +static status_t destroy(private_proposal_substructure_t *this) +{ + /* all proposals are getting destroyed */ + while (this->transforms->get_count(this->transforms) > 0) + { + transform_substructure_t *current_transform; + if (this->transforms->remove_last(this->transforms,(void **)¤t_transform) != SUCCESS) + { + break; + } + current_transform->destroy(current_transform); + } + this->transforms->destroy(this->transforms); + + if (this->spi.ptr != NULL) + { + free(this->spi.ptr); + } + + free(this); + + return SUCCESS; +} + +/* + * 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.get_info_for_transform_type = (status_t (*) (proposal_substructure_t *,transform_type_t,u_int16_t *, u_int16_t *))get_info_for_transform_type; + this->public.set_is_last_proposal = (void (*) (proposal_substructure_t *,bool)) set_is_last_proposal; + this->public.add_to_proposal = (void (*) (proposal_substructure_t*,proposal_t*))add_to_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; + + /* private functions */ + this->compute_length = compute_length; + + /* 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, protocol_id_t proto) +{ + 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, proto, ENCRYPTION_ALGORITHM); + while (iterator->has_next(iterator)) + { + iterator->current(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, proto, INTEGRITY_ALGORITHM); + while (iterator->has_next(iterator)) + { + algorithm_t *algo; + iterator->current(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, proto, PSEUDO_RANDOM_FUNCTION); + while (iterator->has_next(iterator)) + { + algorithm_t *algo; + iterator->current(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, proto, DIFFIE_HELLMAN_GROUP); + while (iterator->has_next(iterator)) + { + algorithm_t *algo; + iterator->current(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, proto, EXTENDED_SEQUENCE_NUMBERS); + while (iterator->has_next(iterator)) + { + algorithm_t *algo; + iterator->current(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); + + /* take over general infos */ + this->spi_size = proto == PROTO_IKE ? 8 : 4; + this->spi.len = this->spi_size; + this->spi.ptr = malloc(this->spi_size); + *((u_int32_t*)this->spi.ptr) = proposal->get_spi(proposal, proto); + this->proposal_number = proposal->get_number(proposal); + this->protocol_id = proto; + + return &(this->public); +} diff --git a/src/charon/charon/encoding/payloads/proposal_substructure.h b/src/charon/charon/encoding/payloads/proposal_substructure.h new file mode 100644 index 000000000..506d25800 --- /dev/null +++ b/src/charon/charon/encoding/payloads/proposal_substructure.h @@ -0,0 +1,231 @@ +/** + * @file proposal_substructure.h + * + * @brief Interface of proposal_substructure_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SUBSTRUCTURE_H_ +#define PROPOSAL_SUBSTRUCTURE_H_ + +#include +#include +#include +#include +#include + + +/** + * Length of the proposal substructure header (without spi). + * + * @ingroup payloads + */ +#define PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH 8 + + +typedef struct proposal_substructure_t proposal_substructure_t; + +/** + * @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 Get informations for a specific transform type. + * + * @param this calling proposal_substructure_t object + * @param type type to get informations for + * @param transform_id transform id of the specific type + * @param key_length key length of the specific key length transform attribute + * @return + * - SUCCESS if transform type is part of this proposal and + * all data (incl. key length) could be fetched + * - NOT_FOUND if transform type is not part of this proposal + */ + status_t (*get_info_for_transform_type) (proposal_substructure_t *this,transform_type_t type, u_int16_t *transform_id, u_int16_t *key_length); + + /** + * @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 Add this proposal_substructure to a proposal. + * + * Since a proposal_t may contain the data of multiple + * proposal_sbustructure_t's, it may be necessary to call + * the function multiple times with the same proposal. + * + * @param this calling proposal_substructure_t object + * @param proposal proposal where the data should be added + */ + void (*add_to_proposal) (proposal_substructure_t *this, proposal_t *proposal); + + /** + * @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(); + +/** + * @brief Creates a proposal substructure from a proposal. + * + * Since a child proposal may contain data for both AH and ESP, + * the protocol must be specified. If the proposal does not contain + * data for proto, NULL is returned. Call twice, once with AH, once + * with ESP, with the same proposal to build the two substructures + * for it. + * + * @param proposal proposal to build a substruct out of it + * @param proto for which protocol the substructure should be built + * @return proposal_substructure_t object, or NULL + * + * @ingroup payloads + */ +proposal_substructure_t *proposal_substructure_create_from_proposal(proposal_t *proposal, protocol_id_t proto); + + +#endif /*PROPOSAL_SUBSTRUCTURE_H_*/ diff --git a/src/charon/charon/encoding/payloads/sa_payload.c b/src/charon/charon/encoding/payloads/sa_payload.c new file mode 100644 index 000000000..81b4e6709 --- /dev/null +++ b/src/charon/charon/encoding/payloads/sa_payload.c @@ -0,0 +1,390 @@ +/** + * @file sa_payload.c + * + * @brief Implementation of sa_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "sa_payload.h" + +#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; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_sa_payload_t object + */ + void (*compute_length) (private_sa_payload_t *this); +}; + +/** + * 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 proposal_number = 1; + status_t status = SUCCESS; + iterator_t *iterator; + bool first = TRUE; + + /* check proposal numbering */ + iterator = this->proposals->create_iterator(this->proposals,TRUE); + + while(iterator->has_next(iterator)) + { + proposal_substructure_t *current_proposal; + iterator->current(iterator,(void **)¤t_proposal); + if (current_proposal->get_proposal_number(current_proposal) > proposal_number) + { + if (first) + { + /* first number must be 1 */ + status = FAILED; + break; + } + + if (current_proposal->get_proposal_number(current_proposal) != (proposal_number + 1)) + { + /* must be only one more then previous proposal */ + status = FAILED; + break; + } + } + else if (current_proposal->get_proposal_number(current_proposal) < proposal_number) + { + /* must not be smaller then proceeding one */ + status = FAILED; + break; + } + + status = current_proposal->payload_interface.verify(&(current_proposal->payload_interface)); + if (status != SUCCESS) + { + break; + } + first = FALSE; + } + + iterator->destroy(iterator); + return status; +} + + +/** + * Implementation of payload_t.destroy and sa_payload_t.destroy. + */ +static status_t destroy(private_sa_payload_t *this) +{ + /* all proposals are getting destroyed */ + while (this->proposals->get_count(this->proposals) > 0) + { + proposal_substructure_t *current_proposal; + this->proposals->remove_last(this->proposals,(void **)¤t_proposal); + current_proposal->destroy(current_proposal); + } + this->proposals->destroy(this->proposals); + + 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; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_sa_payload_t *this) +{ + 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; + if (this->proposals->get_count(this->proposals) > 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); + + this->proposals->insert_last(this->proposals,(void *) proposal); + this->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; + protocol_id_t proto[2]; + u_int i; + + /* build the substructures for every protocol */ + proposal->get_protocols(proposal, proto); + for (i = 0; i<2; i++) + { + if (proto[i] != PROTO_NONE) + { + substructure = proposal_substructure_create_from_proposal(proposal, proto[i]); + add_proposal_substructure(this, substructure); + } + } +} + +/** + * Implementation of sa_payload_t.get_proposals. + */ +static linked_list_t *get_proposals(private_sa_payload_t *this) +{ + int proposal_struct_number = 0; + iterator_t *iterator; + proposal_t *proposal; + linked_list_t *proposal_list; + + /* this list will hold our proposals */ + proposal_list = linked_list_create(); + + /* iterate over structures, one OR MORE structures will result in a proposal */ + iterator = this->proposals->create_iterator(this->proposals,TRUE); + while (iterator->has_next(iterator)) + { + proposal_substructure_t *proposal_struct; + iterator->current(iterator,(void **)&(proposal_struct)); + + if (proposal_struct->get_proposal_number(proposal_struct) > proposal_struct_number) + { + /* here starts a new proposal, create a new one and add it to the list */ + proposal_struct_number = proposal_struct->get_proposal_number(proposal_struct); + proposal = proposal_create(proposal_struct_number); + proposal_list->insert_last(proposal_list, proposal); + } + /* proposal_substructure_t does the dirty work and builds up the proposal */ + proposal_struct->add_to_proposal(proposal_struct, proposal); + } + iterator->destroy(iterator); + return proposal_list; +} + +/** + * Implementation of private_sa_payload_t.compute_length. + */ +static void compute_length (private_sa_payload_t *this) +{ + iterator_t *iterator; + size_t length = SA_PAYLOAD_HEADER_LENGTH; + iterator = this->proposals->create_iterator(this->proposals,TRUE); + while (iterator->has_next(iterator)) + { + payload_t *current_proposal; + iterator->current(iterator,(void **) ¤t_proposal); + length += current_proposal->get_length(current_proposal); + } + iterator->destroy(iterator); + + this->payload_length = length; +} + +/* + * 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.get_proposals = (linked_list_t* (*) (sa_payload_t *)) get_proposals; + this->public.destroy = (void (*) (sa_payload_t *)) destroy; + + /* private functions */ + this->compute_length = compute_length; + + /* 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->has_next(iterator)) + { + iterator->current(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/charon/encoding/payloads/sa_payload.h b/src/charon/charon/encoding/payloads/sa_payload.h new file mode 100644 index 000000000..45095c030 --- /dev/null +++ b/src/charon/charon/encoding/payloads/sa_payload.h @@ -0,0 +1,140 @@ +/** + * @file sa_payload.h + * + * @brief Interface of sa_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SA_PAYLOAD_H_ +#define SA_PAYLOAD_H_ + +#include +#include +#include +#include + +/** + * SA_PAYLOAD length in bytes without any proposal substructure. + * + * @ingroup payloads + */ +#define SA_PAYLOAD_HEADER_LENGTH 4 + +typedef struct sa_payload_t sa_payload_t; + +/** + * @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(); + +/** + * @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/charon/encoding/payloads/traffic_selector_substructure.c b/src/charon/charon/encoding/payloads/traffic_selector_substructure.c new file mode 100644 index 000000000..c1a461e8a --- /dev/null +++ b/src/charon/charon/encoding/payloads/traffic_selector_substructure.c @@ -0,0 +1,374 @@ +/** + * @file traffic_selector_substructure.c + * + * @brief Interface of traffic_selector_substructure_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "traffic_selector_substructure.h" + +#include +#include + +/** + * String mappings for ts_type_t. + */ +mapping_t ts_type_m[] = { + {TS_IPV4_ADDR_RANGE, "TS_IPV4_ADDR_RANGE"}, + {TS_IPV6_ADDR_RANGE, "TS_IPV6_ADDR_RANGE"}, + {MAPPING_END, NULL} +}; + + +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; + + /** + * update length + */ + void (*compute_length) (private_traffic_selector_substructure_t *this); +}; + +/** + * 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: + 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_ts_type. + */ +static ts_type_t get_ts_type (private_traffic_selector_substructure_t *this) +{ + return this->ts_type; +} + +/** + * Implementation of traffic_selector_substructure_t.set_ts_type. + */ +static void set_ts_type (private_traffic_selector_substructure_t *this,ts_type_t ts_type) +{ + this->ts_type = ts_type; +} + +/** + * Implementation of traffic_selector_substructure_t.get_protocol_id. + */ +static u_int8_t get_protocol_id (private_traffic_selector_substructure_t *this) +{ + return this->ip_protocol_id; +} + +/** + * Implementation of traffic_selector_substructure_t.set_protocol_id. + */ +static void set_protocol_id (private_traffic_selector_substructure_t *this,u_int8_t protocol_id) +{ + this->ip_protocol_id = protocol_id; +} + +/** + * Implementation of traffic_selector_substructure_t.get_start_host. + */ +static host_t * get_start_host (private_traffic_selector_substructure_t *this) +{ + return (host_create_from_chunk(AF_INET,this->starting_address, this->start_port)); +} + +/** + * Implementation of traffic_selector_substructure_t.set_start_host. + */ +static void set_start_host (private_traffic_selector_substructure_t *this,host_t *start_host) +{ + this->start_port = start_host->get_port(start_host); + if (this->starting_address.ptr != NULL) + { + chunk_free(&(this->starting_address)); + } + this->starting_address = start_host->get_address_as_chunk(start_host); + this->compute_length(this); +} + +/** + * Implementation of traffic_selector_substructure_t.get_end_host. + */ +static host_t *get_end_host (private_traffic_selector_substructure_t *this) +{ + return (host_create_from_chunk(AF_INET,this->ending_address, this->end_port)); +} + +/** + * Implementation of traffic_selector_substructure_t.set_end_host. + */ +static void set_end_host (private_traffic_selector_substructure_t *this,host_t *end_host) +{ + this->end_port = end_host->get_port(end_host); + if (this->ending_address.ptr != NULL) + { + chunk_free(&(this->ending_address)); + } + this->ending_address = end_host->get_address_as_chunk(end_host); + this->compute_length(this); +} + +/** + * 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; +} + +/** + * Implementation of private_ts_payload_t.compute_length + */ +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.destroy = (void (*) (traffic_selector_substructure_t *)) destroy; + this->public.get_ts_type = (ts_type_t (*) (traffic_selector_substructure_t *)) get_ts_type; + this->public.set_ts_type = (void (*) (traffic_selector_substructure_t *,ts_type_t)) set_ts_type; + this->public.get_protocol_id = (u_int8_t (*) (traffic_selector_substructure_t *)) get_protocol_id; + this->public.set_protocol_id = (void (*) (traffic_selector_substructure_t *,u_int8_t)) set_protocol_id; + this->public.get_start_host = (host_t * (*) (traffic_selector_substructure_t *))get_start_host; + this->public.set_start_host = (void (*) (traffic_selector_substructure_t *, host_t *))set_start_host; + this->public.get_end_host = (host_t * (*) (traffic_selector_substructure_t *))get_end_host; + this->public.set_end_host = (void (*) (traffic_selector_substructure_t *, host_t *))set_end_host; + this->public.get_traffic_selector = (traffic_selector_t* (*)(traffic_selector_substructure_t*))get_traffic_selector; + + /* private functions */ + this->compute_length = compute_length; + + /* private variables */ + this->payload_length = TRAFFIC_SELECTOR_HEADER_LENGTH; + this->start_port = 0; + this->end_port = 0; + this->starting_address = CHUNK_INITIALIZER; + this->ending_address = CHUNK_INITIALIZER; + 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); + + this->compute_length(this); + + return &(this->public); +} diff --git a/src/charon/charon/encoding/payloads/traffic_selector_substructure.h b/src/charon/charon/encoding/payloads/traffic_selector_substructure.h new file mode 100644 index 000000000..755917055 --- /dev/null +++ b/src/charon/charon/encoding/payloads/traffic_selector_substructure.h @@ -0,0 +1,171 @@ +/** + * @file traffic_selector_substructure.h + * + * @brief Interface of traffic_selector_substructure_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ +#define TRAFFIC_SELECTOR_SUBSTRUCTURE_H_ + +#include +#include +#include +#include + +/** + * Length of a TRAFFIC SELECTOR SUBSTRUCTURE without start and end address. + * + * @ingroup payloads + */ +#define TRAFFIC_SELECTOR_HEADER_LENGTH 8 + +typedef struct traffic_selector_substructure_t traffic_selector_substructure_t; + +/** + * @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(); + +/** + * @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/charon/encoding/payloads/transform_attribute.c b/src/charon/charon/encoding/payloads/transform_attribute.c new file mode 100644 index 000000000..71cdd59e2 --- /dev/null +++ b/src/charon/charon/encoding/payloads/transform_attribute.c @@ -0,0 +1,333 @@ +/** + * @file transform_attribute.c + * + * @brief Implementation of transform_attribute_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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; +}; + +/** + * String mappings for transform_attribute_type_t. + */ +mapping_t transform_attribute_type_m[] = { + {ATTRIBUTE_UNDEFINED, "ATTRIBUTE_UNDEFINED"}, + {KEY_LENGTH, "KEY_LENGTH"}, + {MAPPING_END, NULL} +}; + +/** + * 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/charon/encoding/payloads/transform_attribute.h b/src/charon/charon/encoding/payloads/transform_attribute.h new file mode 100644 index 000000000..547699915 --- /dev/null +++ b/src/charon/charon/encoding/payloads/transform_attribute.h @@ -0,0 +1,154 @@ +/** + * @file transform_attribute.h + * + * @brief Interface of transform_attribute_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TRANSFORM_ATTRIBUTE_H_ +#define TRANSFORM_ATTRIBUTE_H_ + +#include +#include + + +typedef enum transform_attribute_type_t transform_attribute_type_t; + +/** + * Type of the attribute, as in IKEv2 RFC 3.3.5. + * + * @ingroup payloads + */ +enum transform_attribute_type_t { + ATTRIBUTE_UNDEFINED = 16384, + KEY_LENGTH = 14 +}; + +/** + * String mappings for transform_attribute_type_t. + * + * @ingroup payloads + */ +extern mapping_t transform_attribute_type_m[]; + +typedef struct transform_attribute_t transform_attribute_t; + +/** + * @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(); + +/** + * @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/charon/encoding/payloads/transform_substructure.c b/src/charon/charon/encoding/payloads/transform_substructure.c new file mode 100644 index 000000000..350ad63e4 --- /dev/null +++ b/src/charon/charon/encoding/payloads/transform_substructure.c @@ -0,0 +1,485 @@ +/** + * @file transform_substructure.h + * + * @brief Implementation of transform_substructure_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "transform_substructure.h" + +#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; + + /** + * @brief Computes the length of this substructure. + * + * @param this calling private_transform_substructure_t object + */ + void (*compute_length) (private_transform_substructure_t *this); +}; + + +/** + * 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; + + if ((this->next_payload != NO_PAYLOAD) && (this->next_payload != 3)) + { + /* must be 0 or 3 */ + return FAILED; + } + + switch (this->transform_type) + { + case ENCRYPTION_ALGORITHM: + { + if ((this->transform_id < ENCR_DES_IV64) || (this->transform_id > ENCR_AES_CTR)) + { + return FAILED; + } + break; + } + case PSEUDO_RANDOM_FUNCTION: + { + if ((this->transform_id < PRF_HMAC_MD5) || (this->transform_id > PRF_AES128_CBC)) + { + return FAILED; + } + break; + } + case INTEGRITY_ALGORITHM: + { + if ((this->transform_id < AUTH_HMAC_MD5_96) || (this->transform_id > AUTH_AES_XCBC_96)) + { + return FAILED; + } + break; + } + case DIFFIE_HELLMAN_GROUP: + { + switch (this->transform_id) + { + 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: + { + return FAILED; + } + } + + + break; + } + case EXTENDED_SEQUENCE_NUMBERS: + { + if ((this->transform_id != NO_EXT_SEQ_NUMBERS) && (this->transform_id != EXT_SEQ_NUMBERS)) + { + return FAILED; + } + break; + } + default: + { + /* not a supported transform type! */ + return FAILED; + } + } + iterator = this->attributes->create_iterator(this->attributes,TRUE); + + while(iterator->has_next(iterator)) + { + payload_t *current_attributes; + iterator->current(iterator,(void **)¤t_attributes); + + status = current_attributes->verify(current_attributes); + if (status != SUCCESS) + { + 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_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); +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_transform_substructure_t *this) +{ + 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); + this->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 private_transform_substructure_t.compute_length. + */ +static void compute_length (private_transform_substructure_t *this) +{ + iterator_t *iterator; + size_t length = TRANSFORM_SUBSTRUCTURE_HEADER_LENGTH; + iterator = this->attributes->create_iterator(this->attributes,TRUE); + while (iterator->has_next(iterator)) + { + payload_t * current_attribute; + iterator->current(iterator,(void **) ¤t_attribute); + length += current_attribute->get_length(current_attribute); + } + iterator->destroy(iterator); + + this->transform_length = length; +} + +/** + * Implementation of transform_substructure_t.clone. + */ +static transform_substructure_t *clone(private_transform_substructure_t *this) +{ + private_transform_substructure_t *new_clone; + iterator_t *attributes; + + new_clone = (private_transform_substructure_t *) transform_substructure_create(); + + new_clone->next_payload = this->next_payload; + new_clone->transform_type = this->transform_type; + new_clone->transform_id = this->transform_id; + + attributes = this->attributes->create_iterator(this->attributes,FALSE); + + while (attributes->has_next(attributes)) + { + transform_attribute_t *current_attribute; + transform_attribute_t *current_attribute_clone; + attributes->current(attributes,(void **) ¤t_attribute); + + current_attribute_clone = current_attribute->clone(current_attribute); + + new_clone->public.add_transform_attribute(&(new_clone->public),current_attribute_clone); + } + + attributes->destroy(attributes); + + return &(new_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; + + attributes = this->attributes->create_iterator(this->attributes,TRUE); + + while (attributes->has_next(attributes)) + { + transform_attribute_t *current_attribute; + attributes->current(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) +{ + /* all proposals are getting destroyed */ + while (this->attributes->get_count(this->attributes) > 0) + { + transform_attribute_t *current_attribute; + this->attributes->remove_last(this->attributes,(void **)¤t_attribute); + current_attribute->destroy(current_attribute); + } + this->attributes->destroy(this->attributes); + + 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; + + /* private functions */ + this->compute_length = compute_length; + + /* 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 AES encryption */ + if (transform_type == ENCRYPTION_ALGORITHM && + transform_id == ENCR_AES_CBC) + { + transform_attribute_t *attribute = transform_attribute_create_key_length(key_length); + transform->add_transform_attribute(transform,attribute); + } + + return transform; +} diff --git a/src/charon/charon/encoding/payloads/transform_substructure.h b/src/charon/charon/encoding/payloads/transform_substructure.h new file mode 100644 index 000000000..f6af3ee59 --- /dev/null +++ b/src/charon/charon/encoding/payloads/transform_substructure.h @@ -0,0 +1,198 @@ +/** + * @file transform_substructure.h + * + * @brief Interface of transform_substructure_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TRANSFORM_SUBSTRUCTURE_H_ +#define TRANSFORM_SUBSTRUCTURE_H_ + +#include +#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 + + +typedef struct transform_substructure_t transform_substructure_t; + +/** + * @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(); + +/** + * @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/charon/encoding/payloads/ts_payload.c b/src/charon/charon/encoding/payloads/ts_payload.c new file mode 100644 index 000000000..58772e666 --- /dev/null +++ b/src/charon/charon/encoding/payloads/ts_payload.c @@ -0,0 +1,365 @@ +/** + * @file ts_payload.c + * + * @brief Implementation of ts_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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; + + /** + * @brief Computes the length of this payload. + * + * @param this calling private_ts_payload_t object + */ + void (*compute_length) (private_ts_payload_t *this); +}; + +/** + * 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; + 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->has_next(iterator)) + { + payload_t *current_traffic_selector; + iterator->current(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; +} + +/** + * Implementation of payload_t.get_length. + */ +static size_t get_length(private_ts_payload_t *this) +{ + 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; + linked_list_t *ts_list = linked_list_create(); + + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors, TRUE); + while (iterator->has_next(iterator)) + { + traffic_selector_substructure_t *ts_substructure; + iterator->current(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 private_ts_payload_t.compute_length. + */ +static void compute_length (private_ts_payload_t *this) +{ + iterator_t *iterator; + size_t ts_count = 0; + size_t length = TS_PAYLOAD_HEADER_LENGTH; + iterator = this->traffic_selectors->create_iterator(this->traffic_selectors,TRUE); + while (iterator->has_next(iterator)) + { + payload_t * current_traffic_selector; + iterator->current(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.destroy and ts_payload_t.destroy. + */ +static void destroy(private_ts_payload_t *this) +{ + while (this->traffic_selectors->get_count(this->traffic_selectors) > 0) + { + payload_t *current_traffic_selector; + + this->traffic_selectors->remove_last(this->traffic_selectors,(void **) ¤t_traffic_selector); + + current_traffic_selector->destroy(current_traffic_selector); + } + + this->traffic_selectors->destroy(this->traffic_selectors); + + 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 functions */ + this->compute_length = compute_length; + + /* 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->has_next(iterator)) + { + iterator->current(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/charon/encoding/payloads/ts_payload.h b/src/charon/charon/encoding/payloads/ts_payload.h new file mode 100644 index 000000000..775ff6134 --- /dev/null +++ b/src/charon/charon/encoding/payloads/ts_payload.h @@ -0,0 +1,152 @@ +/** + * @file ts_payload.h + * + * @brief Interface of ts_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TS_PAYLOAD_H_ +#define TS_PAYLOAD_H_ + +#include +#include +#include +#include +#include + +/** + * Length of a TS payload without the Traffic selectors. + * + * @ingroup payloads + */ +#define TS_PAYLOAD_HEADER_LENGTH 8 + + +typedef struct ts_payload_t ts_payload_t; + +/** + * @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/charon/encoding/payloads/unknown_payload.c b/src/charon/charon/encoding/payloads/unknown_payload.c new file mode 100644 index 000000000..25bb37d59 --- /dev/null +++ b/src/charon/charon/encoding/payloads/unknown_payload.c @@ -0,0 +1,207 @@ +/** + * @file unknown_payload.c + * + * @brief Implementation of unknown_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/unknown_payload.h b/src/charon/charon/encoding/payloads/unknown_payload.h new file mode 100644 index 000000000..9c4926ea7 --- /dev/null +++ b/src/charon/charon/encoding/payloads/unknown_payload.h @@ -0,0 +1,95 @@ +/** + * @file unknown_payload.h + * + * @brief Interface of unknown_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 UNKNOWN_PAYLOAD_H_ +#define UNKNOWN_PAYLOAD_H_ + +#include +#include + +/** + * Header length of the unknown payload. + * + * @ingroup payloads + */ +#define UNKNOWN_PAYLOAD_HEADER_LENGTH 4 + + +typedef struct unknown_payload_t unknown_payload_t; + +/** + * @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(); + + +#endif /* UNKNOWN_PAYLOAD_H_ */ diff --git a/src/charon/charon/encoding/payloads/vendor_id_payload.c b/src/charon/charon/encoding/payloads/vendor_id_payload.c new file mode 100644 index 000000000..436b82d79 --- /dev/null +++ b/src/charon/charon/encoding/payloads/vendor_id_payload.c @@ -0,0 +1,227 @@ +/** + * @file vendor_id_payload.c + * + * @brief Implementation of vendor_id_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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_INITIALIZER; + + return (&(this->public)); +} diff --git a/src/charon/charon/encoding/payloads/vendor_id_payload.h b/src/charon/charon/encoding/payloads/vendor_id_payload.h new file mode 100644 index 000000000..c9ead4337 --- /dev/null +++ b/src/charon/charon/encoding/payloads/vendor_id_payload.h @@ -0,0 +1,103 @@ +/** + * @file vendor_id_payload.h + * + * @brief Interface of vendor_id_payload_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 VENDOR_ID_PAYLOAD_H_ +#define VENDOR_ID_PAYLOAD_H_ + +#include +#include + +/** + * Length of a VENDOR ID payload without the VID data in bytes. + * + * @ingroup payloads + */ +#define VENDOR_ID_PAYLOAD_HEADER_LENGTH 4 + + +typedef struct vendor_id_payload_t vendor_id_payload_t; + +/** + * @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(); + + +#endif /* VENDOR_ID_PAYLOAD_H_ */ diff --git a/src/charon/charon/network/Makefile.network b/src/charon/charon/network/Makefile.network new file mode 100644 index 000000000..fd99bd085 --- /dev/null +++ b/src/charon/charon/network/Makefile.network @@ -0,0 +1,24 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +NETWORK_DIR= $(CHARON_DIR)network/ + + +CHARON_OBJS+= $(BUILD_DIR)packet.o +$(BUILD_DIR)packet.o : $(NETWORK_DIR)packet.c $(NETWORK_DIR)packet.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)socket.o +$(BUILD_DIR)socket.o : $(NETWORK_DIR)socket.c $(NETWORK_DIR)socket.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/charon/network/packet.c b/src/charon/charon/network/packet.c new file mode 100644 index 000000000..6cded72a3 --- /dev/null +++ b/src/charon/charon/network/packet.c @@ -0,0 +1,189 @@ +/** + * @file packet.c + * + * @brief Implementation of packet_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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) +{ + if (this->source) + { + this->source->destroy(this->source); + } + this->source = source; +} + +/** + * Implements packet_t.set_destination + */ +static void set_destination(private_packet_t *this, host_t *destination) +{ + if (this->destination) + { + this->destination->destroy(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); + } + else + { + other->destination = NULL; + } + + if (this->source != NULL) + { + other->source = this->source->clone(this->source); + } + else + { + other->source = NULL; + } + + /* only clone existing chunks :-) */ + if (this->data.ptr != NULL) + { + other->data.ptr = clalloc(this->data.ptr,this->data.len); + other->data.len = this->data.len; + } + else + { + other->data = CHUNK_INITIALIZER; + } + return &(other->public); +} + + +/* + * Documented in header + */ +packet_t *packet_create() +{ + 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_INITIALIZER; + + return &(this->public); +} diff --git a/src/charon/charon/network/packet.h b/src/charon/charon/network/packet.h new file mode 100644 index 000000000..a2620d391 --- /dev/null +++ b/src/charon/charon/network/packet.h @@ -0,0 +1,135 @@ +/** + * @file packet.h + * + * @brief Interface of packet_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PACKET_H_ +#define PACKET_H_ + + +#include +#include + + +typedef struct packet_t packet_t; + +/** + * @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(); + + +#endif /*PACKET_H_*/ diff --git a/src/charon/charon/network/socket.c b/src/charon/charon/network/socket.c new file mode 100644 index 000000000..32ff84538 --- /dev/null +++ b/src/charon/charon/network/socket.c @@ -0,0 +1,457 @@ +/** + * @file socket.c + * + * @brief Implementation of socket_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 1998-2002 D. Hugh Redelmeier. + * Copyright (C) 1997 Angelos D. Keromytis. + * + * Some parts of interface lookup 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 "socket.h" + +#include +#include + + +#define IP_HEADER_LENGTH 20 +#define UDP_HEADER_LENGTH 8 + + +/** + * 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[] = +{ + /* Protocol must be UDP */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 9), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 7), + /* Destination Port must be 500 */ + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 22), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 500, 0, 5), + /* IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 45), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 3), + /* packet length is length in IKEv2 header + ip header + udp header */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 52), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, IP_HEADER_LENGTH + UDP_HEADER_LENGTH), + BPF_STMT(BPF_RET+BPF_A, 0), + /* packet doesn't match IKEv2, 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 +}; + + +typedef struct interface_t interface_t; + +/** + * An interface on which we listen. + */ +struct interface_t { + + /** + * Name of the interface + */ + char name[IFNAMSIZ]; + + /** + * Associated socket + */ + int socket_fd; + + /** + * Host with listening address + */ + host_t *address; +}; + +typedef struct private_socket_t private_socket_t; + +/** + * Private data of an socket_t object + */ +struct private_socket_t{ + /** + * public functions + */ + socket_t public; + + /** + * Master socket + */ + int master_fd; + + /** + * List of all socket to listen + */ + linked_list_t* interfaces; + + /** + * logger for this socket + */ + logger_t *logger; +}; + +/** + * 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 = packet_create(); + host_t *source, *dest; + int bytes_read = 0; + + + while (bytes_read >= 0) + { + int max_fd = 1; + fd_set readfds; + iterator_t *iterator; + int oldstate; + interface_t *interface; + + /* build fd_set */ + FD_ZERO(&readfds); + iterator = this->interfaces->create_iterator(this->interfaces, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&interface); + FD_SET(interface->socket_fd, &readfds); + if (interface->socket_fd > max_fd) + { + max_fd = interface->socket_fd + 1; + } + } + iterator->destroy(iterator); + + /* add packet destroy handler for cancellation, enable cancellation */ + pthread_cleanup_push((void(*)(void*))pkt->destroy, (void*)pkt); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + + this->logger->log(this->logger, CONTROL|LEVEL1, "waiting on sockets"); + bytes_read = select(max_fd, &readfds, NULL, NULL, NULL); + + /* reset cancellation, remove packet destroy handler (without executing) */ + pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); + + /* read on the first nonblocking socket */ + bytes_read = 0; + iterator = this->interfaces->create_iterator(this->interfaces, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&interface); + if (FD_ISSET(interface->socket_fd, &readfds)) + { + /* do the read */ + bytes_read = recv(interface->socket_fd, buffer, MAX_PACKET, 0); + break; + } + } + iterator->destroy(iterator); + + if (bytes_read < 0) + { + this->logger->log(this->logger, ERROR, "error reading from socket: %s", strerror(errno)); + continue; + } + if (bytes_read > IP_HEADER_LENGTH + UDP_HEADER_LENGTH) + { + /* read source/dest from raw IP/UDP header */ + chunk_t source_chunk = {buffer + 12, 4}; + chunk_t dest_chunk = {buffer + 16, 4}; + u_int16_t source_port = ntohs(*(u_int16_t*)(buffer + 20)); + u_int16_t dest_port = ntohs(*(u_int16_t*)(buffer + 22)); + source = host_create_from_chunk(AF_INET, source_chunk, source_port); + dest = host_create_from_chunk(AF_INET, dest_chunk, dest_port); + pkt->set_source(pkt, source); + pkt->set_destination(pkt, dest); + break; + } + this->logger->log(this->logger, ERROR|LEVEL1, "too short packet received"); + } + + this->logger->log(this->logger, CONTROL, "received packet: from %s:%d to %s:%d", + source->get_address(source), source->get_port(source), + dest->get_address(dest), dest->get_port(dest)); + + /* fill in packet */ + data.len = bytes_read - IP_HEADER_LENGTH - UDP_HEADER_LENGTH; + data.ptr = malloc(data.len); + memcpy(data.ptr, buffer + IP_HEADER_LENGTH + UDP_HEADER_LENGTH, data.len); + pkt->set_data(pkt, data); + + /* return packet */ + *packet = pkt; + + return SUCCESS; +} + +/** + * implementation of socket_t.send + */ +status_t sender(private_socket_t *this, packet_t *packet) +{ + ssize_t bytes_sent; + chunk_t data; + host_t *src, *dst; + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + data = packet->get_data(packet); + + this->logger->log(this->logger, CONTROL, "sending packet: from %s:%d to %s:%d", + src->get_address(src), src->get_port(src), + dst->get_address(dst), dst->get_port(dst)); + + /* send data */ + /* TODO: should we send via the interface we received the packet? */ + bytes_sent = sendto(this->master_fd, data.ptr, data.len, 0, + dst->get_sockaddr(dst), *(dst->get_sockaddr_len(dst))); + + if (bytes_sent != data.len) + { + this->logger->log(this->logger, ERROR, "error writing to socket: %s", strerror(errno)); + return FAILED; + } + return SUCCESS; +} + +/** + * Find all suitable interfaces, bind them and add them to the list + */ +static status_t build_interface_list(private_socket_t *this, u_int16_t port) +{ + int on = TRUE; + int i; + struct sockaddr_in addr; + struct ifconf ifconf; + struct ifreq buf[300]; + + /* master socket for querying socket for a specific interfaces */ + this->master_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->master_fd == -1) + { + this->logger->log(this->logger, ERROR, "could not open IPv4 master socket!"); + return FAILED; + } + + /* allow binding of multiplo sockets */ + if (setsockopt(this->master_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + { + this->logger->log(this->logger, ERROR, "unable to set SO_REUSEADDR on master socket!"); + return FAILED; + } + + /* bind the master socket */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + if (bind(this->master_fd,(struct sockaddr*)&addr, sizeof(addr)) < 0) + { + this->logger->log(this->logger, ERROR, "unable to bind master socket: %s!", strerror(errno)); + return FAILED; + } + + /* get all interfaces */ + ifconf.ifc_len = sizeof(buf); + ifconf.ifc_buf = (void*) buf; + memset(buf, 0, sizeof(buf)); + if (ioctl(this->master_fd, SIOCGIFCONF, &ifconf) == -1) + { + this->logger->log(this->logger, ERROR, "unable to get interfaces!"); + return FAILED; + } + + /* add every interesting interfaces to our interface list */ + for (i = 0; (i+1) * sizeof(*buf) <= (size_t)ifconf.ifc_len; i++) + { + struct sockaddr_in *current = (struct sockaddr_in*) &buf[i].ifr_addr; + struct ifreq auxinfo; + int skt; + interface_t *interface; + + if (current->sin_family != AF_INET) + { + /* ignore all but AF_INET interfaces */ + continue; + } + + /* get auxilary info about socket */ + memset(&auxinfo, 0, sizeof(auxinfo)); + memcpy(auxinfo.ifr_name, buf[i].ifr_name, IFNAMSIZ); + if (ioctl(this->master_fd, SIOCGIFFLAGS, &auxinfo) == -1) + { + this->logger->log(this->logger, ERROR, "unable to SIOCGIFFLAGS master socket!"); + continue; + } + if (!(auxinfo.ifr_flags & IFF_UP)) + { + /* ignore an interface that isn't up */ + continue; + } + if (current->sin_addr.s_addr == 0) + { + /* ignore unconfigured interfaces */ + continue; + } + + /* set up interface socket */ + skt = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); + if (socket < 0) + { + this->logger->log(this->logger, ERROR, "unable to open interface socket!"); + continue; + } + if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + { + this->logger->log(this->logger, ERROR, "unable to set SO_REUSEADDR on interface socket!"); + close(skt); + continue; + } + current->sin_port = htons(port); + current->sin_family = AF_INET; + if (bind(skt, (struct sockaddr*)current, sizeof(struct sockaddr_in)) < 0) + { + this->logger->log(this->logger, ERROR, "unable to bind interface socket!"); + close(skt); + continue; + } + + if (setsockopt(skt, SOL_SOCKET, SO_ATTACH_FILTER, &ikev2_filter, sizeof(ikev2_filter)) < 0) + { + this->logger->log(this->logger, ERROR, "unable to attack IKEv2 filter to interface socket!"); + close(skt); + continue; + } + + /* add socket with interface name to list */ + interface = malloc_thing(interface_t); + memcpy(interface->name, buf[i].ifr_name, IFNAMSIZ); + interface->name[IFNAMSIZ-1] = '\0'; + interface->socket_fd = skt; + interface->address = host_create_from_sockaddr((struct sockaddr*)current); + this->logger->log(this->logger, CONTROL, "listening on %s (%s)", + interface->name, interface->address->get_address(interface->address)); + this->interfaces->insert_last(this->interfaces, (void*)interface); + } + + if (this->interfaces->get_count(this->interfaces) == 0) + { + this->logger->log(this->logger, ERROR, "unable to find any usable interface!"); + return FAILED; + } + return SUCCESS; +} + +/** + * implementation of socket_t.is_listening_on + */ +static bool is_listening_on(private_socket_t *this, host_t *host) +{ + iterator_t *iterator; + + /* listening on 0.0.0.0 is always TRUE */ + if (host->is_default_route(host)) + { + return TRUE; + } + + /* compare host with all interfaces */ + iterator = this->interfaces->create_iterator(this->interfaces, TRUE); + while (iterator->has_next(iterator)) + { + interface_t *interface; + iterator->current(iterator, (void**)&interface); + if (host->equals(host, interface->address)) + { + iterator->destroy(iterator); + return TRUE; + } + } + iterator->destroy(iterator); + return FALSE; +} + +/** + * implementation of socket_t.destroy + */ +static void destroy(private_socket_t *this) +{ + interface_t *interface; + while (this->interfaces->remove_last(this->interfaces, (void**)&interface) == SUCCESS) + { + interface->address->destroy(interface->address); + close(interface->socket_fd); + free(interface); + } + this->interfaces->destroy(this->interfaces); + close(this->master_fd); + free(this); +} + +/* + * See header for description + */ +socket_t *socket_create(u_int16_t 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.is_listening_on = (bool (*)(socket_t*,host_t*))is_listening_on; + this->public.destroy = (void(*)(socket_t*)) destroy; + + this->logger = logger_manager->get_logger(logger_manager, SOCKET); + this->interfaces = linked_list_create(); + + if (build_interface_list(this, port) != SUCCESS) + { + this->interfaces->destroy(this->interfaces); + free(this); + charon->kill(charon, "could not bind any interface!"); + } + + return (socket_t*)this; +} diff --git a/src/charon/charon/network/socket.h b/src/charon/charon/network/socket.h new file mode 100644 index 000000000..498e7700a --- /dev/null +++ b/src/charon/charon/network/socket.h @@ -0,0 +1,128 @@ +/** + * @file socket.h + * + * @brief Interface for socket_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SOCKET_H_ +#define SOCKET_H_ + + +#include +#include + + +/** + * @brief Maximum size of a packet. + * + * 3000 Bytes should be sufficient, see IKEv2 RFC. + * + * @ingroup network + */ +#define MAX_PACKET 3000 + + +typedef struct socket_t socket_t; + +/** + * @brief Abstraction all sockets (currently IPv4 only). + * + * All available IPv4 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() + * + * @todo add IPv6 support + * + * @todo We currently use multiple sockets for historic reasons. With the + * new RAW socket mechanism, we could use just one socket and filter + * addresses in userspace (or via linux socket filter). This would allow + * realtime interface/address management in a easy way... + * + * @ingroup network + */ +struct socket_t { + /** + * @brief Receive a packet. + * + * Reads a packet from the socket and sets source/dest + * appropriately. + * + * @param sock 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 *sock, 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 sock 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 *sock, packet_t *packet); + + /** + * @brief Check if socket listens on an address. + * + * @param sock socket_t object to work on + * @param host address to check + * @return TRUE if listening on host, FALSE otherwise + */ + bool (*is_listening_on) (socket_t *sock, host_t *host); + + /** + * @brief Destroy sockets. + * + * close sockets and destroy socket_t object + * + * @param sock socket_t to destroy + */ + void (*destroy) (socket_t *sock); +}; + +/** + * @brief Create a socket_t, wich binds multiple sockets. + * + * currently creates one socket, listening on all addresses + * on "port". + * + * @param port port to bind socket to + * @return socket_t object + * + * @ingroup network + */ +socket_t *socket_create(u_int16_t port); + + +#endif /*SOCKET_H_*/ diff --git a/src/charon/charon/queues/Makefile.queues b/src/charon/charon/queues/Makefile.queues new file mode 100644 index 000000000..eeb012d2b --- /dev/null +++ b/src/charon/charon/queues/Makefile.queues @@ -0,0 +1,30 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +QUEUES_DIR= $(CHARON_DIR)queues/ + +CHARON_OBJS+= $(BUILD_DIR)event_queue.o +$(BUILD_DIR)event_queue.o : $(QUEUES_DIR)event_queue.c $(QUEUES_DIR)event_queue.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)job_queue.o +$(BUILD_DIR)job_queue.o : $(QUEUES_DIR)job_queue.c $(QUEUES_DIR)job_queue.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)send_queue.o +$(BUILD_DIR)send_queue.o : $(QUEUES_DIR)send_queue.c $(QUEUES_DIR)send_queue.h + $(CC) $(CFLAGS) -c -o $@ $< + + +include $(QUEUES_DIR)jobs/Makefile.jobs \ No newline at end of file diff --git a/src/charon/charon/queues/event_queue.c b/src/charon/charon/queues/event_queue.c new file mode 100644 index 000000000..ece9d1513 --- /dev/null +++ b/src/charon/charon/queues/event_queue.c @@ -0,0 +1,349 @@ +/** + * @file event_queue.c + * + * @brief Implementation of event_queue_t + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "event_queue.h" + +#include +#include + + + +typedef struct event_t event_t; + +/** + * @brief Represents an event as it is stored in the event queue. + * + * A event consists of a event time and an assigned job object. + * + */ +struct event_t{ + /** + * Time to fire the event. + */ + timeval_t time; + + /** + * Every event has its assigned job. + */ + job_t * job; + + /** + * @brief Destroys a event_t object. + * + * @param event_t calling object + */ + void (*destroy) (event_t *event); +}; + + +/** + * implements event_t.destroy + */ +static void event_destroy(event_t *event) +{ + free(event); +} + +/** + * @brief Creates a event for a specific time + * + * @param time absolute time to fire the event + * @param job job to add to job-queue at specific time + * + * @returns created event_t object + */ +static event_t *event_create(timeval_t time, job_t *job) +{ + event_t *this = malloc_thing(event_t); + + this->destroy = event_destroy; + this->time = time; + this->job = job; + + return this; +} + + +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 microseconds + * + * @param end_time end time + * @param start_time start time + * + * @warning this function is also defined in the tester class + * In later improvements, this function can be added to a general + * class type! + * + * @return difference in microseconds (end time - start time) + */ +static long time_difference(struct timeval *end_time, struct timeval *start_time) +{ + long seconds, microseconds; + + seconds = (end_time->tv_sec - start_time->tv_sec); + microseconds = (end_time->tv_usec - start_time->tv_usec); + return ((seconds * 1000000) + microseconds); +} + + +/** + * 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 (1) + { + 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; + + next_event->destroy(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_create(time,job); + event_t *current_event; + status_t status; + + pthread_mutex_lock(&(this->mutex)); + + /* while just used to break out */ + while(1) + { + 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_t * iterator; + + iterator = this->list->create_iterator(this->list,TRUE); + + iterator->has_next(iterator); + /* first element has not to be checked (already done) */ + + while(iterator->has_next(iterator)) + { + status = iterator->current(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; + int micros = ms * 1000; + + gettimeofday(¤t_time, NULL); + + time.tv_usec = ((current_time.tv_usec + micros) % 1000000); + time.tv_sec = current_time.tv_sec + ((current_time.tv_usec + micros)/ 1000000); + + this->add_absolute(this, job, time); +} + + +/** + * Implements event_queue_t.destroy. + */ +static void event_queue_destroy(private_event_queue_t *this) +{ + while (this->list->get_count(this->list) > 0) + { + event_t *event; + + if (this->list->remove_first(this->list,(void *) &event) != SUCCESS) + { + this->list->destroy(this->list); + break; + } + event->job->destroy_all(event->job); + event->destroy(event); + } + this->list->destroy(this->list); + + pthread_mutex_destroy(&(this->mutex)); + + pthread_cond_destroy(&(this->condvar)); + + 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/charon/queues/event_queue.h b/src/charon/charon/queues/event_queue.h new file mode 100644 index 000000000..a60424100 --- /dev/null +++ b/src/charon/charon/queues/event_queue.h @@ -0,0 +1,117 @@ +/** + * @file event_queue.h + * + * @brief Interface of job_queue_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 EVENT_QUEUE_H_ +#define EVENT_QUEUE_H_ + +#include + +#include +#include + +typedef struct event_queue_t event_queue_t; + +/** + * @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] absolute time 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(); + +#endif /*EVENT_QUEUE_H_*/ diff --git a/src/charon/charon/queues/job_queue.c b/src/charon/charon/queues/job_queue.c new file mode 100644 index 000000000..3640395ab --- /dev/null +++ b/src/charon/charon/queues/job_queue.c @@ -0,0 +1,153 @@ +/** + * @file job_queue.c + * + * @brief Implementation of job_queue_t + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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) +{ + while (this->list->get_count(this->list) > 0) + { + job_t *job; + if (this->list->remove_first(this->list,(void *) &job) != SUCCESS) + { + this->list->destroy(this->list); + break; + } + job->destroy_all(job); + } + this->list->destroy(this->list); + + pthread_mutex_destroy(&(this->mutex)); + + pthread_cond_destroy(&(this->condvar)); + + free(this); +} + +/* + * + * Documented in header + */ +job_queue_t *job_queue_create() +{ + 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/charon/queues/job_queue.h b/src/charon/charon/queues/job_queue.h new file mode 100644 index 000000000..9fcf08001 --- /dev/null +++ b/src/charon/charon/queues/job_queue.h @@ -0,0 +1,99 @@ +/** + * @file job_queue.h + * + * @brief Interface of job_queue_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 JOB_QUEUE_H_ +#define JOB_QUEUE_H_ + +#include +#include + +typedef struct job_queue_t job_queue_t; + +/** + * @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(); + +#endif /*JOB_QUEUE_H_*/ diff --git a/src/charon/charon/queues/jobs/Makefile.jobs b/src/charon/charon/queues/jobs/Makefile.jobs new file mode 100644 index 000000000..db89987bc --- /dev/null +++ b/src/charon/charon/queues/jobs/Makefile.jobs @@ -0,0 +1,40 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +JOBS_DIR= $(QUEUES_DIR)jobs/ + +CHARON_OBJS+= $(BUILD_DIR)delete_half_open_ike_sa_job.o +$(BUILD_DIR)delete_half_open_ike_sa_job.o : $(JOBS_DIR)delete_half_open_ike_sa_job.c $(JOBS_DIR)delete_half_open_ike_sa_job.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)delete_established_ike_sa_job.o +$(BUILD_DIR)delete_established_ike_sa_job.o : $(JOBS_DIR)delete_established_ike_sa_job.c $(JOBS_DIR)delete_established_ike_sa_job.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)incoming_packet_job.o +$(BUILD_DIR)incoming_packet_job.o : $(JOBS_DIR)incoming_packet_job.c $(JOBS_DIR)incoming_packet_job.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)initiate_ike_sa_job.o +$(BUILD_DIR)initiate_ike_sa_job.o : $(JOBS_DIR)initiate_ike_sa_job.c $(JOBS_DIR)initiate_ike_sa_job.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)retransmit_request_job.o +$(BUILD_DIR)retransmit_request_job.o : $(JOBS_DIR)retransmit_request_job.c $(JOBS_DIR)retransmit_request_job.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)job.o +$(BUILD_DIR)job.o : $(JOBS_DIR)job.c $(JOBS_DIR)job.h + $(CC) $(CFLAGS) -c -o $@ $< + \ No newline at end of file diff --git a/src/charon/charon/queues/jobs/delete_established_ike_sa_job.c b/src/charon/charon/queues/jobs/delete_established_ike_sa_job.c new file mode 100644 index 000000000..7251e2ca4 --- /dev/null +++ b/src/charon/charon/queues/jobs/delete_established_ike_sa_job.c @@ -0,0 +1,90 @@ +/** + * @file delete_established_ike_sa_job.c + * + * @brief Implementation of delete_established_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_established_ike_sa_job.h" + + + +typedef struct private_delete_established_ike_sa_job_t private_delete_established_ike_sa_job_t; + +/** + * Private data of an delete_established_ike_sa_job_t object. + */ +struct private_delete_established_ike_sa_job_t { + /** + * Public delete_established_ike_sa_job_t interface. + */ + delete_established_ike_sa_job_t public; + + /** + * ID of the ike_sa to delete. + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_delete_established_ike_sa_job_t *this) +{ + return DELETE_ESTABLISHED_IKE_SA; +} + +/** + * Implementation of delete_established_ike_sa_job_t.get_ike_sa_id + */ +static ike_sa_id_t *get_ike_sa_id(private_delete_established_ike_sa_job_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_delete_established_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +delete_established_ike_sa_job_t *delete_established_ike_sa_job_create(ike_sa_id_t *ike_sa_id) +{ + private_delete_established_ike_sa_job_t *this = malloc_thing(private_delete_established_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + /* same as destroy */ + this->public.job_interface.destroy_all = (void (*) (job_t *)) destroy; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* public functions */ + this->public.get_ike_sa_id = (ike_sa_id_t * (*)(delete_established_ike_sa_job_t *)) get_ike_sa_id; + this->public.destroy = (void (*)(delete_established_ike_sa_job_t *)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/charon/queues/jobs/delete_established_ike_sa_job.h b/src/charon/charon/queues/jobs/delete_established_ike_sa_job.h new file mode 100644 index 000000000..762dceae6 --- /dev/null +++ b/src/charon/charon/queues/jobs/delete_established_ike_sa_job.h @@ -0,0 +1,78 @@ +/** + * @file delete_established_ike_sa_job.h + * + * @brief Interface of delete_established_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_ESTABLISHED_IKE_SA_JOB_H_ +#define DELETE_ESTABLISHED_IKE_SA_JOB_H_ + +#include +#include +#include + + +typedef struct delete_established_ike_sa_job_t delete_established_ike_sa_job_t; + +/** + * @brief Class representing an DELETE_ESTABLISHED_IKE_SA Job. + * + * This job initiates the deletion of an IKE_SA. The SA + * to delete is specified via an ike_sa_id_t. + * + * @b Constructors: + * - delete_established_ike_sa_job_create() + * + * @ingroup jobs + */ +struct delete_established_ike_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; + + /** + * @brief Returns the currently set ike_sa_id. + * + * @warning Returned object is not copied. + * + * @param this calling delete_established_ike_sa_job_t object + * @return ike_sa_id_t object + */ + ike_sa_id_t * (*get_ike_sa_id) (delete_established_ike_sa_job_t *this); + + /** + * @brief Destroys an delete_established_ike_sa_job_t object (including assigned data). + * + * @param this delete_established_ike_sa_job_t object to destroy + */ + void (*destroy) (delete_established_ike_sa_job_t *this); +}; + +/** + * @brief Creates a job of type DELETE_ESTABLISHED_IKE_SA. + * + * @param ike_sa_id id of the IKE_SA to delete + * @return delete_established_ike_sa_job_t object + * + * @ingroup jobs + */ +delete_established_ike_sa_job_t *delete_established_ike_sa_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*DELETE_ESTABLISHED_IKE_SA_JOB_H_*/ diff --git a/src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c b/src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c new file mode 100644 index 000000000..610285e20 --- /dev/null +++ b/src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c @@ -0,0 +1,90 @@ +/** + * @file delete_half_open_ike_sa_job.c + * + * @brief Implementation of delete_half_open_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_half_open_ike_sa_job.h" + + + +typedef struct private_delete_half_open_ike_sa_job_t private_delete_half_open_ike_sa_job_t; + +/** + * Private data of an delete_half_open_ike_sa_job_t Object + */ +struct private_delete_half_open_ike_sa_job_t { + /** + * public delete_half_open_ike_sa_job_t interface + */ + delete_half_open_ike_sa_job_t public; + + /** + * ID of the ike_sa to delete + */ + ike_sa_id_t *ike_sa_id; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_delete_half_open_ike_sa_job_t *this) +{ + return DELETE_HALF_OPEN_IKE_SA; +} + +/** + * Implements elete_ike_sa_job_t.get_ike_sa_id + */ +static ike_sa_id_t *get_ike_sa_id(private_delete_half_open_ike_sa_job_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_delete_half_open_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +delete_half_open_ike_sa_job_t *delete_half_open_ike_sa_job_create(ike_sa_id_t *ike_sa_id) +{ + private_delete_half_open_ike_sa_job_t *this = malloc_thing(private_delete_half_open_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + /* same as destroy */ + this->public.job_interface.destroy_all = (void (*) (job_t *)) destroy; + this->public.job_interface.destroy = (void (*)(job_t *)) destroy;; + + /* public functions */ + this->public.get_ike_sa_id = (ike_sa_id_t * (*)(delete_half_open_ike_sa_job_t *)) get_ike_sa_id; + this->public.destroy = (void (*)(delete_half_open_ike_sa_job_t *)) destroy; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h b/src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h new file mode 100644 index 000000000..ea42be8f2 --- /dev/null +++ b/src/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h @@ -0,0 +1,79 @@ +/** + * @file delete_half_open_ike_sa_job.h + * + * @brief Interface of delete_half_open_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_HALF_OPEN_IKE_SA_JOB_H_ +#define DELETE_HALF_OPEN_IKE_SA_JOB_H_ + +#include +#include +#include + + +typedef struct delete_half_open_ike_sa_job_t delete_half_open_ike_sa_job_t; + +/** + * @brief Class representing an DELETE_HALF_OPEN_IKE_SA Job. + * + * This job is responsible for deleting of half open IKE_SAs. A half + * open IKE_SA is every IKE_SA which hasn't reache the ike_sa_established + * state. + * + * @b Constructors: + * - delete_half_open_ike_sa_job_create() + * + * @ingroup jobs + */ +struct delete_half_open_ike_sa_job_t { + /** + * The job_t interface. + */ + job_t job_interface; + + /** + * @brief Returns the currently set ike_sa_id. + * + * @warning Returned object is not copied. + * + * @param this calling delete_half_open_ike_sa_job_t object + * @return ike_sa_id_t object + */ + ike_sa_id_t * (*get_ike_sa_id) (delete_half_open_ike_sa_job_t *this); + + /** + * @brief Destroys an delete_half_open_ike_sa_job_t object (including assigned data). + * + * @param this delete_half_open_ike_sa_job_t object to destroy + */ + void (*destroy) (delete_half_open_ike_sa_job_t *this); +}; + +/** + * @brief Creates a job of type DELETE_HALF_OPEN_IKE_SA. + * + * @param ike_sa_id id of the IKE_SA to delete + * @return created delete_half_open_ike_sa_job_t object + * + * @ingroup jobs + */ +delete_half_open_ike_sa_job_t *delete_half_open_ike_sa_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*DELETE_HALF_OPEN_IKE_SA_JOB_H_*/ diff --git a/src/charon/charon/queues/jobs/incoming_packet_job.c b/src/charon/charon/queues/jobs/incoming_packet_job.c new file mode 100644 index 000000000..fc71f63ea --- /dev/null +++ b/src/charon/charon/queues/jobs/incoming_packet_job.c @@ -0,0 +1,102 @@ +/** + * @file incoming_packet_job.h + * + * @brief Implementation of incoming_packet_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "incoming_packet_job.h" + + + +typedef struct private_incoming_packet_job_t private_incoming_packet_job_t; + +/** + * Private data of an incoming_packet_job_t Object + */ +struct private_incoming_packet_job_t { + /** + * public incoming_packet_job_t interface + */ + incoming_packet_job_t public; + + /** + * Assigned packet + */ + packet_t *packet; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_incoming_packet_job_t *this) +{ + return INCOMING_PACKET; +} + +/** + * Implements incoming_packet_job_t.get_packet. + */ +static packet_t *get_packet(private_incoming_packet_job_t *this) +{ + return this->packet; +} + +/** + * Implements job_t.destroy_all. + */ +static void destroy_all(private_incoming_packet_job_t *this) +{ + if (this->packet != NULL) + { + this->packet->destroy(this->packet); + } + free(this); +} + +/** + * Implements job_t.destroy. + */ +static void destroy(job_t *job) +{ + private_incoming_packet_job_t *this = (private_incoming_packet_job_t *) job; + free(this); +} + +/* + * Described in header + */ +incoming_packet_job_t *incoming_packet_job_create(packet_t *packet) +{ + private_incoming_packet_job_t *this = malloc_thing(private_incoming_packet_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy_all = (void (*) (job_t *)) destroy_all; + this->public.job_interface.destroy = destroy; + + /* public functions */ + this->public.get_packet = (packet_t * (*)(incoming_packet_job_t *)) get_packet; + this->public.destroy = (void (*)(incoming_packet_job_t *)) destroy; + + /* private variables */ + this->packet = packet; + + return &(this->public); +} diff --git a/src/charon/charon/queues/jobs/incoming_packet_job.h b/src/charon/charon/queues/jobs/incoming_packet_job.h new file mode 100644 index 000000000..e3fb5797e --- /dev/null +++ b/src/charon/charon/queues/jobs/incoming_packet_job.h @@ -0,0 +1,78 @@ +/** + * @file incoming_packet_job.h + * + * @brief Interface of incoming_packet_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 INCOMING_PACKET_JOB_H_ +#define INCOMING_PACKET_JOB_H_ + +#include +#include +#include + + +typedef struct incoming_packet_job_t incoming_packet_job_t; + +/** + * @brief Class representing an INCOMING_PACKET Job. + * + * An incoming pack job is created from the receiver, which has + * read a packet to process from the socket. + * + * @b Constructors: + * - incoming_packet_job_create() + * + * @ingroup jobs + */ +struct incoming_packet_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Returns the assigned packet_t object + * + * @warning Returned packet is not cloned and has to get destroyed by the caller. + * + * @param this calling incoming_packet_job_t object + * @return assigned packet + */ + packet_t *(*get_packet) (incoming_packet_job_t *this); + + /** + * @brief Destroys an incoming_packet_job_t object. + * + * @param this incoming_packet_job_t object to destroy + */ + void (*destroy) (incoming_packet_job_t *this); +}; + +/** + * @brief Creates a job of type INCOMING_PACKET + * + * @param[in] packet packet to assign with this job + * @return created incoming_packet_job_t object + * + * @ingroup jobs + */ +incoming_packet_job_t *incoming_packet_job_create(packet_t *packet); + +#endif /*INCOMING_PACKET_JOB_H_*/ diff --git a/src/charon/charon/queues/jobs/initiate_ike_sa_job.c b/src/charon/charon/queues/jobs/initiate_ike_sa_job.c new file mode 100644 index 000000000..ac9ace36c --- /dev/null +++ b/src/charon/charon/queues/jobs/initiate_ike_sa_job.c @@ -0,0 +1,101 @@ +/** + * @file initiate_ike_sa_job.c + * + * @brief Implementation of initiate_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "initiate_ike_sa_job.h" + + + +typedef struct private_initiate_ike_sa_job_t private_initiate_ike_sa_job_t; + +/** + * Private data of an initiate_ike_sa_job_t Object + */ +struct private_initiate_ike_sa_job_t { + /** + * public initiate_ike_sa_job_t interface + */ + initiate_ike_sa_job_t public; + + /** + * associated connection object to initiate + */ + connection_t *connection; +}; + + +/** + * Implements initiate_ike_sa_job_t.get_type. + */ +static job_type_t get_type(private_initiate_ike_sa_job_t *this) +{ + return INITIATE_IKE_SA; +} + +/** + * Implements initiate_ike_sa_job_t.get_configuration_name. + */ +static connection_t *get_connection(private_initiate_ike_sa_job_t *this) +{ + return this->connection; +} + +/** + * Implements job_t.destroy. + */ +static void destroy_all(private_initiate_ike_sa_job_t *this) +{ + this->connection->destroy(this->connection); + free(this); +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_initiate_ike_sa_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +initiate_ike_sa_job_t *initiate_ike_sa_job_create(connection_t *connection) +{ + private_initiate_ike_sa_job_t *this = malloc_thing(private_initiate_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy_all = (void (*) (job_t *)) destroy_all; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* public functions */ + this->public.get_connection = (connection_t* (*)(initiate_ike_sa_job_t *)) get_connection; + this->public.destroy = (void (*)(initiate_ike_sa_job_t *)) destroy; + + /* private variables */ + this->connection = connection; + + return &(this->public); +} diff --git a/src/charon/charon/queues/jobs/initiate_ike_sa_job.h b/src/charon/charon/queues/jobs/initiate_ike_sa_job.h new file mode 100644 index 000000000..cee31f07b --- /dev/null +++ b/src/charon/charon/queues/jobs/initiate_ike_sa_job.h @@ -0,0 +1,75 @@ +/** + * @file initiate_ike_sa_job.h + * + * @brief Interface of initiate_ike_sa_job_t. + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 INITIATE_IKE_SA_JOB_H_ +#define INITIATE_IKE_SA_JOB_H_ + +#include +#include +#include + + +typedef struct initiate_ike_sa_job_t initiate_ike_sa_job_t; + +/** + * @brief Class representing an INITIATE_IKE_SA Job. + * + * This job is created if an IKE_SA should be iniated. This + * happens via a user request, or via the kernel interface. + * + * @b Constructors: + * - initiate_ike_sa_job_create() + * + * @ingroup jobs + */ +struct initiate_ike_sa_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Returns the connection_t to initialize + * + * @param this calling initiate_ike_sa_job_t object + * @return connection_t + */ + connection_t *(*get_connection) (initiate_ike_sa_job_t *this); + + /** + * @brief Destroys an initiate_ike_sa_job_t object. + * + * @param this initiate_ike_sa_job_t object to destroy + */ + void (*destroy) (initiate_ike_sa_job_t *this); +}; + +/** + * @brief Creates a job of type INITIATE_IKE_SA. + * + * @param connection connection_t to initializes + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +initiate_ike_sa_job_t *initiate_ike_sa_job_create(connection_t *connection); + +#endif /*INITIATE_IKE_SA_JOB_H_*/ diff --git a/src/charon/charon/queues/jobs/job.c b/src/charon/charon/queues/jobs/job.c new file mode 100644 index 000000000..df739f9e5 --- /dev/null +++ b/src/charon/charon/queues/jobs/job.c @@ -0,0 +1,34 @@ +/** + * @file job.c + * + * @brief Interface additions to job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "job.h" + + +mapping_t job_type_m[] = { + {INCOMING_PACKET, "INCOMING_PACKET"}, + {RETRANSMIT_REQUEST, "RETRANSMIT_REQUEST"}, + {INITIATE_IKE_SA, "INITIATE_IKE_SA"}, + {DELETE_HALF_OPEN_IKE_SA, "DELETE_HALF_OPEN_IKE_SA"}, + {DELETE_ESTABLISHED_IKE_SA, "DELETE_ESTABLISHED_IKE_SA"}, + {MAPPING_END, NULL} +}; diff --git a/src/charon/charon/queues/jobs/job.h b/src/charon/charon/queues/jobs/job.h new file mode 100644 index 000000000..eea4da09e --- /dev/null +++ b/src/charon/charon/queues/jobs/job.h @@ -0,0 +1,120 @@ +/** + * @file job.h + * + * @brief Interface job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 JOB_H_ +#define JOB_H_ + +#include +#include + + +typedef enum job_type_t job_type_t; + +/** + * @brief Definition of the various job types. + * + * @todo add more jobs, such as rekeying. + * + * @ingroup jobs + */ +enum job_type_t { + /** + * Process an incoming IKEv2-Message. + * + * Job is implemented in class type incoming_packet_job_t + */ + INCOMING_PACKET, + + /** + * Retransmit an IKEv2-Message. + */ + RETRANSMIT_REQUEST, + + /** + * Establish an ike sa as initiator. + * + * Job is implemented in class type initiate_ike_sa_job_t + */ + INITIATE_IKE_SA, + + /** + * Delete an ike sa which is still not established. + * + * Job is implemented in class type delete_half_open_ike_sa_job_t + */ + DELETE_HALF_OPEN_IKE_SA, + + /** + * Delete an ike sa which is established. + * + * Job is implemented in class type delete_established_ike_sa_job_t + */ + DELETE_ESTABLISHED_IKE_SA +}; + +/** + * string mappings for job_type_t + * + * @ingroup jobs + */ +extern mapping_t job_type_m[]; + + +typedef struct job_t job_t; + +/** + * @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 Destroys a job_t object and all assigned data! + * + * @param job_t calling object + */ + void (*destroy_all) (job_t *job); + + /** + * @brief Destroys a job_t object + * + * @param job_t calling object + */ + void (*destroy) (job_t *job); +}; + + +#endif /*JOB_H_*/ diff --git a/src/charon/charon/queues/jobs/retransmit_request_job.c b/src/charon/charon/queues/jobs/retransmit_request_job.c new file mode 100644 index 000000000..e171df5bd --- /dev/null +++ b/src/charon/charon/queues/jobs/retransmit_request_job.c @@ -0,0 +1,132 @@ +/** + * @file retransmit_request_job.c + * + * @brief Implementation of retransmit_request_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "retransmit_request_job.h" + + + + +typedef struct private_retransmit_request_job_t private_retransmit_request_job_t; + +/** + * Private data of an retransmit_request_job_t Object. + */ +struct private_retransmit_request_job_t { + /** + * Public retransmit_request_job_t interface. + */ + retransmit_request_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; + + /** + * Number of times a request was retransmitted + */ + u_int32_t retransmit_count; +}; + + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_retransmit_request_job_t *this) +{ + return RETRANSMIT_REQUEST; +} + +/** + * Implements retransmit_request_job_t.get_ike_sa_id. + */ +static ike_sa_id_t *get_ike_sa_id(private_retransmit_request_job_t *this) +{ + return this->ike_sa_id; +} + +/** + * Implements retransmit_request_job_t.get_retransmit_count. + */ +static u_int32_t get_retransmit_count(private_retransmit_request_job_t *this) +{ + return this->retransmit_count; +} + +/** + * Implements retransmit_request_job_t.increase_retransmit_count. + */ +static void increase_retransmit_count(private_retransmit_request_job_t *this) +{ + this->retransmit_count++; +} + +/** + * Implements retransmit_request_job_t.get_message_id. + */ +static u_int32_t get_message_id(private_retransmit_request_job_t *this) +{ + return this->message_id; +} + + +/** + * Implements job_t.destroy. + */ +static void destroy(private_retransmit_request_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header. + */ +retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id) +{ + private_retransmit_request_job_t *this = malloc_thing(private_retransmit_request_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + /* same as destroy */ + this->public.job_interface.destroy_all = (void (*) (job_t *)) destroy; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* public functions */ + this->public.get_ike_sa_id = (ike_sa_id_t * (*)(retransmit_request_job_t *)) get_ike_sa_id; + this->public.get_message_id = (u_int32_t (*)(retransmit_request_job_t *)) get_message_id; + this->public.destroy = (void (*)(retransmit_request_job_t *)) destroy; + this->public.get_retransmit_count = (u_int32_t (*)(retransmit_request_job_t *)) get_retransmit_count; + this->public.increase_retransmit_count = (void (*)(retransmit_request_job_t *)) increase_retransmit_count; + + /* private variables */ + this->message_id = message_id; + this->retransmit_count = 0; + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + + return &(this->public); +} diff --git a/src/charon/charon/queues/jobs/retransmit_request_job.h b/src/charon/charon/queues/jobs/retransmit_request_job.h new file mode 100644 index 000000000..2349d3f5e --- /dev/null +++ b/src/charon/charon/queues/jobs/retransmit_request_job.h @@ -0,0 +1,105 @@ +/** + * @file retransmit_request_job.h + * + * @brief Interface of retransmit_request_job_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 RESEND_MESSAGE_JOB_H_ +#define RESEND_MESSAGE_JOB_H_ + +#include +#include +#include + + +typedef struct retransmit_request_job_t retransmit_request_job_t; + +/** + * @brief Class representing an RETRANSMIT_REQUEST 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_request_job_create() + * + * @ingroup jobs + */ +struct retransmit_request_job_t { + /** + * The job_t interface. + */ + job_t job_interface; + + /** + * @brief Returns the retransmit count for a specific request. + * + * @param this calling retransmit_request_job_t object + * @return retransmit count of request + */ + u_int32_t (*get_retransmit_count) (retransmit_request_job_t *this); + + /** + * @brief Increases number of retransmitt attemps. + * + * @param this calling retransmit_request_job_t object + */ + void (*increase_retransmit_count) (retransmit_request_job_t *this); + + /** + * @brief Returns the message_id of the request to be resent + * + * @param this calling retransmit_request_job_t object + * @return message id of the request to resend + */ + u_int32_t (*get_message_id) (retransmit_request_job_t *this); + + /** + * @brief Returns the ike_sa_id_t object of the IKE_SA + * which the request belongs to + * + * @warning returned ike_sa_id_t object is getting destroyed in + * retransmit_request_job_t.destroy. + * + * @param this calling retransmit_request_job_t object + * @return ike_sa_id_t object to identify IKE_SA (gets NOT cloned) + */ + ike_sa_id_t *(*get_ike_sa_id) (retransmit_request_job_t *this); + + /** + * @brief Destroys an retransmit_request_job_t object. + * + * @param this retransmit_request_job_t object to destroy + */ + void (*destroy) (retransmit_request_job_t *this); +}; + +/** + * @brief Creates a job of type RETRANSMIT_REQUEST. + * + * @param message_id message_id of the request to resend + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return retransmit_request_job_t object + * + * @ingroup jobs + */ +retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id); + +#endif /* RESEND_MESSAGE_JOB_H_ */ diff --git a/src/charon/charon/queues/send_queue.c b/src/charon/charon/queues/send_queue.c new file mode 100644 index 000000000..0852e5303 --- /dev/null +++ b/src/charon/charon/queues/send_queue.c @@ -0,0 +1,153 @@ +/** + * @file send_queue.c + * + * @brief Implementation of send_queue_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "send_queue.h" + +#include + + +typedef struct private_send_queue_t private_send_queue_t; + +/** + * @brief Private Variables and Functions of send_queue class + * + */ +struct private_send_queue_t { + /** + * Public part of the send_queue_t object + */ + send_queue_t public; + + /** + * The packets 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 send_queue_t.get_count + */ +static int get_count(private_send_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 send_queue_t.get + */ +static packet_t *get(private_send_queue_t *this) +{ + int oldstate; + packet_t *packet; + pthread_mutex_lock(&(this->mutex)); + /* go to wait while no packets 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 **)&packet); + pthread_mutex_unlock(&(this->mutex)); + return packet; +} + +/** + * implements send_queue_t.add + */ +static void add(private_send_queue_t *this, packet_t *packet) +{ + pthread_mutex_lock(&(this->mutex)); + this->list->insert_last(this->list,packet); + pthread_cond_signal( &(this->condvar)); + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * implements send_queue_t.destroy + */ +static void destroy (private_send_queue_t *this) +{ + + /* destroy all packets in list before destroying list */ + while (this->list->get_count(this->list) > 0) + { + packet_t *packet; + if (this->list->remove_first(this->list,(void *) &packet) != SUCCESS) + { + this->list->destroy(this->list); + break; + } + packet->destroy(packet); + } + this->list->destroy(this->list); + + pthread_mutex_destroy(&(this->mutex)); + + pthread_cond_destroy(&(this->condvar)); + + free(this); +} + +/* + * + * Documented in header + */ +send_queue_t *send_queue_create() +{ + private_send_queue_t *this = malloc_thing(private_send_queue_t); + + this->public.get_count = (int(*)(send_queue_t*)) get_count; + this->public.get = (packet_t*(*)(send_queue_t*)) get; + this->public.add = (void(*)(send_queue_t*, packet_t*)) add; + this->public.destroy = (void(*)(send_queue_t*)) 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/charon/queues/send_queue.h b/src/charon/charon/queues/send_queue.h new file mode 100644 index 000000000..6dc5867eb --- /dev/null +++ b/src/charon/charon/queues/send_queue.h @@ -0,0 +1,100 @@ +/** + * @file send_queue.h + * + * @brief Interface of send_queue_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SEND_QUEUE_H_ +#define SEND_QUEUE_H_ + +#include +#include + + +typedef struct send_queue_t send_queue_t; + +/** + * @brief The send queue stores packet for the sender_t instance. + * + * The sender_t will send them consequently over the wire. + * Although the send-queue is based on a linked_list_t + * all access functions are thread-save implemented. + * + * @b Constructors: + * - send_queue_create() + * + * @ingroup queues + */ +struct send_queue_t { + + /** + * @brief returns number of packets in queue + * + * @param send_queue_t calling object + * @param[out] count integer pointer to store the count in + * @returns number of items in queue + */ + int (*get_count) (send_queue_t *send_queue); + + /** + * @brief get the next packet from the queue. + * + * If the queue is empty, this function blocks until a packet can be returned. + * + * After using, the returned packet has to get destroyed by the caller. + * + * @param send_queue_t calling object + * @return next packet from the queue + */ + packet_t *(*get) (send_queue_t *send_queue); + + /** + * @brief adds a packet to the queue. + * + * This function is non blocking and adds a packet_t to the list. + * The specific packet object has to get destroyed by the thread which + * removes the packet. + * + * @param send_queue_t calling object + * @param packet packet_t to add to the queue (packet is not copied) + */ + void (*add) (send_queue_t *send_queue, packet_t *packet); + + /** + * @brief destroys a send_queue object. + * + * @warning The caller of this function has to make sure + * that no thread is going to add or get a packet from the send_queue + * after calling this function. + * + * @param send_queue_t calling object + */ + void (*destroy) (send_queue_t *send_queue); +}; + +/** + * @brief Creates an empty send_queue_t. + * + * @return send_queue_t object + * + * @ingroup queues + */ +send_queue_t *send_queue_create(); + +#endif /*SEND_QUEUE_H_*/ diff --git a/src/charon/charon/sa/Makefile.sa b/src/charon/charon/sa/Makefile.sa new file mode 100644 index 000000000..825c19959 --- /dev/null +++ b/src/charon/charon/sa/Makefile.sa @@ -0,0 +1,37 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +SA_DIR= $(CHARON_DIR)sa/ + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_id.o +$(BUILD_DIR)ike_sa_id.o : $(SA_DIR)ike_sa_id.c $(SA_DIR)ike_sa_id.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_manager.o +$(BUILD_DIR)ike_sa_manager.o : $(SA_DIR)ike_sa_manager.c $(SA_DIR)ike_sa_manager.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa.o +$(BUILD_DIR)ike_sa.o : $(SA_DIR)ike_sa.c $(SA_DIR)ike_sa.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)authenticator.o +$(BUILD_DIR)authenticator.o : $(SA_DIR)authenticator.c $(SA_DIR)authenticator.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)child_sa.o +$(BUILD_DIR)child_sa.o : $(SA_DIR)child_sa.c $(SA_DIR)child_sa.h + $(CC) $(CFLAGS) -c -o $@ $< + +include $(SA_DIR)states/Makefile.states \ No newline at end of file diff --git a/src/charon/charon/sa/authenticator.c b/src/charon/charon/sa/authenticator.c new file mode 100644 index 000000000..3aeb8795f --- /dev/null +++ b/src/charon/charon/sa/authenticator.c @@ -0,0 +1,405 @@ +/** + * @file authenticator.c + * + * @brief Implementation of authenticator_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 + +/** + * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + */ +#define IKEV2_KEY_PAD "Key Pad for IKEv2" + + +typedef struct private_authenticator_t private_authenticator_t; + +/** + * Private data of an authenticator_t object. + */ +struct private_authenticator_t { + + /** + * Public authenticator_t interface. + */ + authenticator_t public; + + /** + * Assigned IKE_SA. Needed to get objects of type prf_t and logger_t. + */ + protected_ike_sa_t *ike_sa; + + /** + * PRF taken from the IKE_SA. + */ + prf_t *prf; + + /** + * A logger for. + * + * Using logger of IKE_SA. + */ + logger_t *logger; + + /** + * @brief Creates the octets which are signed (RSA) or MACed (shared secret) as described in section + * 2.15 of RFC. + * + * @param this calling object + * @param last_message the last message to include in created octets + * (either binary form of IKE_SA_INIT request or IKE_SA_INIT response) + * @param other_nonce Nonce data received from other peer + * @param my_id id_payload_t object representing an ID payload + * @param initiator Type of peer. TRUE, if it is original initiator, FALSE otherwise + * @return octets as described in section 2.15. Memory gets allocated and has to get + * destroyed by caller. + */ + chunk_t (*allocate_octets) (private_authenticator_t *this, + chunk_t last_message, + chunk_t other_nonce, + id_payload_t *my_id, + bool initiator); + + /** + * @brief Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + * + * @param this calling object + * @param last_message the last message + * (either binary form of IKE_SA_INIT request or IKE_SA_INIT response) + * @param nonce Nonce data to include in auth data compution + * @param id_payload id_payload_t object representing an ID payload + * @param initiator Type of peer. TRUE, if it is original initiator, FALSE otherwise + * @param shared_secret shared secret as chunk_t. If shared secret is a string, + * the NULL termination is not included. + * @return AUTH data as dscribed in section 2.15 for + * AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. + * Memory gets allocated and has to get destroyed by caller. + */ + chunk_t (*build_preshared_secret_signature) (private_authenticator_t *this, + chunk_t last_message, + chunk_t nonce, + id_payload_t *id_payload, + bool initiator, + chunk_t preshared_secret); +}; + +/** + * Implementation of private_authenticator_t.allocate_octets. + */ +static chunk_t allocate_octets(private_authenticator_t *this, + chunk_t last_message, + chunk_t other_nonce, + id_payload_t *my_id, + bool initiator) +{ + prf_t *prf; + chunk_t id_chunk = my_id->get_data(my_id); + u_int8_t id_with_header[4 + id_chunk.len]; + /* + * IKEv2 for linux (http://sf.net/projects/ikev2/) + * is not compatible with IKEv2 Draft and so not compatible with this + * implementation, cause AUTH data are computed without + * ID type and the three reserved bytes. + */ + chunk_t id_with_header_chunk = {ptr:id_with_header, len: sizeof(id_with_header)}; + u_int8_t *current_pos; + chunk_t octets; + + id_with_header[0] = my_id->get_id_type(my_id); + id_with_header[1] = 0x00; + id_with_header[2] = 0x00; + id_with_header[3] = 0x00; + memcpy(id_with_header + 4,id_chunk.ptr,id_chunk.len); + + if (initiator) + { + prf = this->ike_sa->get_prf_auth_i(this->ike_sa); + } + else + { + prf = this->ike_sa->get_prf_auth_r(this->ike_sa); + } + + /* 4 bytes are id type and reserved fields of id payload */ + octets.len = last_message.len + other_nonce.len + prf->get_block_size(prf); + octets.ptr = malloc(octets.len); + current_pos = octets.ptr; + memcpy(current_pos,last_message.ptr,last_message.len); + current_pos += last_message.len; + memcpy(current_pos,other_nonce.ptr,other_nonce.len); + current_pos += other_nonce.len; + prf->get_bytes(prf, id_with_header_chunk, current_pos); + + this->logger->log_chunk(this->logger,RAW | LEVEL2, "Octets (Mesage + Nonce + prf(Sk_px,Idx)",octets); + return octets; +} + +/** + * Implementation of private_authenticator_t.build_preshared_secret_signature. + */ +static chunk_t build_preshared_secret_signature(private_authenticator_t *this, + chunk_t last_message, + chunk_t nonce, + id_payload_t *id_payload, + bool initiator, + chunk_t preshared_secret) +{ + chunk_t key_pad = {ptr: IKEV2_KEY_PAD, len:strlen(IKEV2_KEY_PAD)}; + u_int8_t key_buffer[this->prf->get_block_size(this->prf)]; + chunk_t key = {ptr: key_buffer, len: sizeof(key_buffer)}; + chunk_t auth_data; + + chunk_t octets = this->allocate_octets(this,last_message,nonce,id_payload,initiator); + + /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), ) */ + this->prf->set_key(this->prf, preshared_secret); + this->prf->get_bytes(this->prf, key_pad, key_buffer); + this->prf->set_key(this->prf, key); + this->prf->allocate_bytes(this->prf, octets, &auth_data); + chunk_free(&octets); + this->logger->log_chunk(this->logger,RAW | LEVEL2, "Authenticated data",auth_data); + + return auth_data; +} + +/** + * Implementation of authenticator_t.verify_auth_data. + */ +static status_t verify_auth_data (private_authenticator_t *this, + auth_payload_t *auth_payload, + chunk_t last_received_packet, + chunk_t my_nonce, + id_payload_t *other_id_payload, + bool initiator) +{ + switch(auth_payload->get_auth_method(auth_payload)) + { + case SHARED_KEY_MESSAGE_INTEGRITY_CODE: + { + identification_t *other_id = other_id_payload->get_identification(other_id_payload); + chunk_t auth_data = auth_payload->get_data(auth_payload); + chunk_t preshared_secret; + status_t status; + + status = charon->credentials->get_shared_secret(charon->credentials, + other_id, + &preshared_secret); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No shared secret found for %s", + other_id->get_string(other_id)); + other_id->destroy(other_id); + return status; + } + + chunk_t my_auth_data = this->build_preshared_secret_signature(this, + last_received_packet, + my_nonce, + other_id_payload, + initiator, + preshared_secret); + chunk_free(&preshared_secret); + + if (auth_data.len != my_auth_data.len) + { + chunk_free(&my_auth_data); + status = FAILED; + } + else if (memcmp(auth_data.ptr,my_auth_data.ptr, my_auth_data.len) == 0) + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with preshared secret successful", + other_id->get_string(other_id)); + status = SUCCESS; + } + else + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with preshared secret failed", + other_id->get_string(other_id)); + status = FAILED; + } + other_id->destroy(other_id); + chunk_free(&my_auth_data); + return status; + } + case RSA_DIGITAL_SIGNATURE: + { + identification_t *other_id = other_id_payload->get_identification(other_id_payload); + rsa_public_key_t *public_key; + status_t status; + chunk_t octets, auth_data; + + auth_data = auth_payload->get_data(auth_payload); + + public_key = charon->credentials->get_rsa_public_key(charon->credentials, + other_id); + if (public_key == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No RSA public key found for %s", + other_id->get_string(other_id)); + other_id->destroy(other_id); + return NOT_FOUND; + } + + octets = this->allocate_octets(this,last_received_packet, my_nonce,other_id_payload, initiator); + + status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data); + if (status == SUCCESS) + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with RSA successful", + other_id->get_string(other_id)); + } + else + { + this->logger->log(this->logger, CONTROL, "Authentication of %s with RSA failed", + other_id->get_string(other_id)); + } + + public_key->destroy(public_key); + other_id->destroy(other_id); + chunk_free(&octets); + return status; + } + default: + { + return NOT_SUPPORTED; + } + } +} + +/** + * Implementation of authenticator_t.compute_auth_data. + */ +static status_t compute_auth_data (private_authenticator_t *this, + auth_payload_t **auth_payload, + chunk_t last_sent_packet, + chunk_t other_nonce, + id_payload_t *my_id_payload, + bool initiator) +{ + connection_t *connection = this->ike_sa->get_connection(this->ike_sa); + + switch(connection->get_auth_method(connection)) + { + case SHARED_KEY_MESSAGE_INTEGRITY_CODE: + { + identification_t *my_id = my_id_payload->get_identification(my_id_payload); + chunk_t preshared_secret; + status_t status; + chunk_t auth_data; + + status = charon->credentials->get_shared_secret(charon->credentials, + my_id, + &preshared_secret); + + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No shared secret found for %s", + my_id->get_string(my_id)); + my_id->destroy(my_id); + return status; + } + my_id->destroy(my_id); + + auth_data = this->build_preshared_secret_signature(this, last_sent_packet, other_nonce, + my_id_payload, initiator, preshared_secret); + chunk_free(&preshared_secret); + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, SHARED_KEY_MESSAGE_INTEGRITY_CODE); + (*auth_payload)->set_data(*auth_payload, auth_data); + + chunk_free(&auth_data); + return SUCCESS; + } + case RSA_DIGITAL_SIGNATURE: + { + identification_t *my_id = my_id_payload->get_identification(my_id_payload); + rsa_private_key_t *private_key; + status_t status; + chunk_t octets, auth_data; + + private_key = charon->credentials->get_rsa_private_key(charon->credentials, my_id); + if (private_key == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, "No RSA private key found for %s", + my_id->get_string(my_id)); + my_id->destroy(my_id); + return NOT_FOUND; + } + my_id->destroy(my_id); + + octets = this->allocate_octets(this,last_sent_packet,other_nonce,my_id_payload,initiator); + + status = private_key->build_emsa_pkcs1_signature(private_key, HASH_SHA1, octets, &auth_data); + chunk_free(&octets); + if (status != SUCCESS) + { + private_key->destroy(private_key); + return status; + } + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, RSA_DIGITAL_SIGNATURE); + (*auth_payload)->set_data(*auth_payload, auth_data); + + private_key->destroy(private_key); + chunk_free(&auth_data); + return SUCCESS; + } + default: + { + return NOT_SUPPORTED; + } + } +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy (private_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa) +{ + private_authenticator_t *this = malloc_thing(private_authenticator_t); + + /* Public functions */ + this->public.destroy = (void(*)(authenticator_t*))destroy; + this->public.verify_auth_data = (status_t (*) (authenticator_t *,auth_payload_t *, chunk_t ,chunk_t ,id_payload_t *,bool)) verify_auth_data; + this->public.compute_auth_data = (status_t (*) (authenticator_t *,auth_payload_t **, chunk_t ,chunk_t ,id_payload_t *,bool)) compute_auth_data; + + /* private functions */ + this->allocate_octets = allocate_octets; + this->build_preshared_secret_signature = build_preshared_secret_signature; + + /* private data */ + this->ike_sa = ike_sa; + this->prf = this->ike_sa->get_prf(this->ike_sa); + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &(this->public); +} diff --git a/src/charon/charon/sa/authenticator.h b/src/charon/charon/sa/authenticator.h new file mode 100644 index 000000000..b6bc317ac --- /dev/null +++ b/src/charon/charon/sa/authenticator.h @@ -0,0 +1,138 @@ +/** + * @file authenticator.h + * + * @brief Interface of authenticator_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 AUTHENTICATOR_H_ +#define AUTHENTICATOR_H_ + +#include +#include +#include +#include +#include + + +typedef struct authenticator_t authenticator_t; + +/** + * @brief Class used to authenticate a peer. + * + * Currently the following two AUTH methods are supported: + * - SHARED_KEY_MESSAGE_INTEGRITY_CODE + * - RSA_DIGITAL_SIGNATURE + * + * This class retrieves needed data for specific AUTH methods (RSA keys, shared secrets, etc.) + * over an internal stored protected_ike_sa_t object or directly from the configuration_t over + * the daemon_t object "charon". + * + * @b Constructors: + * - authenticator_create() + * + * @ingroup sa + */ +struct authenticator_t { + + /** + * @brief Verify's given authentication data. + * + * To verify a received AUTH payload the following data must be provided: + * - the last received IKEv2 Message from the other peer in binary form + * - the nonce value sent to the other peer + * - the ID payload of the other peer + * + * @param this calling object + * @param last_received_packet binary representation of the last received IKEv2-Message + * @param my_nonce the sent nonce (without payload header) + * @param other_id_payload the ID payload received from other peer + * @param initiator type of other peer. TRUE, if it is original initiator, FALSE otherwise + * + * @todo Document RSA error status types + * + * @return + * - SUCCESS if verification successful + * - FAILED if verification failed + * - NOT_SUPPORTED if AUTH method not supported + * - NOT_FOUND if the data for specific AUTH method could not be found + * (e.g. shared secret, rsa key) + */ + status_t (*verify_auth_data) (authenticator_t *this, + auth_payload_t *auth_payload, + chunk_t last_received_packet, + chunk_t my_nonce, + id_payload_t *other_id_payload, + bool initiator); + + /** + * @brief Computes authentication data and creates specific AUTH payload. + * + * To create an AUTH payload, the following data must be provided: + * - the last sent IKEv2 Message in binary form + * - the nonce value received from the other peer + * - the ID payload of myself + * + * @param this calling object + * @param[out] auth_payload The object of typee auth_payload_t will be created at pointing location + * @param last_sent_packet binary representation of the last sent IKEv2-Message + * @param other_nonce the received nonce (without payload header) + * @param my_id_payload the ID payload going to send to other peer + * @param initiator type of myself. TRUE, if I'm original initiator, FALSE otherwise + * + * @todo Document RSA error status types + * + * @return + * - SUCCESS if authentication data could be computed + * - NOT_SUPPORTED if AUTH method not supported + * - NOT_FOUND if the data for AUTH method could not be found + */ + status_t (*compute_auth_data) (authenticator_t *this, + auth_payload_t **auth_payload, + chunk_t last_sent_packet, + chunk_t other_nonce, + id_payload_t *my_id_payload, + bool initiator); + + /** + * @brief Destroys a authenticator_t object. + * + * @param this calling object + */ + void (*destroy) (authenticator_t *this); +}; + +/** + * @brief Creates an authenticator object. + * + * @warning: The following functions of the assigned protected_ike_sa_t object + * must return a valid value: + * - protected_ike_sa_t.get_policy + * - protected_ike_sa_t.get_prf + * - protected_ike_sa_t.get_logger + * This preconditions are not given in IKE_SA states INITIATOR_INIT or RESPONDER_INIT! + * + * @param ike_sa object of type protected_ike_sa_t + * + * @return authenticator_t object + * + * @ingroup sa + */ +authenticator_t *authenticator_create(protected_ike_sa_t *ike_sa); + +#endif /* AUTHENTICATOR_H_ */ diff --git a/src/charon/charon/sa/child_sa.c b/src/charon/charon/sa/child_sa.c new file mode 100644 index 000000000..a678ea9b8 --- /dev/null +++ b/src/charon/charon/sa/child_sa.c @@ -0,0 +1,590 @@ +/** + * @file child_sa.c + * + * @brief Implementation of child_sa_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "child_sa.h" + +#include + + +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 { + + /** + * Network on local side + */ + host_t *my_net; + + /** + * Network on remote side + */ + host_t *other_net; + + /** + * Number of bits for local network (subnet size) + */ + u_int8_t my_net_mask; + + /** + * Number of bits for remote network (subnet size) + */ + u_int8_t other_net_mask; + + /** + * Protocol for this policy, such as TCP/UDP/ICMP... + */ + int upper_proto; +}; + +typedef struct private_child_sa_t private_child_sa_t; + +/** + * Private data of a child_sa_t object. + */ +struct private_child_sa_t { + /** + * Public interface of child_sa_t. + */ + child_sa_t public; + + /** + * IP of this peer + */ + host_t *me; + + /** + * IP of other peer + */ + host_t *other; + + /** + * Local security parameter index for AH protocol, 0 if not used + */ + u_int32_t my_ah_spi; + + /** + * Local security parameter index for ESP protocol, 0 if not used + */ + u_int32_t my_esp_spi; + + /** + * Remote security parameter index for AH protocol, 0 if not used + */ + u_int32_t other_ah_spi; + + /** + * Remote security parameter index for ESP protocol, 0 if not used + */ + u_int32_t other_esp_spi; + + /** + * List containing policy_id_t objects + */ + linked_list_t *policies; + + /** + * reqid used for this child_sa + */ + u_int32_t reqid; + + /** + * CHILD_SAs own logger + */ + logger_t *logger; +}; + +/** + * Implements child_sa_t.alloc + */ +static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) +{ + protocol_id_t protocols[2]; + iterator_t *iterator; + proposal_t *proposal; + status_t status; + u_int i; + + /* iterator through proposals */ + iterator = proposals->create_iterator(proposals, TRUE); + while(iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&proposal); + proposal->get_protocols(proposal, protocols); + + /* check all protocols */ + for (i = 0; i<2; i++) + { + switch (protocols[i]) + { + case PROTO_AH: + /* do we already have an spi for AH?*/ + if (this->my_ah_spi == 0) + { + /* nope, get one */ + status = charon->kernel_interface->get_spi( + charon->kernel_interface, + this->me, this->other, + PROTO_AH, FALSE, + &(this->my_ah_spi)); + } + /* update proposal */ + proposal->set_spi(proposal, PROTO_AH, (u_int64_t)this->my_ah_spi); + break; + case PROTO_ESP: + /* do we already have an spi for ESP?*/ + if (this->my_esp_spi == 0) + { + /* nope, get one */ + status = charon->kernel_interface->get_spi( + charon->kernel_interface, + this->me, this->other, + PROTO_ESP, FALSE, + &(this->my_esp_spi)); + } + /* update proposal */ + proposal->set_spi(proposal, PROTO_ESP, (u_int64_t)this->my_esp_spi); + break; + default: + break; + } + if (status != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + } + iterator->destroy(iterator); + return SUCCESS; +} + +static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine) +{ + protocol_id_t protocols[2]; + u_int32_t spi; + encryption_algorithm_t enc_algo; + integrity_algorithm_t int_algo; + chunk_t enc_key, int_key; + algorithm_t *algo; + crypter_t *crypter; + signer_t *signer; + size_t key_size; + host_t *src; + host_t *dst; + status_t status; + u_int i; + + /* we must assign the roles to correctly set up the SAs */ + if (mine) + { + src = this->me; + dst = this->other; + } + else + { + dst = this->me; + src = this->other; + } + + proposal->get_protocols(proposal, protocols); + /* derive keys in order as protocols appear */ + for (i = 0; i<2; i++) + { + if (protocols[i] != PROTO_NONE) + { + + /* now we have to decide which spi to use. Use self allocated, if "mine", + * or the one in the proposal, if not "mine" (others). */ + if (mine) + { + if (protocols[i] == PROTO_AH) + { + spi = this->my_ah_spi; + } + else + { + spi = this->my_esp_spi; + } + } + else /* use proposals spi */ + { + spi = proposal->get_spi(proposal, protocols[i]); + if (protocols[i] == PROTO_AH) + { + this->other_ah_spi = spi; + } + else + { + this->other_esp_spi = spi; + } + } + + /* derive encryption key first */ + if (proposal->get_algorithm(proposal, protocols[i], ENCRYPTION_ALGORITHM, &algo)) + { + enc_algo = algo->algorithm; + this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s, ", + mapping_find(protocol_id_m, protocols[i]), + mine ? "me" : "other", + mapping_find(transform_type_m, ENCRYPTION_ALGORITHM), + mapping_find(encryption_algorithm_m, enc_algo)); + + /* we must create a (unused) crypter, since its the only way to get the size + * of the key. This is not so nice, since charon must support all algorithms + * the kernel supports... + * TODO: build something of a encryption algorithm lookup function + */ + crypter = crypter_create(enc_algo, algo->key_size); + key_size = crypter->get_key_size(crypter); + crypter->destroy(crypter); + prf_plus->allocate_bytes(prf_plus, key_size, &enc_key); + this->logger->log_chunk(this->logger, PRIVATE, "key:", enc_key); + } + else + { + enc_algo = ENCR_UNDEFINED; + } + + /* derive integrity key */ + if (proposal->get_algorithm(proposal, protocols[i], INTEGRITY_ALGORITHM, &algo)) + { + int_algo = algo->algorithm; + this->logger->log(this->logger, CONTROL|LEVEL1, "%s for %s: using %s %s,", + mapping_find(protocol_id_m, protocols[i]), + mine ? "me" : "other", + mapping_find(transform_type_m, INTEGRITY_ALGORITHM), + mapping_find(integrity_algorithm_m, algo->algorithm)); + + signer = signer_create(int_algo); + key_size = signer->get_key_size(signer); + signer->destroy(signer); + prf_plus->allocate_bytes(prf_plus, key_size, &int_key); + this->logger->log_chunk(this->logger, PRIVATE, "key:", int_key); + } + else + { + int_algo = AUTH_UNDEFINED; + } + /* send keys down to kernel */ + this->logger->log(this->logger, CONTROL|LEVEL1, + "installing 0x%.8x for %s, src %s dst %s", + ntohl(spi), mapping_find(protocol_id_m, protocols[i]), + src->get_address(src), dst->get_address(dst)); + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, + spi, protocols[i], + this->reqid, + enc_algo, enc_key, + int_algo, int_key, mine); + /* clean up for next round */ + if (enc_algo != ENCR_UNDEFINED) + { + chunk_free(&enc_key); + } + if (int_algo != AUTH_UNDEFINED) + { + chunk_free(&int_key); + } + + if (status != SUCCESS) + { + return FAILED; + } + + + } + } + return SUCCESS; +} + +static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus) +{ + linked_list_t *list; + + /* install others (initiators) SAs*/ + if (install(this, proposal, prf_plus, FALSE) != SUCCESS) + { + return FAILED; + } + + /* get SPIs for our SAs */ + list = linked_list_create(); + list->insert_last(list, proposal); + if (alloc(this, list) != SUCCESS) + { + list->destroy(list); + return FAILED; + } + list->destroy(list); + + /* install our (responders) SAs */ + if (install(this, proposal, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + + return SUCCESS; +} + +static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus) +{ + /* install our (initator) SAs */ + if (install(this, proposal, prf_plus, TRUE) != SUCCESS) + { + return FAILED; + } + /* install his (responder) SAs */ + if (install(this, proposal, prf_plus, FALSE) != 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) +{ + iterator_t *my_iter, *other_iter; + traffic_selector_t *my_ts, *other_ts; + + /* 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->has_next(my_iter)) + { + my_iter->current(my_iter, (void**)&my_ts); + other_iter->reset(other_iter); + while (other_iter->has_next(other_iter)) + { + /* set up policies for every entry in my_ts_list to every entry in other_ts_list */ + int family; + chunk_t from_addr; + u_int16_t from_port, to_port; + sa_policy_t *policy; + status_t status; + + other_iter->current(other_iter, (void**)&other_ts); + + /* only set up policies if protocol matches */ + if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts)) + { + continue; + } + policy = malloc_thing(sa_policy_t); + policy->upper_proto = my_ts->get_protocol(my_ts); + + /* calculate net and ports for local side */ + family = my_ts->get_type(my_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + from_addr = my_ts->get_from_address(my_ts); + from_port = my_ts->get_from_port(my_ts); + to_port = my_ts->get_to_port(my_ts); + from_port = (from_port != to_port) ? 0 : from_port; + policy->my_net = host_create_from_chunk(family, from_addr, from_port); + policy->my_net_mask = my_ts->get_netmask(my_ts); + chunk_free(&from_addr); + + /* calculate net and ports for remote side */ + family = other_ts->get_type(other_ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6; + from_addr = other_ts->get_from_address(other_ts); + from_port = other_ts->get_from_port(other_ts); + to_port = other_ts->get_to_port(other_ts); + from_port = (from_port != to_port) ? 0 : from_port; + policy->other_net = host_create_from_chunk(family, from_addr, from_port); + policy->other_net_mask = other_ts->get_netmask(other_ts); + chunk_free(&from_addr); + + /* install 3 policies: out, in and forward */ + status = charon->kernel_interface->add_policy(charon->kernel_interface, + this->me, this->other, + policy->my_net, policy->other_net, + policy->my_net_mask, policy->other_net_mask, + XFRM_POLICY_OUT, policy->upper_proto, + this->my_ah_spi, this->my_esp_spi, + this->reqid); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_IN, policy->upper_proto, + this->my_ah_spi, this->my_esp_spi, + this->reqid); + + status |= charon->kernel_interface->add_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_FWD, policy->upper_proto, + this->my_ah_spi, this->my_esp_spi, + this->reqid); + + if (status != SUCCESS) + { + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + policy->my_net->destroy(policy->my_net); + policy->other_net->destroy(policy->other_net); + free(policy); + return status; + } + + /* add it to the policy list, since we want to know which policies we own */ + this->policies->insert_last(this->policies, policy); + } + } + + my_iter->destroy(my_iter); + other_iter->destroy(other_iter); + return SUCCESS; +} + +/** + * Implementation of child_sa_t.log_status. + */ +static void log_status(private_child_sa_t *this, logger_t *logger, char* name) +{ + iterator_t *iterator; + sa_policy_t *policy; + struct protoent *proto; + char proto_buf[8] = ""; + char *proto_name = proto_buf; + + if (logger == NULL) + { + logger = this->logger; + } + logger->log(logger, CONTROL|LEVEL1, "\"%s\": protected with ESP (0x%x/0x%x), AH (0x%x,0x%x):", + name, + htonl(this->my_esp_spi), htonl(this->other_esp_spi), + htonl(this->my_ah_spi), htonl(this->other_ah_spi)); + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&policy); + if (policy->upper_proto) + { + proto = getprotobynumber(policy->upper_proto); + if (proto) + { + proto_name = proto->p_name; + } + else + { + snprintf(proto_buf, sizeof(proto_buf), "<%d>", policy->upper_proto); + } + } + logger->log(logger, CONTROL, "\"%s\": %s/%d==%s==%s/%d", + name, + policy->my_net->get_address(policy->my_net), policy->my_net_mask, + proto_name, + policy->other_net->get_address(policy->other_net), policy->other_net_mask); + } + iterator->destroy(iterator); +} + +/** + * Implementation of child_sa_t.destroy. + */ +static void destroy(private_child_sa_t *this) +{ + /* delete all policys in the kernel */ + sa_policy_t *policy; + while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) + { + charon->kernel_interface->del_policy(charon->kernel_interface, + this->me, this->other, + policy->my_net, policy->other_net, + policy->my_net_mask, policy->other_net_mask, + XFRM_POLICY_OUT, policy->upper_proto); + + charon->kernel_interface->del_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_IN, policy->upper_proto); + + charon->kernel_interface->del_policy(charon->kernel_interface, + this->other, this->me, + policy->other_net, policy->my_net, + policy->other_net_mask, policy->my_net_mask, + XFRM_POLICY_FWD, policy->upper_proto); + + policy->my_net->destroy(policy->my_net); + policy->other_net->destroy(policy->other_net); + free(policy); + } + this->policies->destroy(this->policies); + + /* delete SAs in the kernel, if they are set up */ + if (this->my_ah_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other, this->my_ah_spi, PROTO_AH); + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me, this->other_ah_spi, PROTO_AH); + } + if (this->my_esp_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other, this->my_esp_spi, PROTO_ESP); + charon->kernel_interface->del_sa(charon->kernel_interface, + this->me, this->other_esp_spi, PROTO_ESP); + } + free(this); +} + +/* + * Described in header. + */ +child_sa_t * child_sa_create(host_t *me, host_t* other) +{ + static u_int32_t reqid = 0xc0000000; + private_child_sa_t *this = malloc_thing(private_child_sa_t); + + /* public functions */ + this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; + this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add; + this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update; + this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; + this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status; + this->public.destroy = (void(*)(child_sa_t*))destroy; + + /* private data */ + this->logger = logger_manager->get_logger(logger_manager, CHILD_SA); + this->me = me; + this->other = other; + this->my_ah_spi = 0; + this->my_esp_spi = 0; + this->other_ah_spi = 0; + this->other_esp_spi = 0; + this->reqid = reqid++; + this->policies = linked_list_create(); + + return (&this->public); +} diff --git a/src/charon/charon/sa/child_sa.h b/src/charon/charon/sa/child_sa.h new file mode 100644 index 000000000..6ccbff13f --- /dev/null +++ b/src/charon/charon/sa/child_sa.h @@ -0,0 +1,149 @@ +/** + * @file child_sa.h + * + * @brief Interface of child_sa_t. + * + */ + +/* + * Copyright (C) 2005 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_SA_H_ +#define CHILD_SA_H_ + +#include +#include +#include +#include + +typedef struct child_sa_t child_sa_t; + +/** + * @brief Represents multiple IPsec SAs between two hosts. + * + * A child_sa_t contains multiple SAs. SAs for both + * directions are managed in one child_sa_t object, and + * if both AH and ESP is set up, both protocols are managed + * by one child_sa_t. This means we can have two or + * in the AH+ESP case four IPsec-SAs in one child_sa_t. + * + * 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 Allocate SPIs for a given proposals. + * + * Since the kernel manages SPIs for us, we need + * to allocate them. If the 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 proposal proposal for which SPIs are allocated + */ + status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); + + /** + * @brief Install the kernel SAs for a proposal. + * + * Since the kernel manages SPIs for us, we need + * to allocate them. If the 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 proposal proposal for which SPIs are allocated + * @param prf_plus key material to use for key derivation + */ + status_t (*add)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus); + + /** + * @brief Install the kernel SAs for a proposal, if SPIs already allocated. + * + * This one updates the SAs in the kernel, which are + * allocated via alloc, with a selected proposals. + * + * @param this calling object + * @param proposal proposal for which SPIs are allocated + * @param prf_plus key material to use for key derivation + */ + status_t (*update)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus); + + /** + * @brief Install the policies using some traffic selectors. + * + * Spplied 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 + * @return SUCCESS or FAILED + */ + status_t (*add_policies) (child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list); + + /** + * @brief Log the status of a child_sa to a logger. + * + * The status of ESP/AH SAs is logged with the supplied logger in + * a human readable form. + * Supplying NULL as logger uses the internal child_sa logger + * to do the logging. The name is only a log-prefix without further + * meaning. + * + * @param this calling object + * @param logger logger to use for logging + * @param name connection name + */ + void (*log_status) (child_sa_t *this, logger_t *logger, char *name); + + /** + * @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 + * @return child_sa_t object + * + * @ingroup sa + */ +child_sa_t * child_sa_create(host_t *me, host_t *other); + +#endif /*CHILD_SA_H_*/ diff --git a/src/charon/charon/sa/ike_sa.c b/src/charon/charon/sa/ike_sa.c new file mode 100644 index 000000000..6322eb8e9 --- /dev/null +++ b/src/charon/charon/sa/ike_sa.c @@ -0,0 +1,1199 @@ +/** + * @file ike_sa.c + * + * @brief Implementation of ike_sa_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "ike_sa.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + +typedef struct private_ike_sa_t private_ike_sa_t; + +/** + * Private data of an ike_sa_t object. + */ +struct private_ike_sa_t { + + /** + * Protected part of a ike_sa_t object. + */ + protected_ike_sa_t protected; + + /** + * Identifier for the current IKE_SA. + */ + ike_sa_id_t *ike_sa_id; + + /** + * Linked List containing the child sa's of the current IKE_SA. + */ + linked_list_t *child_sas; + + /** + * Current state of the IKE_SA represented as state_t object. + * + * A state object representates one of the following states and is processing + * messages in the specific state: + * - INITIATOR_INIT + * - RESPONDER_INIT + * - IKE_SA_INIT_REQUESTED + * - IKE_SA_INIT_RESPONDED + * - IKE_AUTH_REQUESTED + * -IKE_SA_ESTABLISHED + */ + state_t *current_state; + + /** + * INIT configuration, needed for the IKE_SA_INIT exchange. + * + * Gets set in states: + * - INITATOR_INIT + * - RESPONDER_INIT + * + * Available in states: + * - IKE_SA_INIT_REQUESTED + * - IKE_SA_INIT_RESPONDED + * - IKE_AUTH_REQUESTED + * -IKE_SA_ESTABLISHED + */ + connection_t *connection; + + /** + * SA configuration, needed for all other exchanges after IKE_SA_INIT exchange. + * + * Gets set in states: + * - IKE_SA_INIT_REQUESTED + * - IKE_SA_INIT_RESPONDED + * + * Available in states: + * - IKE_AUTH_REQUESTED + * -IKE_SA_ESTABLISHED + */ + policy_t *policy; + + /** + * This SA's source for random data. + * + * Is available in every state. + */ + randomizer_t *randomizer; + + /** + * The last responded message. + */ + message_t *last_responded_message; + + /** + * The ast requested message. + */ + message_t *last_requested_message; + + /** + * Crypter object for initiator. + */ + crypter_t *crypter_initiator; + + /** + * Crypter object for responder. + */ + crypter_t *crypter_responder; + + /** + * Signer object for initiator. + */ + signer_t *signer_initiator; + + /** + * Signer object for responder. + */ + signer_t *signer_responder; + + /** + * Multi purpose prf, set key, use it, forget it + */ + prf_t *prf; + + /** + * Prf function for derivating keymat child SAs + */ + prf_t *child_prf; + + /** + * PRF, with key set to pi_key, used for authentication + */ + prf_t *prf_auth_i; + + /** + * PRF, with key set to pr_key, used for authentication + */ + prf_t *prf_auth_r; + + /** + * Next message id to receive. + */ + u_int32_t message_id_in; + + /** + * Next message id to send. + */ + u_int32_t message_id_out; + + /** + * Last reply id which was successfully received. + */ + int32_t last_replied_message_id; + + /** + * A logger for this IKE_SA. + */ + logger_t *logger; + + /** + * Resends the last sent reply. + * + * @param this calling object + */ + status_t (*resend_last_reply) (private_ike_sa_t *this); +}; + +/** + * Implementation of ike_sa_t.process_message. + */ +static status_t process_message (private_ike_sa_t *this, message_t *message) +{ + u_int32_t message_id; + exchange_type_t exchange_type; + bool is_request; + + /* We must process each request or response from remote host */ + + /* Find out type of message (request or response) */ + is_request = message->get_request(message); + exchange_type = message->get_exchange_type(message); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Process %s of exchange type %s", + (is_request) ? "request" : "response",mapping_find(exchange_type_m,exchange_type)); + + message_id = message->get_message_id(message); + + /* + * It has to be checked, if the message has to be resent cause of lost packets! + */ + if (is_request && (message_id == (this->message_id_in - 1))) + { + /* Message can be resent ! */ + this->logger->log(this->logger, CONTROL|LEVEL1, "Resent request detected. Send stored reply."); + return (this->resend_last_reply(this)); + } + + /* Now, the message id is checked for request AND reply */ + if (is_request) + { + /* In a request, the message has to be this->message_id_in (other case is already handled) */ + if (message_id != this->message_id_in) + { + this->logger->log(this->logger, ERROR | LEVEL1, + "Message request with message id %d received, but %d expected", + message_id,this->message_id_in); + return FAILED; + } + } + else + { + /* In a reply, the message has to be this->message_id_out -1 cause it is the reply to the last sent message*/ + if (message_id != (this->message_id_out - 1)) + { + this->logger->log(this->logger, ERROR | LEVEL1, + "Message reply with message id %d received, but %d expected", + message_id,this->message_id_in); + return FAILED; + } + } + + /* now the message is processed by the current state object. + * The specific state object is responsible to check if a message can be received in + * the state it represents. + * The current state is also responsible to change the state object to the next state + * by calling protected_ike_sa_t.set_new_state*/ + return this->current_state->process_message(this->current_state,message); +} + +/** + * Implementation of protected_ike_sa_t.build_message. + */ +static void build_message(private_ike_sa_t *this, exchange_type_t type, bool request, message_t **message) +{ + message_t *new_message; + host_t *me, *other; + + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Build empty message"); + new_message = message_create(); + new_message->set_source(new_message, me->clone(me)); + new_message->set_destination(new_message, other->clone(other)); + new_message->set_exchange_type(new_message, type); + new_message->set_request(new_message, request); + new_message->set_message_id(new_message, (request) ? this->message_id_out : this->message_id_in); + new_message->set_ike_sa_id(new_message, this->ike_sa_id); + + *message = new_message; +} + +/** + * Implementation of protected_ike_sa_t.initiate_connection. + */ +static status_t initiate_connection(private_ike_sa_t *this, connection_t *connection) +{ + initiator_init_t *current_state; + + /* Work is done in state object of type INITIATOR_INIT. All other states are not + * initial states and so don't have a initiate_connection function */ + + if (this->current_state->get_state(this->current_state) != INITIATOR_INIT) + { + return FAILED; + } + + current_state = (initiator_init_t *) this->current_state; + + return current_state->initiate_connection(current_state, connection); +} + +/** + * Implementation of ike_sa_t.send_delete_ike_sa_request. + */ +static void send_delete_ike_sa_request (private_ike_sa_t *this) +{ + message_t *informational_request; + delete_payload_t *delete_payload; + crypter_t *crypter; + signer_t *signer; + packet_t *packet; + status_t status; + + if (this->current_state->get_state(this->current_state) != IKE_SA_ESTABLISHED) + { + return; + } + + /* build empty INFORMATIONAL message */ + this->protected.build_message(&(this->protected), INFORMATIONAL, TRUE, &informational_request); + + delete_payload = delete_payload_create(); + delete_payload->set_protocol_id(delete_payload, PROTO_IKE); + + informational_request->add_payload(informational_request,(payload_t *)delete_payload); + + if (this->ike_sa_id->is_initiator(this->ike_sa_id)) + { + crypter = this->crypter_initiator; + signer = this->signer_initiator; + } + else + { + crypter = this->crypter_responder; + signer = this->signer_responder; + } + + status = informational_request->generate(informational_request, + crypter, + signer, &packet); + informational_request->destroy(informational_request); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Could not generate packet from message"); + return ; + } + + charon->send_queue->add(charon->send_queue,packet); +} + +/** + * 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_host. + */ +static host_t* get_my_host(private_ike_sa_t *this) +{ + return this->connection->get_my_host(this->connection);; +} + +/** + * Implementation of ike_sa_t.get_other_host. + */ +static host_t* get_other_host(private_ike_sa_t *this) +{ + return this->connection->get_other_host(this->connection);; +} + +/** + * Implementation of ike_sa_t.get_my_id. + */ +static identification_t* get_my_id(private_ike_sa_t *this) +{ + return this->connection->get_my_id(this->connection);; +} + +/** + * Implementation of ike_sa_t.get_other_id. + */ +static identification_t* get_other_id(private_ike_sa_t *this) +{ + return this->connection->get_other_id(this->connection);; +} + +/** + * Implementation of private_ike_sa_t.resend_last_reply. + */ +static status_t resend_last_reply(private_ike_sa_t *this) +{ + packet_t *packet; + + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to retransmit last reply"); + packet = this->last_responded_message->get_packet(this->last_responded_message); + charon->send_queue->add(charon->send_queue, packet); + + return SUCCESS; +} + +/** + * Implementation of ike_sa_t.retransmit_request. + */ +status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id) +{ + packet_t *packet; + + if (this->last_requested_message == NULL) + { + return NOT_FOUND; + } + + if (message_id == this->last_replied_message_id) + { + return NOT_FOUND; + } + + if ((this->last_requested_message->get_message_id(this->last_requested_message)) != message_id) + { + return NOT_FOUND; + } + + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to retransmit message with id %d",message_id); + packet = this->last_requested_message->get_packet(this->last_requested_message); + charon->send_queue->add(charon->send_queue, packet); + + return SUCCESS; +} + +/** + * Implementation of protected_ike_sa_t.set_new_state. + */ +static void set_new_state (private_ike_sa_t *this, state_t *state) +{ + this->logger->log(this->logger, CONTROL, "statechange: %s => %s", + mapping_find(ike_sa_state_m,this->current_state->get_state(this->current_state)), + mapping_find(ike_sa_state_m,state->get_state(state))); + this->current_state = state; +} + +/** + * Implementation of protected_ike_sa_t.get_connection. + */ +static connection_t *get_connection (private_ike_sa_t *this) +{ + return this->connection; +} + +/** + * Implementation of protected_ike_sa_t.set_connection. + */ +static void set_connection (private_ike_sa_t *this,connection_t * connection) +{ + this->connection = connection; +} + +/** + * Implementation of protected_ike_sa_t.get_policy. + */ +static policy_t *get_policy (private_ike_sa_t *this) +{ + return this->policy; +} + +/** + * Implementation of protected_ike_sa_t.set_policy. + */ +static void set_policy (private_ike_sa_t *this,policy_t * policy) +{ + this->policy = policy; +} + +/** + * Implementation of protected_ike_sa_t.get_prf. + */ +static prf_t *get_prf (private_ike_sa_t *this) +{ + return this->prf; +} + +/** + * Implementation of protected_ike_sa_t.get_prf. + */ +static prf_t *get_child_prf (private_ike_sa_t *this) +{ + return this->child_prf; +} + +/** + * Implementation of protected_ike_sa_t.get_prf_auth_i. + */ +static prf_t *get_prf_auth_i (private_ike_sa_t *this) +{ + return this->prf_auth_i; +} + +/** + * Implementation of protected_ike_sa_t.get_prf_auth_r. + */ +static prf_t *get_prf_auth_r (private_ike_sa_t *this) +{ + return this->prf_auth_r; +} + + +/** + * Implementation of protected_ike_sa_t.build_transforms. + */ +static status_t build_transforms(private_ike_sa_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r) +{ + chunk_t nonces, nonces_spis, skeyseed, key, secret; + u_int64_t spi_i, spi_r; + prf_plus_t *prf_plus; + algorithm_t *algo; + size_t key_size; + + /* + * Build the PRF+ instance for deriving keys + */ + if (this->prf != NULL) + { + this->prf->destroy(this->prf); + } + proposal->get_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, &algo); + if (algo == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL2, "No PRF algoithm selected!?"); + return FAILED; + } + this->prf = prf_create(algo->algorithm); + if (this->prf == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "PSEUDO_RANDOM_FUNCTION %s not supported!", + mapping_find(pseudo_random_function_m, algo->algorithm)); + return FAILED; + } + + /* concatenate nonces = nonce_i | nonce_r */ + nonces = chunk_alloc(nonce_i.len + nonce_r.len); + memcpy(nonces.ptr, nonce_i.ptr, nonce_i.len); + memcpy(nonces.ptr + nonce_i.len, nonce_r.ptr, nonce_r.len); + + /* concatenate prf_seed = nonce_i | nonce_r | spi_i | spi_r */ + nonces_spis = chunk_alloc(nonces.len + 16); + memcpy(nonces_spis.ptr, nonces.ptr, nonces.len); + spi_i = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); + spi_r = this->ike_sa_id->get_responder_spi(this->ike_sa_id); + memcpy(nonces_spis.ptr + nonces.len, &spi_i, 8); + memcpy(nonces_spis.ptr + nonces.len + 8, &spi_r, 8); + + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + dh->get_shared_secret(dh, &secret); + this->logger->log_chunk(this->logger, PRIVATE, "Shared Diffie Hellman secret", secret); + this->prf->set_key(this->prf, nonces); + this->prf->allocate_bytes(this->prf, secret, &skeyseed); + this->logger->log_chunk(this->logger, PRIVATE | LEVEL1, "SKEYSEED", skeyseed); + chunk_free(&secret); + + /* prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr ) + * = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr + * + * we use the prf directly for prf+ + */ + this->prf->set_key(this->prf, skeyseed); + prf_plus = prf_plus_create(this->prf, nonces_spis); + + /* clean up unused stuff */ + chunk_free(&nonces); + chunk_free(&nonces_spis); + chunk_free(&skeyseed); + + + /* + * We now can derive all of our key. We build the transforms + * directly. + */ + + + /* SK_d used for prf+ to derive keys for child SAs */ + 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); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_d secret", key); + this->child_prf->set_key(this->child_prf, key); + chunk_free(&key); + + + /* SK_ai/SK_ar used for integrity protection */ + proposal->get_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, &algo); + if (algo == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL2, "No integrity algoithm selected?!"); + return FAILED; + } + if (this->signer_initiator != NULL) + { + this->signer_initiator->destroy(this->signer_initiator); + } + if (this->signer_responder != NULL) + { + this->signer_responder->destroy(this->signer_responder); + } + + this->signer_initiator = signer_create(algo->algorithm); + this->signer_responder = signer_create(algo->algorithm); + if (this->signer_initiator == NULL || this->signer_responder == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "INTEGRITY_ALGORITHM %s not supported!", + mapping_find(integrity_algorithm_m,algo->algorithm)); + return FAILED; + } + key_size = this->signer_initiator->get_key_size(this->signer_initiator); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_ai secret", key); + this->signer_initiator->set_key(this->signer_initiator, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_ar secret", key); + this->signer_responder->set_key(this->signer_responder, key); + chunk_free(&key); + + + /* SK_ei/SK_er used for encryption */ + proposal->get_algorithm(proposal, PROTO_IKE, ENCRYPTION_ALGORITHM, &algo); + if (algo == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL2, "No encryption algoithm selected!?"); + return FAILED; + } + if (this->crypter_initiator != NULL) + { + this->crypter_initiator->destroy(this->crypter_initiator); + } + if (this->crypter_responder != NULL) + { + this->crypter_responder->destroy(this->crypter_responder); + } + + this->crypter_initiator = crypter_create(algo->algorithm, algo->key_size); + this->crypter_responder = crypter_create(algo->algorithm, algo->key_size); + if (this->crypter_initiator == NULL || this->crypter_responder == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "ENCRYPTION_ALGORITHM %s (key size %d) not supported!", + mapping_find(encryption_algorithm_m, algo->algorithm), + algo->key_size); + return FAILED; + } + key_size = this->crypter_initiator->get_key_size(this->crypter_initiator); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_ei secret", key); + this->crypter_initiator->set_key(this->crypter_initiator, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_er secret", key); + this->crypter_responder->set_key(this->crypter_responder, key); + chunk_free(&key); + + /* SK_pi/SK_pr used for authentication */ + proposal->get_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, &algo); + if (this->prf_auth_i != NULL) + { + this->prf_auth_i->destroy(this->prf_auth_i); + } + if (this->prf_auth_r != NULL) + { + this->prf_auth_r->destroy(this->prf_auth_r); + } + + this->prf_auth_i = prf_create(algo->algorithm); + this->prf_auth_r = prf_create(algo->algorithm); + + key_size = this->prf_auth_i->get_key_size(this->prf_auth_i); + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_pi secret", key); + this->prf_auth_i->set_key(this->prf_auth_i, key); + chunk_free(&key); + + prf_plus->allocate_bytes(prf_plus, key_size, &key); + this->logger->log_chunk(this->logger, PRIVATE, "Sk_pr secret", key); + this->prf_auth_r->set_key(this->prf_auth_r, key); + chunk_free(&key); + + /* all done, prf_plus not needed anymore */ + prf_plus->destroy(prf_plus); + + return SUCCESS; +} + +/** + * Implementation of protected_ike_sa_t.get_randomizer. + */ +static randomizer_t *get_randomizer (private_ike_sa_t *this) +{ + return this->randomizer; +} + +/** + * Implementation of protected_ike_sa_t.get_crypter_initiator. + */ +static crypter_t *get_crypter_initiator (private_ike_sa_t *this) +{ + return this->crypter_initiator; +} + +/** + * Implementation of protected_ike_sa_t.get_signer_initiator. + */ +static signer_t *get_signer_initiator (private_ike_sa_t *this) +{ + return this->signer_initiator; +} + +/** + * Implementation of protected_ike_sa_t.get_crypter_responder. + */ +static crypter_t *get_crypter_responder(private_ike_sa_t *this) +{ + return this->crypter_responder; +} + +/** + * Implementation of protected_ike_sa_t.get_signer_responder. + */ +static signer_t *get_signer_responder (private_ike_sa_t *this) +{ + return this->signer_responder; +} + +/** + * Implementation of protected_ike_sa_t.send_request. + */ +static status_t send_request (private_ike_sa_t *this,message_t * message) +{ + retransmit_request_job_t *retransmit_job; + u_int32_t timeout; + crypter_t *crypter; + signer_t *signer; + packet_t *packet; + status_t status; + + if (message->get_message_id(message) != this->message_id_out) + { + this->logger->log(this->logger, ERROR, "Message could not be sent cause id (%d) was not as expected (%d)", + message->get_message_id(message),this->message_id_out); + return FAILED; + } + + /* generate packet */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Generate packet from message"); + + if (this->ike_sa_id->is_initiator(this->ike_sa_id)) + { + crypter = this->crypter_initiator; + signer = this->signer_initiator; + } + else + { + crypter = this->crypter_responder; + signer =this->signer_responder; + } + + status = message->generate(message, crypter,signer, &packet); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Could not generate packet from message"); + return FAILED; + } + + this->logger->log(this->logger, CONTROL|LEVEL3, + "Add request packet with message id %d to global send queue", + this->message_id_out); + charon->send_queue->add(charon->send_queue, packet); + + if (this->last_requested_message != NULL) + { + /* destroy message */ + this->last_requested_message->destroy(this->last_requested_message); + } + + this->logger->log(this->logger, CONTROL|LEVEL3, "Replace last requested message with new one"); + this->last_requested_message = message; + + retransmit_job = retransmit_request_job_create(this->message_id_out,this->ike_sa_id); + + status = charon->configuration->get_retransmit_timeout (charon->configuration, + retransmit_job->get_retransmit_count(retransmit_job),&timeout); + + if (status != SUCCESS) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "No retransmit job for message created!"); + retransmit_job->destroy(retransmit_job); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL2, "Request will be retransmitted in %d ms.",timeout); + charon->event_queue->add_relative(charon->event_queue,(job_t *) retransmit_job,timeout); + } + + /* message counter can now be increased */ + this->logger->log(this->logger, CONTROL|LEVEL3, + "Increase message counter for outgoing messages from %d", + this->message_id_out); + this->message_id_out++; + return SUCCESS; +} + +/** + * Implementation of protected_ike_sa_t.send_response. + */ +static status_t send_response (private_ike_sa_t *this,message_t * message) +{ + crypter_t *crypter; + signer_t *signer; + packet_t *packet; + status_t status; + + if (message->get_message_id(message) != this->message_id_in) + { + this->logger->log(this->logger, ERROR, "Message could not be sent cause id was not as expected"); + return FAILED; + } + + + if (this->ike_sa_id->is_initiator(this->ike_sa_id)) + { + crypter = this->crypter_initiator; + signer = this->signer_initiator; + } + else + { + crypter = this->crypter_responder; + signer =this->signer_responder; + } + + status = message->generate(message, crypter,signer, &packet); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Could not generate packet from message"); + return FAILED; + } + + this->logger->log(this->logger, CONTROL|LEVEL3, + "Add response packet with message id %d to global send queue", + this->message_id_in); + charon->send_queue->add(charon->send_queue, packet); + + if (this->last_responded_message != NULL) + { + /* destroy message */ + this->last_responded_message->destroy(this->last_responded_message); + } + + this->logger->log(this->logger, CONTROL|LEVEL3, "Replace last responded message with new one"); + this->last_responded_message = message; + + /* message counter can now be increased */ + this->logger->log(this->logger, CONTROL|LEVEL3, "Increase message counter for incoming messages"); + this->message_id_in++; + + return SUCCESS; +} + +/** + * Implementation of of private_responder_init_t.send_notify_reply. + */ +static void send_notify(private_ike_sa_t *this, exchange_type_t exchange_type, notify_message_type_t type, chunk_t data) +{ + notify_payload_t *payload; + message_t *response; + packet_t *packet; + status_t status; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Going to build message with notify payload"); + /* set up the reply */ + this->protected.build_message(&(this->protected), exchange_type, FALSE, &response); + payload = notify_payload_create_from_protocol_and_type(PROTO_IKE, type); + if ((data.ptr != NULL) && (data.len > 0)) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "Add Data to notify payload"); + payload->set_notification_data(payload,data); + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add Notify payload to message"); + response->add_payload(response,(payload_t *) payload); + + /* generate packet */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Generate packet from message"); + status = response->generate(response, this->crypter_responder, this->signer_responder, &packet); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "Could not generate notify message"); + response->destroy(response); + return; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add packet to global send queue"); + charon->send_queue->add(charon->send_queue, packet); + this->logger->log(this->logger, CONTROL|LEVEL2, "Destroy message"); + response->destroy(response); +} + +/** + * Implementation of protected_ike_sa_t.set_last_replied_message_id. + */ +static void set_last_replied_message_id (private_ike_sa_t *this,u_int32_t message_id) +{ + this->last_replied_message_id = message_id; +} + +/** + * Implementation of protected_ike_sa_t.get_last_responded_message. + */ +static message_t * get_last_responded_message (private_ike_sa_t *this) +{ + return this->last_responded_message; +} + +/** + * Implementation of protected_ike_sa_t.get_last_requested_message. + */ +static message_t * get_last_requested_message (private_ike_sa_t *this) +{ + return this->last_requested_message; +} + +/** + * Implementation of protected_ike_sa_t.get_state. + */ +static ike_sa_state_t get_state (private_ike_sa_t *this) +{ + return this->current_state->get_state(this->current_state); +} + +/** + * Implementation of protected_ike_sa_t.get_state. + */ +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 protected_ike_sa_t.reset_message_buffers. + */ +static void reset_message_buffers (private_ike_sa_t *this) +{ + this->logger->log(this->logger, CONTROL|LEVEL2, "Reset message counters and destroy stored messages"); + /* destroy stored requested message */ + if (this->last_requested_message != NULL) + { + this->last_requested_message->destroy(this->last_requested_message); + this->last_requested_message = NULL; + } + + /* destroy stored responded messages */ + if (this->last_responded_message != NULL) + { + this->last_responded_message->destroy(this->last_responded_message); + this->last_responded_message = NULL; + } + + this->message_id_out = 0; + this->message_id_in = 0; + this->last_replied_message_id = -1; +} + +/** + * Implementation of protected_ike_sa_t.log_status. + */ +static void log_status(private_ike_sa_t *this, logger_t *logger, char *name) +{ + iterator_t *iterator; + child_sa_t *child_sa; + + /* only log if name == NULL or name == connection_name */ + if (name) + { + if (strcmp(this->connection->get_name(this->connection), name) != 0) + { + return; + } + } + else + { + name = this->connection->get_name(this->connection); + } + + host_t *my_host = this->connection->get_my_host(this->connection); + host_t *other_host = this->connection->get_other_host(this->connection); + + identification_t *my_id = this->connection->get_my_id(this->connection); + identification_t *other_id = this->connection->get_other_id(this->connection); + + if (logger == NULL) + { + logger = this->logger; + } + logger->log(logger, CONTROL|LEVEL1, "\"%s\": IKE_SA in state %s, SPIs: 0x%.16llx 0x%.16llx", + name, + mapping_find(ike_sa_state_m, this->current_state->get_state(this->current_state)), + this->ike_sa_id->get_initiator_spi(this->ike_sa_id), + this->ike_sa_id->get_responder_spi(this->ike_sa_id)); + logger->log(logger, CONTROL, "\"%s\": %s[%s]...%s[%s]", + name, + my_host->get_address(my_host), + my_id->get_string(my_id), + other_host->get_address(other_host), + other_id->get_string(other_id)); + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&child_sa); + child_sa->log_status(child_sa, logger, name); + } + iterator->destroy(iterator); +} + +/** + * Implementation of protected_ike_sa_t.destroy. + */ +static void destroy (private_ike_sa_t *this) +{ + child_sa_t *child_sa; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Going to destroy IKE SA %llu:%llu, role %s", + this->ike_sa_id->get_initiator_spi(this->ike_sa_id), + this->ike_sa_id->get_responder_spi(this->ike_sa_id), + this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); + + /* inform other peer of delete */ + send_delete_ike_sa_request(this); + while (this->child_sas->remove_last(this->child_sas, (void**)&child_sa) == SUCCESS) + { + child_sa->destroy(child_sa); + } + this->child_sas->destroy(this->child_sas); + + if (this->crypter_initiator) + { + this->crypter_initiator->destroy(this->crypter_initiator); + } + if (this->crypter_responder) + { + this->crypter_responder->destroy(this->crypter_responder); + } + if (this->signer_initiator) + { + this->signer_initiator->destroy(this->signer_initiator); + } + if (this->signer_responder) + { + this->signer_responder->destroy(this->signer_responder); + } + if (this->prf) + { + this->prf->destroy(this->prf); + } + if (this->child_prf) + { + this->child_prf->destroy(this->child_prf); + } + if (this->prf_auth_i) + { + this->prf_auth_i->destroy(this->prf_auth_i); + } + if (this->prf_auth_r) + { + this->prf_auth_r->destroy(this->prf_auth_r); + } + if (this->connection) + { + host_t *me, *other; + me = this->connection->get_my_host(this->connection); + other = this->connection->get_other_host(this->connection); + + this->logger->log(this->logger, AUDIT, "IKE_SA deleted between %s - %s", + me->get_address(me), other->get_address(other)); + this->connection->destroy(this->connection); + } + if (this->policy) + { + this->policy->destroy(this->policy); + } + if (this->last_requested_message) + { + this->last_requested_message->destroy(this->last_requested_message); + } + if (this->last_responded_message) + { + this->last_responded_message->destroy(this->last_responded_message); + } + this->ike_sa_id->destroy(this->ike_sa_id); + this->randomizer->destroy(this->randomizer); + this->current_state->destroy(this->current_state); + + 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); + + /* Public functions */ + this->protected.public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; + this->protected.public.initiate_connection = (status_t(*)(ike_sa_t*,connection_t*)) initiate_connection; + this->protected.public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; + this->protected.public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host; + this->protected.public.get_other_host = (host_t*(*)(ike_sa_t*)) get_other_host; + this->protected.public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id; + this->protected.public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id; + this->protected.public.get_connection = (connection_t*(*)(ike_sa_t*)) get_connection; + this->protected.public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request; + this->protected.public.get_state = (ike_sa_state_t (*) (ike_sa_t *this)) get_state; + this->protected.public.send_delete_ike_sa_request = (void (*)(ike_sa_t*)) send_delete_ike_sa_request; + this->protected.public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status; + this->protected.public.destroy = (void(*)(ike_sa_t*))destroy; + + /* protected functions */ + this->protected.build_message = (void (*) (protected_ike_sa_t *, exchange_type_t , bool , message_t **)) build_message; + this->protected.get_prf = (prf_t *(*) (protected_ike_sa_t *)) get_prf; + this->protected.get_child_prf = (prf_t *(*) (protected_ike_sa_t *)) get_child_prf; + this->protected.get_prf_auth_i = (prf_t *(*) (protected_ike_sa_t *)) get_prf_auth_i; + this->protected.get_prf_auth_r = (prf_t *(*) (protected_ike_sa_t *)) get_prf_auth_r; + this->protected.add_child_sa = (void (*) (protected_ike_sa_t*,child_sa_t*)) add_child_sa; + this->protected.set_connection = (void (*) (protected_ike_sa_t *,connection_t *)) set_connection; + this->protected.get_connection = (connection_t *(*) (protected_ike_sa_t *)) get_connection; + this->protected.set_policy = (void (*) (protected_ike_sa_t *,policy_t *)) set_policy; + this->protected.get_policy = (policy_t *(*) (protected_ike_sa_t *)) get_policy; + this->protected.get_randomizer = (randomizer_t *(*) (protected_ike_sa_t *)) get_randomizer; + this->protected.send_request = (status_t (*) (protected_ike_sa_t *,message_t *)) send_request; + this->protected.send_response = (status_t (*) (protected_ike_sa_t *,message_t *)) send_response; + this->protected.send_notify = (void (*)(protected_ike_sa_t*,exchange_type_t,notify_message_type_t,chunk_t)) send_notify; + this->protected.build_transforms = (status_t (*) (protected_ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t)) build_transforms; + this->protected.set_new_state = (void (*) (protected_ike_sa_t *,state_t *)) set_new_state; + this->protected.get_crypter_initiator = (crypter_t *(*) (protected_ike_sa_t *)) get_crypter_initiator; + this->protected.get_signer_initiator = (signer_t *(*) (protected_ike_sa_t *)) get_signer_initiator; + this->protected.get_crypter_responder = (crypter_t *(*) (protected_ike_sa_t *)) get_crypter_responder; + this->protected.get_signer_responder = (signer_t *(*) (protected_ike_sa_t *)) get_signer_responder; + this->protected.reset_message_buffers = (void (*) (protected_ike_sa_t *)) reset_message_buffers; + this->protected.get_last_responded_message = (message_t * (*) (protected_ike_sa_t *this)) get_last_responded_message; + this->protected.get_last_requested_message = (message_t * (*) (protected_ike_sa_t *this)) get_last_requested_message; + + this->protected.set_last_replied_message_id = (void (*) (protected_ike_sa_t *,u_int32_t)) set_last_replied_message_id; + + /* private functions */ + this->resend_last_reply = resend_last_reply; + + /* initialize private fields */ + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->child_sas = linked_list_create(); + this->randomizer = randomizer_create(); + + this->last_requested_message = NULL; + this->last_responded_message = NULL; + this->message_id_out = 0; + this->message_id_in = 0; + this->last_replied_message_id = -1; + this->crypter_initiator = NULL; + this->crypter_responder = NULL; + this->signer_initiator = NULL; + this->signer_responder = NULL; + this->prf = NULL; + this->prf_auth_i = NULL; + this->prf_auth_r = NULL; + this->child_prf = NULL; + this->connection = NULL; + this->policy = NULL; + + /* at creation time, IKE_SA is in a initiator state */ + if (ike_sa_id->is_initiator(ike_sa_id)) + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Create first state_t object of type INITIATOR_INIT"); + this->current_state = (state_t *) initiator_init_create(&(this->protected)); + } + else + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Create first state_t object of type RESPONDER_INIT"); + this->current_state = (state_t *) responder_init_create(&(this->protected)); + } + return &(this->protected.public); +} diff --git a/src/charon/charon/sa/ike_sa.h b/src/charon/charon/sa/ike_sa.h new file mode 100644 index 000000000..c526c6347 --- /dev/null +++ b/src/charon/charon/sa/ike_sa.h @@ -0,0 +1,462 @@ +/** + * @file ike_sa.h + * + * @brief Interface of ike_sa_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_H_ +#define IKE_SA_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Nonce size in bytes for nonces sending to other peer. + * + * @warning Nonce size MUST be between 16 and 256 bytes. + * + * @ingroup sa + */ +#define NONCE_SIZE 16 + + +typedef struct ike_sa_t ike_sa_t; + +/** + * @brief Class ike_sa_t representing an IKE_SA. + * + * An object of this type is managed by an ike_sa_manager_t object + * and represents an IKE_SA. Message processing is split up in different states. + * They will handle all related things for the state they represent. + * + * @b Constructors: + * - ike_sa_create() + * + * @ingroup sa + */ +struct ike_sa_t { + + /** + * @brief Processes a incoming IKEv2-Message of type message_t. + * + * @param this ike_sa_t object object + * @param[in] message message_t object to process + * @return + * - SUCCESS + * - FAILED + * - DELETE_ME if this IKE_SA MUST be deleted + */ + status_t (*process_message) (ike_sa_t *this,message_t *message); + + /** + * @brief Initiate a new connection with given connection_t object. + * + * The connection_t object 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 + * @return + * - SUCCESS if initialization started + * - FAILED if in wrong state + * - DELETE_ME if initialization failed and IKE_SA MUST be deleted + */ + status_t (*initiate_connection) (ike_sa_t *this, connection_t *connection); + + /** + * @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_request) (ike_sa_t *this, u_int32_t message_id); + + /** + * @brief Sends a request to delete IKE_SA. + * + * Only supported in state IKE_SA_ESTABLISHED + * + * @param this calling object + */ + void (*send_delete_ike_sa_request) (ike_sa_t *this); + + /** + * @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 local peer address of the IKE_SA. + * + * @param this calling object + * @return local host_t + */ + host_t* (*get_my_host) (ike_sa_t *this); + + /** + * @brief Get remote peer address of the IKE_SA. + * + * @param this calling object + * @return remote host_t + */ + host_t* (*get_other_host) (ike_sa_t *this); + + /** + * @brief Get own ID of the IKE_SA. + * + * @param this calling object + * @return local identification_t + */ + identification_t* (*get_my_id) (ike_sa_t *this); + + /** + * @brief Get remote ID the IKE_SA. + * + * @param this calling object + * @return remote identification_t + */ + identification_t* (*get_other_id) (ike_sa_t *this); + + /** + * @brief Get the connection of the IKE_SA. + * + * The internal used connection specification + * can be queried to get some data of an IKE_SA. + * The connection is still owned to the IKE_SA + * and must not be manipulated. + * + * @param this calling object + * @return connection_t + */ + connection_t* (*get_connection) (ike_sa_t *this); + + /** + * @brief Get the state of type of associated state object. + * + * @param this calling object + * @return state of IKE_SA + */ + ike_sa_state_t (*get_state) (ike_sa_t *this); + + /** + * @brief Log the status of a the ike sa to a logger. + * + * The status of the IKE SA and all child SAs is logged. + * Supplying NULL as logger uses the internal child_sa logger + * to do the logging. The log is only done if the supplied + * connection name is NULL or matches the connections name. + * + * @param this calling object + * @param logger logger to use for logging + * @param name name of the connection + */ + void (*log_status) (ike_sa_t *this, logger_t *logger, char *name); + + /** + * @brief Destroys a ike_sa_t object. + * + * @param this calling object + */ + void (*destroy) (ike_sa_t *this); +}; + + +typedef struct protected_ike_sa_t protected_ike_sa_t; + +/** + * @brief Protected functions of an ike_sa_t object. + * + * This members are only accessed out from + * the various state_t implementations. + * + * @ingroup sa + */ +struct protected_ike_sa_t { + + /** + * Public interface of an ike_sa_t object. + */ + ike_sa_t public; + + /** + * @brief Build an empty IKEv2-Message and fills in default informations. + * + * Depending on the type of message (request or response), the message id is + * either message_id_out or message_id_in. + * + * Used in state_t Implementation to build an empty IKEv2-Message. + * + * @param this calling object + * @param type exchange type of new message + * @param request TRUE, if message has to be a request + * @param message new message is stored at this location + */ + void (*build_message) (protected_ike_sa_t *this, exchange_type_t type, bool request, message_t **message); + + /** + * @brief Get the internal stored connection_t object. + * + * @param this calling object + * @return pointer to the internal stored connection_t object + */ + connection_t *(*get_connection) (protected_ike_sa_t *this); + + /** + * @brief Set the internal connection object. + * + * @param this calling object + * @param connection object of type connection_t + */ + void (*set_connection) (protected_ike_sa_t *this, connection_t *connection); + + /** + * @brief Get the internal stored policy object. + * + * @param this calling object + * @return pointer to the internal stored policy_t object + */ + policy_t *(*get_policy) (protected_ike_sa_t *this); + + /** + * @brief Set the internal policy_t object. + * + * @param this calling object + * @param policy object of type policy_t + */ + void (*set_policy) (protected_ike_sa_t *this,policy_t *policy); + + /** + * @brief Derive all keys and create the transforms for IKE communication. + * + * Keys are derived using the diffie hellman secret, nonces and internal + * stored SPIs. + * Allready existing objects get destroyed. + * + * @param this calling object + * @param proposal proposal which contains algorithms to use + * @param dh diffie hellman object with shared secret + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + */ + status_t (*build_transforms) (protected_ike_sa_t *this, proposal_t* proposal, + diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r); + + /** + * @brief Send the next request message. + * + * Also the first retransmit job is created. + * + * Last stored requested message gets destroyed. Object gets not cloned! + * + * @param this calling object + * @param message pointer to the message which should be sent + * @return + * - SUCCESS + * - FAILED if message id is not next expected one + */ + status_t (*send_request) (protected_ike_sa_t *this,message_t * message); + + /** + * @brief Send the next response message. + * + * Last stored responded message gets destroyed. Object gets not cloned! + * + * @param this calling object + * @param message pointer to the message which should be sent + * return + * - SUCCESS + * - FAILED if message id is not next expected one + */ + status_t (*send_response) (protected_ike_sa_t *this,message_t * message); + + /** + * @brief Send a notify reply message. + * + * @param this calling object + * @param exchange_type type of exchange in which the notify should be wrapped + * @param type type of the notify message to send + * @param data notification data + */ + void (*send_notify) (protected_ike_sa_t *this, exchange_type_t exchange_type, notify_message_type_t type, chunk_t data); + + /** + * @brief Get the internal stored randomizer_t object. + * + * @param this calling object + * @return pointer to the internal randomizer_t object + */ + randomizer_t *(*get_randomizer) (protected_ike_sa_t *this); + + /** + * @brief Set the new state_t object of the IKE_SA object. + * + * The old state_t object gets not destroyed. It's the callers duty to + * make sure old state is destroyed (Normally the old state is the caller). + * + * @param this calling object + * @param state pointer to the new state_t object + */ + void (*set_new_state) (protected_ike_sa_t *this,state_t *state); + + /** + * @brief Set the last replied message id. + * + * @param this calling object + * @param message_id message id + */ + void (*set_last_replied_message_id) (protected_ike_sa_t *this,u_int32_t message_id); + + /** + * @brief Get the internal stored initiator crypter_t object. + * + * @param this calling object + * @return pointer to crypter_t object + */ + crypter_t *(*get_crypter_initiator) (protected_ike_sa_t *this); + + /** + * @brief Get the internal stored initiator signer_t object. + * + * @param this calling object + * @return pointer to signer_t object + */ + signer_t *(*get_signer_initiator) (protected_ike_sa_t *this); + + /** + * @brief Get the internal stored responder crypter_t object. + * + * @param this calling object + * @return pointer to crypter_t object + */ + crypter_t *(*get_crypter_responder) (protected_ike_sa_t *this); + + /** + * @brief Get the internal stored responder signer object. + * + * @param this calling object + * @return pointer to signer_t object + */ + signer_t *(*get_signer_responder) (protected_ike_sa_t *this); + + /** + * @brief Get the multi purpose prf. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf) (protected_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) (protected_ike_sa_t *this); + + /** + * @brief Get the prf used for authentication of initiator. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf_auth_i) (protected_ike_sa_t *this); + + /** + * @brief Get the prf used for authentication of responder. + * + * @param this calling object + * @return pointer to prf_t object + */ + prf_t *(*get_prf_auth_r) (protected_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) (protected_ike_sa_t *this, child_sa_t *child_sa); + + /** + * @brief Get the last responded message. + * + * @param this calling object + * @return + * - last received as message_t object + * - NULL if no last request available + */ + message_t *(*get_last_responded_message) (protected_ike_sa_t *this); + + /** + * @brief Get the last requested message. + * + * @param this calling object + * @return + * - last sent as message_t object + * - NULL if no last request available + */ + message_t *(*get_last_requested_message) (protected_ike_sa_t *this); + + /** + * @brief Resets message counters and does destroy stored received and sent messages. + * + * @param this calling object + */ + void (*reset_message_buffers) (protected_ike_sa_t *this); +}; + + +/** + * @brief Creates an ike_sa_t object with a specific ID. + * + * @warning the Content of internal ike_sa_id_t object can change over time + * e.g. when a IKE_SA_INIT has been finished. + * + * @param[in] ike_sa_id ike_sa_id_t object to associate with new IKE_SA. + * The object is internal getting cloned + * and so has to be destroyed by the caller. + * @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/charon/sa/ike_sa_id.c b/src/charon/charon/sa/ike_sa_id.c new file mode 100644 index 000000000..bf3a05d11 --- /dev/null +++ b/src/charon/charon/sa/ike_sa_id.c @@ -0,0 +1,185 @@ +/** + * @file ike_sa_id.c + * + * @brief Implementation of ike_sa_id_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_sa_id.h" + + + +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); +} + +/** + * 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/charon/sa/ike_sa_id.h b/src/charon/charon/sa/ike_sa_id.h new file mode 100644 index 000000000..0f16f7637 --- /dev/null +++ b/src/charon/charon/sa/ike_sa_id.h @@ -0,0 +1,146 @@ +/** + * @file ike_sa_id.h + * + * @brief Interface of ike_sa_id_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_ID_H_ +#define IKE_SA_ID_H_ + +#include + + +typedef struct ike_sa_id_t ike_sa_id_t; + +/** + * @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/charon/sa/ike_sa_manager.c b/src/charon/charon/sa/ike_sa_manager.c new file mode 100644 index 000000000..01f3f5ad2 --- /dev/null +++ b/src/charon/charon/sa/ike_sa_manager.c @@ -0,0 +1,812 @@ +/** + * @file ike_sa_manager.c + * + * @brief Implementation of ike_sa_mananger_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "ike_sa_manager.h" + +#include +#include +#include +#include +#include + +typedef struct ike_sa_entry_t ike_sa_entry_t; + +/** + * An entry in the linked list, contains IKE_SA, locking and lookup data. + */ +struct ike_sa_entry_t { + /** + * Destructor, also destroys associated ike_sa_t object. + */ + status_t (*destroy) (ike_sa_entry_t *this); + + /** + * 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; +}; + +/** + * Implementation of ike_sa_entry_t.destroy. + */ +static status_t ike_sa_entry_destroy(ike_sa_entry_t *this) +{ + /* also destroy IKE SA */ + this->ike_sa->destroy(this->ike_sa); + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); + return SUCCESS; +} + +/** + * @brief Creates a new entry for the ike_sa_t list. + * + * This constructor additionaly creates a new and empty SA. + * + * @param ike_sa_id The associated ike_sa_id_t, will be cloned + * @return ike_sa_entry_t object + */ +static ike_sa_entry_t *ike_sa_entry_create(ike_sa_id_t *ike_sa_id) +{ + ike_sa_entry_t *this = malloc_thing(ike_sa_entry_t); + + /* destroy function */ + this->destroy = ike_sa_entry_destroy; + + 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; + + /* 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; + + /** + * @brief Get next spi. + * + * We give out SPIs incremental starting at 1. + * + * @param this the ike_sa_manager + * @return the next spi + */ + u_int64_t (*get_next_spi) (private_ike_sa_manager_t *this); + + /** + * @brief Find the ike_sa_entry_t object in the list by SPIs. + * + * This function simply iterates over the linked list. A hash-table + * would be more efficient when storing a lot of IKE_SAs... + * + * @param this calling object + * @param ike_sa_id id of the ike_sa, containing SPIs + * @param[out] entry pointer to set to the found entry + * @return + * - SUCCESS when found, + * - NOT_FOUND when no such ike_sa_id in list + */ + status_t (*get_entry_by_id) (private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_entry_t **entry); + + /** + * @brief Find the ike_sa_entry_t in the list by pointer to SA. + * + * This function simply iterates over the linked list. A hash-table + * would be more efficient when storing a lot of IKE_SAs... + * + * @param this calling object + * @param ike_sa pointer to the ike_sa + * @param[out] entry pointer to set to the found entry + * @return + * - SUCCESS when found, + * - NOT_FOUND when no such ike_sa_id in list + */ + status_t (*get_entry_by_sa) (private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry); + + /** + * @brief Felete an entry from the linked list. + * + * @param this calling object + * @param entry entry to delete + * @return + * - SUCCESS when found, + * - NOT_FOUND when no such ike_sa_id in list + */ + status_t (*delete_entry) (private_ike_sa_manager_t *this, ike_sa_entry_t *entry); + + /** + * Lock for exclusivly accessing the manager. + */ + pthread_mutex_t mutex; + + /** + * Logger used for this IKE SA Manager. + */ + logger_t *logger; + + /** + * Linked list with entries for the ike_sa_t objects. + */ + linked_list_t *ike_sa_list; + + /** + * Next SPI, needed for incremental creation of SPIs. + */ + u_int64_t next_spi; +}; + +/** + * 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, ike_sa_entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + status_t status; + + /* create iterator over list of ike_sa's */ + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + + iterator->current(iterator, (void**)¤t); + if (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)) + && (ike_sa_id->is_initiator(ike_sa_id) == current->ike_sa_id->is_initiator(current->ike_sa_id))) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by initiator spi %d",ike_sa_id->get_initiator_spi(ike_sa_id)); + *entry = current; + status = SUCCESS; + break; + } + } + else if (ike_sa_id->get_responder_spi(ike_sa_id) == 0) + { + if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == ike_sa_id->get_initiator_spi(ike_sa_id)) + && (ike_sa_id->is_initiator(ike_sa_id) == current->ike_sa_id->is_initiator(current->ike_sa_id))) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by initiator spi %d",ike_sa_id->get_initiator_spi(ike_sa_id)); + *entry = current; + status = SUCCESS; + break; + } + } + if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by full ID"); + *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, ike_sa_entry_t **entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + /* default status */ + status = NOT_FOUND; + + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + iterator->current(iterator, (void**)¤t); + /* only pointers are compared */ + if (current->ike_sa == ike_sa) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"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, ike_sa_entry_t *entry) +{ + linked_list_t *list = this->ike_sa_list; + iterator_t *iterator; + status_t status; + + iterator = list->create_iterator(list, TRUE); + + status = NOT_FOUND; + + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + iterator->current(iterator, (void**)¤t); + if (current == entry) + { + this->logger->log(this->logger,CONTROL | LEVEL2,"Found entry by pointer. Going to delete it."); + iterator->remove(iterator); + entry->destroy(entry); + status = SUCCESS; + break; + } + } + iterator->destroy(iterator); + return status; +} + + +/** + * Implementation of private_ike_sa_manager_t.get_next_spi. + */ +static u_int64_t get_next_spi(private_ike_sa_manager_t *this) +{ + this->next_spi++; + if (this->next_spi == 0) { + /* TODO handle overflow, + * delete all SAs or so + */ + } + return this->next_spi; +} + +/** + * Implementation of of ike_sa_manager.create_and_checkout. + */ +static void create_and_checkout(private_ike_sa_manager_t *this,ike_sa_t **ike_sa) +{ + u_int64_t initiator_spi; + ike_sa_entry_t *new_ike_sa_entry; + ike_sa_id_t *new_ike_sa_id; + + initiator_spi = this->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_ike_sa_entry = ike_sa_entry_create(new_ike_sa_id); + new_ike_sa_id->destroy(new_ike_sa_id); + + /* each access is locked */ + pthread_mutex_lock(&(this->mutex)); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry); + + /* check ike_sa out */ + this->logger->log(this->logger,CONTROL | LEVEL1 ,"New IKE_SA created and added to list of known IKE_SA's"); + new_ike_sa_entry->checked_out = TRUE; + *ike_sa = new_ike_sa_entry->ike_sa; + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implementation of of ike_sa_manager.checkout. + */ +static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_t **ike_sa) +{ + bool responder_spi_set; + bool initiator_spi_set; + bool original_initiator; + status_t retval; + + /* each access is locked */ + pthread_mutex_lock(&(this->mutex)); + + responder_spi_set = (FALSE != ike_sa_id->get_responder_spi(ike_sa_id)); + initiator_spi_set = (FALSE != ike_sa_id->get_initiator_spi(ike_sa_id)); + original_initiator = ike_sa_id->is_initiator(ike_sa_id); + + if ((initiator_spi_set && responder_spi_set) || + ((initiator_spi_set && !responder_spi_set) && (original_initiator))) + { + /* we SHOULD have an IKE_SA for these SPIs in the list, + * if not, we can't handle the request... + */ + ike_sa_entry_t *entry; + /* look for the entry */ + if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + /* can we give this ike_sa out to new requesters?*/ + if (entry->driveout_new_threads) + { + this->logger->log(this->logger,CONTROL|LEVEL1,"Drive out new thread for existing IKE_SA"); + /* no we can't */ + retval = NOT_FOUND; + } + else + { + /* is this IKE_SA already checked out ?? + * are we welcome to get this SA ? */ + 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, go home */ + if (entry->driveout_waiting_threads) + { + /* we must signal here, others are interested that we leave */ + pthread_cond_signal(&(entry->condvar)); + this->logger->log(this->logger,CONTROL|LEVEL1,"Drive out waiting thread for existing IKE_SA"); + retval = NOT_FOUND; + } + else + { + this->logger->log(this->logger,CONTROL|LEVEL2,"IKE SA successfully checked out"); + /* ok, this IKE_SA is finally ours */ + entry->checked_out = TRUE; + *ike_sa = entry->ike_sa; + /* DON'T use return, we must unlock the mutex! */ + retval = SUCCESS; + } + } + } + else + { + this->logger->log(this->logger,ERROR | LEVEL1,"IKE SA not stored in known IKE_SA list"); + /* looks like there is no such IKE_SA, better luck next time... */ + /* DON'T use return, we must unlock the mutex! */ + retval = NOT_FOUND; + } + } + else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator)) + { + /* an IKE_SA_INIT from an another endpoint, + * he is the initiator. + * For simplicity, we do NOT check for retransmitted + * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- + * Request (even a retransmitted one) will result in a + * IKE_SA. This could be improved... + */ + u_int64_t responder_spi; + ike_sa_entry_t *new_ike_sa_entry; + + + /* set SPIs, we are the responder */ + responder_spi = this->get_next_spi(this); + + /* we also set arguments spi, so its still valid */ + ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); + + /* create entry */ + new_ike_sa_entry = ike_sa_entry_create(ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_ike_sa_entry); + + /* check ike_sa out */ + this->logger->log(this->logger,CONTROL | LEVEL1 ,"IKE_SA added to list of known IKE_SA's"); + new_ike_sa_entry->checked_out = TRUE; + *ike_sa = new_ike_sa_entry->ike_sa; + + retval = CREATED; + } + else + { + /* responder set, initiator not: here is something seriously wrong! */ + this->logger->log(this->logger,ERROR | LEVEL1, "Invalid IKE_SA SPI's"); + /* DON'T use return, we must unlock the mutex! */ + retval = INVALID_ARG; + } + + pthread_mutex_unlock(&(this->mutex)); + /* OK, unlocked... */ + return retval; +} + +/** + * Implementation of of ike_sa_manager.checkout_by_hosts. + */ +static status_t checkout_by_hosts(private_ike_sa_manager_t *this, host_t *me, host_t *other, ike_sa_t **ike_sa) +{ + iterator_t *iterator; + ike_sa_id_t *ike_sa_id = NULL; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + host_t *sa_me, *sa_other; + + iterator->current(iterator, (void**)¤t); + sa_me = current->ike_sa->get_my_host(current->ike_sa); + sa_other = current->ike_sa->get_other_host(current->ike_sa); + + /* one end may be default/any, but not both */ + if (me->is_default_route(me)) + { + if (other->is_default_route(other)) + { + break; + } + if (other->equals(other, sa_other)) + { + /* other matches */ + ike_sa_id = current->ike_sa_id; + } + } + else if (other->is_default_route(other)) + { + if (me->equals(me, sa_me)) + { + /* ME matches */ + ike_sa_id = current->ike_sa_id; + } + } + else + { + if (me->equals(me, sa_me) && other->equals(other, sa_other)) + { + /* both matches */ + ike_sa_id = current->ike_sa_id; + } + } + } + iterator->destroy(iterator); + pthread_mutex_unlock(&(this->mutex)); + + if (ike_sa_id) + { + /* checkout is done in the checkout function, since its rather complex */ + return checkout(this, ike_sa_id, ike_sa); + } + return NOT_FOUND; +} + +/** + * Implementation of ike_sa_manager_t.get_ike_sa_list. + */ +linked_list_t *get_ike_sa_list(private_ike_sa_manager_t* this) +{ + linked_list_t *list; + iterator_t *iterator; + + pthread_mutex_lock(&(this->mutex)); + + list = linked_list_create(); + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *entry; + iterator->current(iterator, (void**)&entry); + list->insert_last(list, (void*)entry->ike_sa_id->clone(entry->ike_sa_id)); + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); + return list; +} + +/** + * Implementation of ike_sa_manager_t.log_status. + */ +static void log_status(private_ike_sa_manager_t* this, logger_t* logger, char* name) +{ + iterator_t *iterator; + + pthread_mutex_lock(&(this->mutex)); + + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *entry; + iterator->current(iterator, (void**)&entry); + entry->ike_sa->log_status(entry->ike_sa, logger, name); + } + iterator->destroy(iterator); + + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * 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; + ike_sa_entry_t *entry; + + pthread_mutex_lock(&(this->mutex)); + + /* look for the entry */ + if (this->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; + this->logger->log(this->logger,CONTROL | LEVEL1,"Checkin of IKE_SA successful."); + pthread_cond_signal(&(entry->condvar)); + retval = SUCCESS; + } + else + { + this->logger->log(this->logger,ERROR,"Fatal Error: Tried to checkin nonexisting IKE_SA"); + /* this SA is no more, this REALLY should not happen */ + retval = NOT_FOUND; + } + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + + +/** + * Implementation of ike_sa_manager_t.checkin_and_delete. + */ +static status_t checkin_and_delete(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. + */ + ike_sa_entry_t *entry; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* mark it, so now new threads can acquire this SA */ + entry->driveout_new_threads = TRUE; + /* additionaly, drive out waiting threads */ + entry->driveout_waiting_threads = TRUE; + + /* wait until all workers have done their work */ + while (entry->waiting_threads > 0) + { + /* let the other threads do some work*/ + pthread_cond_signal(&(entry->condvar)); + /* and the nice thing, they will wake us again when their work is done */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + /* ok, we are alone now, no threads waiting in the entry's condvar */ + this->delete_entry(this, entry); + this->logger->log(this->logger,CONTROL | LEVEL1,"Checkin and delete of IKE_SA successful"); + retval = SUCCESS; + } + else + { + this->logger->log(this->logger,ERROR,"Fatal Error: Tried to checkin and delete nonexisting IKE_SA"); + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + +/** + * Implementation of ike_sa_manager_t.delete. + */ +static status_t delete(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + /* 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. + */ + ike_sa_entry_t *entry; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + /* mark it, so now new threads can acquire this SA */ + entry->driveout_new_threads = TRUE; + + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_signal(&(entry->condvar)); + /* and the nice thing, they will wake us again when their work is done */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + /* ok, we are alone now, no threads waiting in the entry's condvar */ + this->delete_entry(this, entry); + this->logger->log(this->logger,CONTROL | LEVEL1,"Delete of IKE_SA successful"); + retval = SUCCESS; + } + else + { + this->logger->log(this->logger,ERROR,"Fatal Error: Tried to delete nonexisting IKE_SA"); + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + +/** + * 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; + ike_sa_entry_t *entry; + + pthread_mutex_lock(&(this->mutex)); + + this->logger->log(this->logger,CONTROL | LEVEL1,"Going to destroy IKE_SA manager and all managed IKE_SA's"); + + /* Step 1: drive out all waiting threads */ + iterator = list->create_iterator(list, TRUE); + + this->logger->log(this->logger,CONTROL | LEVEL2,"Set driveout flags for all stored IKE_SA's"); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&entry); + /* do not accept new threads, drive out waiting threads */ + entry->driveout_new_threads = TRUE; + entry->driveout_waiting_threads = TRUE; + } + + this->logger->log(this->logger,CONTROL | LEVEL2,"Wait for all threads to leave IKE_SA's"); + /* Step 2: wait until all are gone */ + iterator->reset(iterator); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&entry); + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_signal(&(entry->condvar)); + /* go sleeping until they are gone */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + } + this->logger->log(this->logger,CONTROL | LEVEL2,"Delete all IKE_SA's"); + /* Step 3: delete all entries */ + iterator->destroy(iterator); + + while (list->get_count(list) > 0) + { + list->get_first(list, (void**)&entry); + this->delete_entry(this, entry); + } + list->destroy(list); + this->logger->log(this->logger,CONTROL | LEVEL2,"IKE_SA's deleted"); + pthread_mutex_unlock(&(this->mutex)); + + 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.create_and_checkout = (void(*)(ike_sa_manager_t*,ike_sa_t**))create_and_checkout; + this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t*,ike_sa_t**))checkout; + this->public.checkout_by_hosts = (status_t(*)(ike_sa_manager_t*,host_t*,host_t*,ike_sa_t**))checkout_by_hosts; + this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list; + this->public.log_status = (void(*)(ike_sa_manager_t*,logger_t*,char*))log_status; + this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; + this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete; + this->public.checkin_and_delete = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_delete; + + /* initialize private functions */ + this->get_next_spi = get_next_spi; + this->get_entry_by_sa = get_entry_by_sa; + this->get_entry_by_id = get_entry_by_id; + this->delete_entry = delete_entry; + + /* initialize private variables */ + this->logger = logger_manager->get_logger(logger_manager, IKE_SA_MANAGER); + + this->ike_sa_list = linked_list_create(); + + pthread_mutex_init(&(this->mutex), NULL); + + this->next_spi = 0; + + return (ike_sa_manager_t*)this; +} diff --git a/src/charon/charon/sa/ike_sa_manager.h b/src/charon/charon/sa/ike_sa_manager.h new file mode 100644 index 000000000..e2235b4b6 --- /dev/null +++ b/src/charon/charon/sa/ike_sa_manager.h @@ -0,0 +1,185 @@ +/** + * @file ike_sa_manager.h + * + * @brief Interface of ike_sa_manager_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_MANAGER_H_ +#define IKE_SA_MANAGER_H_ + +#include +#include +#include + + +typedef struct ike_sa_manager_t ike_sa_manager_t; + +/** + * @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 IKE_SA, create it when necesarry. + * + * Checks out a SA by its ID. An SA will be created, when: + * - Responder SPI is not set (when received an IKE_SA_INIT from initiator) + * Management of SPIs is the managers job, he will set it. + * This function blocks until SA is available for checkout. + * + * @warning checking out two times without checking in will + * result in a deadlock! + * + * @param this the manager object + * @param ike_sa_id[in/out] the SA identifier, will be updated + * @param ike_sa[out] checked out SA + * @returns + * - SUCCESS if checkout successful + * - NOT_FOUND when no such SA is available + * - CREATED if a new IKE_SA got created + */ + status_t (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id, ike_sa_t **ike_sa); + + /** + * @brief Create and checkout an IKE_SA as original initator. + * + * Creates and checks out a SA as initiator. + * Management of SPIs is the managers job, he will set it. + * + * @param this the manager object + * @param ike_sa[out] checked out SA + */ + void (*create_and_checkout) (ike_sa_manager_t* this,ike_sa_t **ike_sa); + + /** + * @brief Check out an IKE_SA, defined be the two peers. + * + * Checking out an IKE_SA by their peer addresses may be necessary + * for kernel traps, status querying and so on... one of the hosts + * may be 0.0.0.0 (defaultroute/any), but not both. + * + * @param this the manager object + * @param me host on local side + * @param other host on remote side + * @param ike_sa[out] checked out SA + * @return + * - NOT_FOUND, if no such SA found + * - SUCCESS, if SA found and ike_sa set appropriatly + */ + status_t (*checkout_by_hosts) (ike_sa_manager_t* this, host_t *me, host_t *other, ike_sa_t **ike_sa); + + /** + * @brief Get a list of all IKE_SA SAs currently set up. + * + * The resulting list with all IDs must be destroyd by + * the caller. There is no guarantee an ike_sa with the + * corrensponding ID really exists, since it may be deleted + * in the meantime by another thread. + * + * @param this the manager object + * @return a list with ike_sa_id_t s + */ + linked_list_t *(*get_ike_sa_list) (ike_sa_manager_t* this); + + /** + * @brief Log the status of the IKE_SA's in the manager. + * + * A informational log is done to the supplied logger. If logger is + * NULL, an internal logger is used. If a name is supplied, + * only connections with the matching name will be logged. + * + * @param this the manager object + * @param logger logger to do the log, or NULL + * @param name name of a connection, or NULL + */ + void (*log_status) (ike_sa_manager_t* this, logger_t* logger, char* name); + + /** + * @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[in/out] the SA identifier, will be updated + * @param ike_sa[out] 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 Delete a SA, which was not checked out. + * + * @warning do not use this when the SA is already checked out, this will + * deadlock! + * + * @param this the manager object + * @param ike_sa_id[in/out] the SA identifier + * @returns + * - SUCCESS if found + * - NOT_FOUND when no such SA is available + */ + status_t (*delete) (ike_sa_manager_t* this, ike_sa_id_t *ike_sa_id); + + /** + * @brief Delete a checked out 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_delete) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + + /** + * @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(); + +#endif /*IKE_SA_MANAGER_H_*/ diff --git a/src/charon/charon/sa/states/Makefile.states b/src/charon/charon/sa/states/Makefile.states new file mode 100644 index 000000000..a258ebef0 --- /dev/null +++ b/src/charon/charon/sa/states/Makefile.states @@ -0,0 +1,43 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +STATES_DIR= $(SA_DIR)states/ + +CHARON_OBJS+= $(BUILD_DIR)ike_auth_requested.o +$(BUILD_DIR)ike_auth_requested.o : $(STATES_DIR)ike_auth_requested.c $(STATES_DIR)ike_auth_requested.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_established.o +$(BUILD_DIR)ike_sa_established.o : $(STATES_DIR)ike_sa_established.c $(STATES_DIR)ike_sa_established.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_init_requested.o +$(BUILD_DIR)ike_sa_init_requested.o : $(STATES_DIR)ike_sa_init_requested.c $(STATES_DIR)ike_sa_init_requested.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)ike_sa_init_responded.o +$(BUILD_DIR)ike_sa_init_responded.o : $(STATES_DIR)ike_sa_init_responded.c $(STATES_DIR)ike_sa_init_responded.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)initiator_init.o +$(BUILD_DIR)initiator_init.o : $(STATES_DIR)initiator_init.c $(STATES_DIR)initiator_init.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)responder_init.o +$(BUILD_DIR)responder_init.o : $(STATES_DIR)responder_init.c $(STATES_DIR)responder_init.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)state.o +$(BUILD_DIR)state.o : $(STATES_DIR)state.c $(STATES_DIR)state.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/charon/sa/states/ike_auth_requested.c b/src/charon/charon/sa/states/ike_auth_requested.c new file mode 100644 index 000000000..3d49f440f --- /dev/null +++ b/src/charon/charon/sa/states/ike_auth_requested.c @@ -0,0 +1,671 @@ +/** + * @file ike_auth_requested.c + * + * @brief Implementation of ike_auth_requested_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "ike_auth_requested.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct private_ike_auth_requested_t private_ike_auth_requested_t; + +/** + * Private data of a ike_auth_requested_t object. + * + */ +struct private_ike_auth_requested_t { + /** + * Public interface of ike_auth_requested_t. + */ + ike_auth_requested_t public; + + /** + * Assigned IKE_SA. + */ + protected_ike_sa_t *ike_sa; + + /** + * SA config, just a copy of the one stored in the ike_sa. + */ + policy_t *policy; + + /** + * Received nonce from responder. + */ + chunk_t received_nonce; + + /** + * Sent nonce in IKE_SA_INIT request. + */ + chunk_t sent_nonce; + + /** + * IKE_SA_INIT-Request in binary form. + */ + chunk_t ike_sa_init_reply_data; + + /** + * Proposal to setup CHILD_SA + */ + proposal_t *proposal; + + /** + * Traffic selectors applicable at our site + */ + linked_list_t *my_ts; + + /** + * Traffic selectors applicable at remote site + */ + linked_list_t *other_ts; + + /** + * Child sa created in ike_sa_init_requested + */ + child_sa_t *child_sa; + + /** + * Assigned Logger. + * + * Is logger of ike_sa! + */ + logger_t *logger; + + /** + * Process the IDr payload (check if other id is valid) + * + * @param this calling object + * @param idr_payload ID payload of responder + * @return + * - SUCCESS + * - DELETE_ME + */ + status_t (*process_idr_payload) (private_ike_auth_requested_t *this, id_payload_t *idr_payload); + + /** + * Process the SA payload (check if selected proposals are valid, setup child sa) + * + * @param this calling object + * @param sa_payload SA payload of responder + * + * - SUCCESS + * - DELETE_ME + */ + status_t (*process_sa_payload) (private_ike_auth_requested_t *this, sa_payload_t *sa_payload); + + /** + * Process the AUTH payload (check authenticity of message) + * + * @param this calling object + * @param auth_payload AUTH payload of responder + * @param other_id_payload ID payload of responder + * + * - SUCCESS + * - DELETE_ME + */ + status_t (*process_auth_payload) (private_ike_auth_requested_t *this, auth_payload_t *auth_payload, id_payload_t *other_id_payload); + + /** + * Process the TS payload (check if selected traffic selectors are valid) + * + * @param this calling object + * @param ts_initiator TRUE if TS payload is TSi, FALSE for TSr + * @param ts_payload TS payload of responder + * + * - SUCCESS + * - DELETE_ME + */ + status_t (*process_ts_payload) (private_ike_auth_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload); + + /** + * Process a notify payload + * + * @param this calling object + * @param notify_payload notify payload + * + * - SUCCESS + * - FAILED + * - DELETE_ME + */ + status_t (*process_notify_payload) (private_ike_auth_requested_t *this, notify_payload_t *notify_payload); + + /** + * Destroy function called internally of this class after state change to + * state IKE_SA_ESTABLISHED succeeded. + * + * This destroy function does not destroy objects which were passed to the new state. + * + * @param this calling object + */ + void (*destroy_after_state_change) (private_ike_auth_requested_t *this); +}; + + +/** + * Implements state_t.process_message + */ +static status_t process_message(private_ike_auth_requested_t *this, message_t *ike_auth_reply) +{ + ts_payload_t *tsi_payload = NULL, *tsr_payload = NULL; + id_payload_t *idr_payload = NULL; + auth_payload_t *auth_payload = NULL; + sa_payload_t *sa_payload = NULL; + iterator_t *payloads = NULL; + crypter_t *crypter = NULL; + signer_t *signer = NULL; + status_t status; + host_t *my_host, *other_host; + chunk_t seed; + prf_plus_t *prf_plus; + connection_t *connection; + + if (ike_auth_reply->get_exchange_type(ike_auth_reply) != IKE_AUTH) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_auth_requested", + mapping_find(exchange_type_m,ike_auth_reply->get_exchange_type(ike_auth_reply))); + return FAILED; + } + + if (ike_auth_reply->get_request(ike_auth_reply)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH requests not allowed state ike_sa_init_responded"); + return FAILED; + } + + /* get signer for verification and crypter for decryption */ + signer = this->ike_sa->get_signer_responder(this->ike_sa); + crypter = this->ike_sa->get_crypter_responder(this->ike_sa); + + /* parse incoming message */ + status = ike_auth_reply->parse_body(ike_auth_reply, crypter, signer); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply decryption failed. Ignoring message"); + return status; + } + + this->policy = this->ike_sa->get_policy(this->ike_sa); + + /* we collect all payloads, which are processed later. Notify's are processed + * in place, since we don't know how may are there. + */ + payloads = ike_auth_reply->get_payload_iterator(ike_auth_reply); + while (payloads->has_next(payloads)) + { + payload_t *payload; + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + case AUTHENTICATION: + { + auth_payload = (auth_payload_t*)payload; + break; + } + case ID_RESPONDER: + { + idr_payload = (id_payload_t*)payload; + break; + } + case SECURITY_ASSOCIATION: + { + sa_payload = (sa_payload_t*)payload; + break; + } + case TRAFFIC_SELECTOR_INITIATOR: + { + tsi_payload = (ts_payload_t*)payload; + break; + } + case TRAFFIC_SELECTOR_RESPONDER: + { + tsr_payload = (ts_payload_t*)payload; + break; + } + case NOTIFY: + { + notify_payload_t *notify_payload = (notify_payload_t *) payload; + /* handle the notify directly, abort if no further processing required */ + status = this->process_notify_payload(this, notify_payload); + if (status != SUCCESS) + { + payloads->destroy(payloads); + return status; + } + } + case CERTIFICATE: + { + /* TODO handle cert payloads */ + } + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring Payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload)); + break; + } + } + } + /* iterator can be destroyed */ + payloads->destroy(payloads); + + /* check if we have all payloads */ + if (!(idr_payload && sa_payload && auth_payload && tsi_payload && tsr_payload)) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA"); + return DELETE_ME; + } + + /* process all payloads */ + status = this->process_idr_payload(this, idr_payload); + if (status != SUCCESS) + { + return status; + } + status = this->process_auth_payload(this, auth_payload,idr_payload); + if (status != SUCCESS) + { + return status; + } + status = this->process_sa_payload(this, sa_payload); + if (status != SUCCESS) + { + return status; + } + status = this->process_ts_payload(this, TRUE, tsi_payload); + if (status != SUCCESS) + { + return status; + } + status = this->process_ts_payload(this, FALSE, tsr_payload); + if (status != SUCCESS) + { + return status; + } + + /* install child SAs for AH and esp */ + if (!this->child_sa) + { + this->logger->log(this->logger, CONTROL, "No CHILD_SA requested, no CHILD_SA built"); + } + if (!this->proposal) + { + this->logger->log(this->logger, CONTROL, "Proposal negotiation failed, no CHILD_SA built"); + this->child_sa->destroy(this->child_sa); + this->child_sa = NULL; + } + else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0) + { + this->logger->log(this->logger, CONTROL, "Traffic selector negotiation failed, no CHILD_SA built"); + this->child_sa->destroy(this->child_sa); + this->child_sa = NULL; + } + else + { + seed = chunk_alloc(this->sent_nonce.len + this->received_nonce.len); + memcpy(seed.ptr, this->sent_nonce.ptr, this->sent_nonce.len); + memcpy(seed.ptr + this->sent_nonce.len, this->received_nonce.ptr, this->received_nonce.len); + prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + chunk_free(&seed); + + status = this->child_sa->update(this->child_sa, this->proposal, prf_plus); + prf_plus->destroy(prf_plus); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA"); + return DELETE_ME; + } + status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy! Deleting IKE_SA"); + return DELETE_ME; + } + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + } + + this->ike_sa->set_last_replied_message_id(this->ike_sa,ike_auth_reply->get_message_id(ike_auth_reply)); + + /* create new state */ + this->ike_sa->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa)); + this->destroy_after_state_change(this); + + connection = this->ike_sa->get_connection(this->ike_sa); + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + this->logger->log(this->logger, AUDIT, "IKE_SA established between %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + + return SUCCESS; +} + +/** + * Implements private_ike_auth_requested_t.process_idr_payload + */ +static status_t process_idr_payload(private_ike_auth_requested_t *this, id_payload_t *idr_payload) +{ + identification_t *other_id, *configured_other_id; + connection_t *connection; + + other_id = idr_payload->get_identification(idr_payload); + configured_other_id = this->policy->get_other_id(this->policy); + + this->logger->log(this->logger, CONTROL|LEVEL1, "configured ID: %s, ID of responder: %s", + configured_other_id->get_string(configured_other_id), + other_id->get_string(other_id)); + + if (!other_id->belongs_to(other_id, configured_other_id)) + { + other_id->destroy(other_id); + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a not acceptable ID. Deleting IKE_SA"); + return DELETE_ME; + } + + connection = this->ike_sa->get_connection(this->ike_sa); + connection->update_other_id(connection, other_id->clone(other_id)); + + this->policy->update_other_id(this->policy, other_id); + return SUCCESS; +} + +/** + * Implements private_ike_auth_requested_t.process_sa_payload + */ +static status_t process_sa_payload(private_ike_auth_requested_t *this, sa_payload_t *sa_payload) +{ + proposal_t *proposal, *proposal_tmp; + linked_list_t *proposal_list; + + /* get his selected proposal */ + proposal_list = sa_payload->get_proposals(sa_payload); + /* check count of proposals */ + if (proposal_list->get_count(proposal_list) == 0) + { + /* no proposal? we accept this, but no child sa is built */ + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply's SA_PAYLOAD didn't contain any proposals. No CHILD_SA created", + proposal_list->get_count(proposal_list)); + proposal_list->destroy(proposal_list); + return SUCCESS; + } + if (proposal_list->get_count(proposal_list) > 1) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply's SA_PAYLOAD contained %d proposal. Deleting IKE_SA", + proposal_list->get_count(proposal_list)); + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + return DELETE_ME; + } + + /* we have to re-check here if other's selection is valid */ + proposal = this->policy->select_proposal(this->policy, proposal_list); + /* list not needed anymore */ + while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS) + { + proposal_tmp->destroy(proposal_tmp); + } + proposal_list->destroy(proposal_list); + /* got a match? */ + if (proposal == NULL) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a not offered proposal. Deleting IKE_SA"); + return DELETE_ME; + } + + /* apply proposal */ + this->proposal = proposal; + + return SUCCESS; +} + +/** + * Implements private_ike_auth_requested_t.process_auth_payload + */ +static status_t process_auth_payload(private_ike_auth_requested_t *this, auth_payload_t *auth_payload, id_payload_t *other_id_payload) +{ + authenticator_t *authenticator; + status_t status; + + authenticator = authenticator_create(this->ike_sa); + status = authenticator->verify_auth_data(authenticator,auth_payload,this->ike_sa_init_reply_data,this->sent_nonce,other_id_payload,FALSE); + authenticator->destroy(authenticator); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Verification of IKE_AUTH reply failed. Deleting IKE_SA"); + return DELETE_ME; + } + + this->logger->log(this->logger, CONTROL|LEVEL1, "AUTH data verified successfully"); + return SUCCESS; +} + +/** + * Implements private_ike_auth_requested_t.process_ts_payload + */ +static status_t process_ts_payload(private_ike_auth_requested_t *this, bool ts_initiator, ts_payload_t *ts_payload) +{ + linked_list_t *ts_received, *ts_selected; + traffic_selector_t *ts; + + /* get ts form payload */ + ts_received = ts_payload->get_traffic_selectors(ts_payload); + /* select ts depending on payload type */ + if (ts_initiator) + { + ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received); + this->my_ts = ts_selected; + } + else + { + ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received); + this->other_ts = ts_selected; + } + /* check if the responder selected valid proposals */ + if (ts_selected->get_count(ts_selected) != ts_received->get_count(ts_received)) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained not offered traffic selectors."); + } + + /* cleanup */ + while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + ts_received->destroy(ts_received); + + return SUCCESS; +} + +/** + * Implements private_ike_auth_requested_t.process_notify_payload + */ +static status_t process_notify_payload(private_ike_auth_requested_t *this, notify_payload_t *notify_payload) +{ + notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s", + mapping_find(notify_message_type_m, notify_message_type)); + + switch (notify_message_type) + { + case INVALID_SYNTAX: + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an INVALID_SYNTAX notify. Deleting IKE_SA"); + return DELETE_ME; + + } + case AUTHENTICATION_FAILED: + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an AUTHENTICATION_FAILED notify. Deleting IKE_SA"); + return DELETE_ME; + + } + case SINGLE_PAIR_REQUIRED: + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained a SINGLE_PAIR_REQUIRED notify. Deleting IKE_SA"); + return DELETE_ME; + } + default: + { + /* + * - In case of unknown error: IKE_SA gets destroyed. + * - In case of unknown status: logging + */ + + if (notify_message_type < 16383) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply contained an unknown notify error (%d). Deleting IKE_SA", + notify_message_type); + return DELETE_ME; + + } + else + { + this->logger->log(this->logger, CONTROL, "IKE_AUTH reply contained an unknown notify (%d), ignored.", + notify_message_type); + return SUCCESS; + } + } + } +} + +/** + * Implements state_t.get_state + */ +static ike_sa_state_t get_state(private_ike_auth_requested_t *this) +{ + return IKE_AUTH_REQUESTED; +} + +/** + * Implements state_t.get_state + */ +static void destroy(private_ike_auth_requested_t *this) +{ + chunk_free(&(this->received_nonce)); + chunk_free(&(this->sent_nonce)); + chunk_free(&(this->ike_sa_init_reply_data)); + if (this->child_sa) + { + this->child_sa->destroy(this->child_sa); + } + if (this->my_ts) + { + traffic_selector_t *ts; + while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->my_ts->destroy(this->my_ts); + } + if (this->other_ts) + { + traffic_selector_t *ts; + while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->other_ts->destroy(this->other_ts); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + free(this); +} +/** + * Implements protected_ike_sa_t.destroy_after_state_change + */ +static void destroy_after_state_change(private_ike_auth_requested_t *this) +{ + chunk_free(&(this->received_nonce)); + chunk_free(&(this->sent_nonce)); + chunk_free(&(this->ike_sa_init_reply_data)); + if (this->my_ts) + { + traffic_selector_t *ts; + while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->my_ts->destroy(this->my_ts); + } + if (this->other_ts) + { + traffic_selector_t *ts; + while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->other_ts->destroy(this->other_ts); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + free(this); +} + +/* + * Described in header. + */ +ike_auth_requested_t *ike_auth_requested_create(protected_ike_sa_t *ike_sa,chunk_t sent_nonce,chunk_t received_nonce,chunk_t ike_sa_init_reply_data, child_sa_t *child_sa) +{ + private_ike_auth_requested_t *this = malloc_thing(private_ike_auth_requested_t); + + /* interface functions */ + this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message; + this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state; + this->public.state_interface.destroy = (void (*) (state_t *)) destroy; + + /* private functions */ + this->process_idr_payload = process_idr_payload; + this->process_sa_payload = process_sa_payload; + this->process_auth_payload = process_auth_payload; + this->process_ts_payload = process_ts_payload; + this->process_notify_payload = process_notify_payload; + this->destroy_after_state_change = destroy_after_state_change; + + /* private data */ + this->ike_sa = ike_sa; + this->received_nonce = received_nonce; + this->sent_nonce = sent_nonce; + this->ike_sa_init_reply_data = ike_sa_init_reply_data; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + this->my_ts = NULL; + this->other_ts = NULL; + this->proposal = NULL; + this->child_sa = child_sa; + + return &(this->public); +} diff --git a/src/charon/charon/sa/states/ike_auth_requested.h b/src/charon/charon/sa/states/ike_auth_requested.h new file mode 100644 index 000000000..a8eef014c --- /dev/null +++ b/src/charon/charon/sa/states/ike_auth_requested.h @@ -0,0 +1,72 @@ +/** + * @file ike_auth_requested.h + * + * @brief Interface of ike_auth_requested_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_REQUESTED_H_ +#define IKE_AUTH_REQUESTED_H_ + +#include +#include + + +typedef struct ike_auth_requested_t ike_auth_requested_t; + +/** + * @brief This class represents an IKE_SA, which has requested an IKE_AUTH. + * + * The state accpets IKE_AUTH responses. It proves the authenticity + * and sets up the first child sa. After that, it changes IKE_SA state to + * IKE_SA_ESTABLISHED. + * + * @ Constructors: + * - ike_auth_requested_create() + * + * @todo handle certificate payloads + * + * @ingroup states + */ +struct ike_auth_requested_t { + /** + * The state_t interface. + */ + state_t state_interface; + +}; + +/** + * Constructor of class ike_auth_requested_t + * + * @param ike_sa assigned ike_sa object + * @param sent_nonce Sent nonce value in IKE_SA_INIT request + * @param received_nonce Received nonce value in IKE_SA_INIT response + * @param ike_sa_init_reply_data binary representation of IKE_SA_INIT reply + * @param child_sa opened but not completed child_sa + * @return created ike_auth_requested_t object + * + * @ingroup states + */ +ike_auth_requested_t *ike_auth_requested_create(protected_ike_sa_t *ike_sa, + chunk_t sent_nonce, + chunk_t received_nonce, + chunk_t ike_sa_init_reply_data, + child_sa_t *child_sa); + +#endif /*IKE_AUTH_REQUESTED_H_*/ diff --git a/src/charon/charon/sa/states/ike_sa_established.c b/src/charon/charon/sa/states/ike_sa_established.c new file mode 100644 index 000000000..e91409f6a --- /dev/null +++ b/src/charon/charon/sa/states/ike_sa_established.c @@ -0,0 +1,239 @@ +/** + * @file ike_sa_established.c + * + * @brief Implementation of ike_sa_established_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_sa_established.h" + +#include +#include + + +typedef struct private_ike_sa_established_t private_ike_sa_established_t; + +/** + * Private data of a ike_sa_established_t object. + */ +struct private_ike_sa_established_t { + /** + * methods of the state_t interface + */ + ike_sa_established_t public; + + /** + * Assigned IKE_SA. + */ + protected_ike_sa_t *ike_sa; + + /** + * Assigned logger. Use logger of IKE_SA. + */ + logger_t *logger; + + /** + * Process a notify payload + * + * @param this calling object + * @param notify_payload notify payload + * @param response response message of type INFORMATIONAL + * + * - SUCCESS + * - FAILED + * - DELETE_ME + */ + status_t (*process_notify_payload) (private_ike_sa_established_t *this, notify_payload_t *notify_payload,message_t *response); +}; + +/** + * Implements state_t.get_state + */ +static status_t process_message(private_ike_sa_established_t *this, message_t *message) +{ + delete_payload_t *delete_request = NULL; + ike_sa_id_t *ike_sa_id; + iterator_t *payloads; + message_t *response; + crypter_t *crypter; + signer_t *signer; + status_t status; + + if (message->get_exchange_type(message) != INFORMATIONAL) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_sa_established", + mapping_find(exchange_type_m,message->get_exchange_type(message))); + return FAILED; + } + + if (!message->get_request(message)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "INFORMATIONAL responses not handled in state ike_sa_established"); + return FAILED; + } + + ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); + + /* get signer for verification and crypter for decryption */ + if (!ike_sa_id->is_initiator(ike_sa_id)) + { + crypter = this->ike_sa->get_crypter_initiator(this->ike_sa); + signer = this->ike_sa->get_signer_initiator(this->ike_sa); + } + else + { + crypter = this->ike_sa->get_crypter_responder(this->ike_sa); + signer = this->ike_sa->get_signer_responder(this->ike_sa); + } + + /* parse incoming message */ + status = message->parse_body(message, crypter, signer); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "INFORMATIONAL request decryption failed. Ignoring message"); + return status; + } + + /* build empty INFORMATIONAL message */ + this->ike_sa->build_message(this->ike_sa, INFORMATIONAL, FALSE, &response); + + payloads = message->get_payload_iterator(message); + + while (payloads->has_next(payloads)) + { + payload_t *payload; + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + case NOTIFY: + { + notify_payload_t *notify_payload = (notify_payload_t *) payload; + /* handle the notify directly, abort if no further processing required */ + status = this->process_notify_payload(this, notify_payload,response); + if (status != SUCCESS) + { + payloads->destroy(payloads); + response->destroy(response); + return status; + } + } + case DELETE: + { + delete_request = (delete_payload_t *) payload; + break; + } + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring Payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload)); + break; + } + } + } + /* iterator can be destroyed */ + payloads->destroy(payloads); + + if (delete_request) + { + if (delete_request->get_protocol_id(delete_request) == PROTO_IKE) + { + this->logger->log(this->logger, AUDIT, "DELETE request for IKE_SA received"); + response->destroy(response); + return DELETE_ME; + } + else + { + this->logger->log(this->logger, AUDIT, "DELETE request for CHILD_SA received. Ignored"); + response->destroy(response); + return SUCCESS; + } + } + + status = this->ike_sa->send_response(this->ike_sa, response); + /* message can now be sent (must not be destroyed) */ + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Unable to send INFORMATIONAL reply"); + response->destroy(response); + return FAILED; + } + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_established_t.process_notify_payload; + */ +static status_t process_notify_payload (private_ike_sa_established_t *this, notify_payload_t *notify_payload, message_t *response) +{ + notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s for protocol %s", + mapping_find(notify_message_type_m, notify_message_type), + mapping_find(protocol_id_m, notify_payload->get_protocol_id(notify_payload))); + + switch (notify_message_type) + { + default: + { + this->logger->log(this->logger, AUDIT, "INFORMATIONAL request contained an unknown notify (%d), ignored.", notify_message_type); + } + } + + + return SUCCESS; +} + +/** + * Implementation of state_t.get_state. + */ +static ike_sa_state_t get_state(private_ike_sa_established_t *this) +{ + return IKE_SA_ESTABLISHED; +} + +/** + * Implementation of state_t.get_state + */ +static void destroy(private_ike_sa_established_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa) +{ + private_ike_sa_established_t *this = malloc_thing(private_ike_sa_established_t); + + /* interface functions */ + this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message; + this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state; + this->public.state_interface.destroy = (void (*) (state_t *)) destroy; + + /* private functions */ + this->process_notify_payload = process_notify_payload; + + /* private data */ + this->ike_sa = ike_sa; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &(this->public); +} diff --git a/src/charon/charon/sa/states/ike_sa_established.h b/src/charon/charon/sa/states/ike_sa_established.h new file mode 100644 index 000000000..8477ad5bc --- /dev/null +++ b/src/charon/charon/sa/states/ike_sa_established.h @@ -0,0 +1,64 @@ +/** + * @file ike_sa_established.h + * + * @brief Interface of ike_sa_established_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_ESTABLISHED_H_ +#define IKE_SA_ESTABLISHED_H_ + +#include +#include + +typedef struct ike_sa_established_t ike_sa_established_t; + +/** + * @brief This class represents an the state of an established + * IKE_SA. + * + * @b Constructors: + * - ike_sa_established_create() + * + * @todo Implement handling of CREATE_CHILD_SA requests + * + * @todo Implement initialization of CREATE_CHILD_SA requests + * + * @todo Implement handling of any other message + * + * @ingroup states + */ +struct ike_sa_established_t { + /** + * methods of the state_t interface + */ + state_t state_interface; + +}; + +/** + * @brief Constructor of class ike_sa_established_t + * + * @param ike_sa assigned ike_sa + * @return created ike_sa_established_t object + * + * @ingroup states + */ +ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa); + +#endif /*IKE_SA_ESTABLISHED_H_*/ diff --git a/src/charon/charon/sa/states/ike_sa_init_requested.c b/src/charon/charon/sa/states/ike_sa_init_requested.c new file mode 100644 index 000000000..311cdf0a0 --- /dev/null +++ b/src/charon/charon/sa/states/ike_sa_init_requested.c @@ -0,0 +1,798 @@ +/** + * @file ike_sa_init_requested.c + * + * @brief Implementation of ike_sa_init_requested_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_sa_init_requested.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_ike_sa_init_requested_t private_ike_sa_init_requested_t; + +/** + * Private data of a ike_sa_init_requested_t object. + * + */ +struct private_ike_sa_init_requested_t { + /** + * Public interface of an ike_sa_init_requested_t object. + */ + ike_sa_init_requested_t public; + + /** + * Assigned IKE_SA + */ + protected_ike_sa_t *ike_sa; + + /** + * Diffie Hellman object used to compute shared secret. + */ + diffie_hellman_t *diffie_hellman; + + /** + * Sent nonce value. + */ + chunk_t sent_nonce; + + /** + * Received nonce + */ + chunk_t received_nonce; + + /** + * Selected proposal + */ + proposal_t *proposal; + + /** + * Packet data of ike_sa_init request + */ + chunk_t ike_sa_init_request_data; + + /** + * Created child sa, if any + */ + child_sa_t *child_sa; + + /** + * Assigned logger + * + * Is logger of ike_sa! + */ + logger_t *logger; + + + /** + * Process NONCE payload of IKE_SA_INIT response. + * + * @param this calling object + * @param nonce_payload NONCE payload to process + * @return SUCCESS in any case + */ + status_t (*process_nonce_payload) (private_ike_sa_init_requested_t *this, nonce_payload_t *nonce_payload); + + /** + * Process SA payload of IKE_SA_INIT response. + * + * @param this calling object + * @param sa_payload SA payload to process + * @return + * - SUCCESS + * - FAILED + */ + status_t (*process_sa_payload) (private_ike_sa_init_requested_t *this, sa_payload_t *sa_payload); + + /** + * Process KE payload of IKE_SA_INIT response. + * + * @param this calling object + * @param sa_payload KE payload to process + * @return + * - SUCCESS + * - FAILED + */ + status_t (*process_ke_payload) (private_ike_sa_init_requested_t *this, ke_payload_t *ke_payload); + + /** + * Build ID payload for IKE_AUTH request. + * + * @param this calling object + * @param[out] id_payload buildet ID payload + * @param response created payload will be added to this message_t object + * @return + * - SUCCESS + * - FAILED + */ + status_t (*build_id_payload) (private_ike_sa_init_requested_t *this,id_payload_t **id_payload, message_t *response); + + /** + * Build IDr payload for IKE_AUTH request. + * + * Only built when the ID of the responder contains no wildcards. + * + * @param this calling object + * @param response created payload will be added to this message_t object + * @return + * - SUCCESS + * - FAILED + */ + status_t (*build_idr_payload) (private_ike_sa_init_requested_t *this, message_t *response); + + /** + * Build AUTH payload for IKE_AUTH request. + * + * @param this calling object + * @param my_id_payload buildet ID payload + * @param response created payload will be added to this message_t object + * @return + * - SUCCESS + * - FAILED + */ + status_t (*build_auth_payload) (private_ike_sa_init_requested_t *this,id_payload_t *my_id_payload, message_t *response); + + /** + * Build SA payload for IKE_AUTH request. + * + * @param this calling object + * @param response created payload will be added to this message_t object + * @return + * - SUCCESS + * - FAILED + */ + status_t (*build_sa_payload) (private_ike_sa_init_requested_t *this, message_t *response); + + /** + * Build TSi payload for IKE_AUTH request. + * + * @param this calling object + * @param response created payload will be added to this message_t object + * @return + * - SUCCESS + * - FAILED + */ + status_t (*build_tsi_payload) (private_ike_sa_init_requested_t *this, message_t *response); + + /** + * Build TSr payload for IKE_AUTH request. + * + * @param this calling object + * @param response created payload will be added to this message_t object + * @return + * - SUCCESS + * - FAILED + */ + status_t (*build_tsr_payload) (private_ike_sa_init_requested_t *this, message_t *response); + + /** + * Process a notify payload and react. + * + * @param this calling object + * @param notify_payload notify_payload to handle + */ + status_t (*process_notify_payload) (private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload); + + /** + * Destroy function called internally of this class after state change to + * state IKE_AUTH_REQUESTED succeeded. + * + * This destroy function does not destroy objects which were passed to the new state. + * + * @param this calling object + */ + void (*destroy_after_state_change) (private_ike_sa_init_requested_t *this); +}; + +/** + * Implementation of state_t.process_message. + */ +static status_t process_message(private_ike_sa_init_requested_t *this, message_t *ike_sa_init_reply) +{ + ike_auth_requested_t *next_state; + chunk_t ike_sa_init_reply_data; + sa_payload_t *sa_payload = NULL; + ke_payload_t *ke_payload = NULL; + id_payload_t *id_payload = NULL; + nonce_payload_t *nonce_payload = NULL; + u_int64_t responder_spi; + ike_sa_id_t *ike_sa_id; + iterator_t *payloads; + host_t *me; + connection_t *connection; + policy_t *policy; + + message_t *request; + status_t status; + + /* + * In this state a reply message of type IKE_SA_INIT is expected: + * + * <-- HDR, SAr1, KEr, Nr, [CERTREQ] + * or + * <-- HDR, N + */ + + if (ike_sa_init_reply->get_exchange_type(ike_sa_init_reply) != IKE_SA_INIT) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_sa_init_requested", + mapping_find(exchange_type_m,ike_sa_init_reply->get_exchange_type(ike_sa_init_reply))); + return FAILED; + } + + if (ike_sa_init_reply->get_request(ike_sa_init_reply)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT requests not allowed state ike_sa_init_responded"); + return FAILED; + } + + /* parse incoming message */ + status = ike_sa_init_reply->parse_body(ike_sa_init_reply, NULL, NULL); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT reply parsing faild. Ignoring message"); + return status; + } + + /* because we are original initiator we have to update the responder SPI to the new one */ + responder_spi = ike_sa_init_reply->get_responder_spi(ike_sa_init_reply); + if (responder_spi == 0) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT reply contained a SPI of zero"); + return FAILED; + } + ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); + ike_sa_id->set_responder_spi(ike_sa_id,responder_spi); + + /* Iterate over all payloads. + * + * The message is allready checked for the right payload types. + */ + payloads = ike_sa_init_reply->get_payload_iterator(ike_sa_init_reply); + while (payloads->has_next(payloads)) + { + payload_t *payload; + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + { + sa_payload = (sa_payload_t*)payload; + break; + } + case KEY_EXCHANGE: + { + ke_payload = (ke_payload_t*)payload; + break; + } + case NONCE: + { + nonce_payload = (nonce_payload_t*)payload; + break; + } + case NOTIFY: + { + notify_payload_t *notify_payload = (notify_payload_t *) payload; + + status = this->process_notify_payload(this, notify_payload); + if (status != SUCCESS) + { + payloads->destroy(payloads); + return status; + } + break; + } + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload)); + break; + } + + } + + } + payloads->destroy(payloads); + + if (!(nonce_payload && sa_payload && ke_payload)) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT reply did not contain all required payloads. Deleting IKE_SA"); + return DELETE_ME; + } + + status = this->process_nonce_payload (this,nonce_payload); + if (status != SUCCESS) + { + return status; + } + + status = this->process_sa_payload (this,sa_payload); + if (status != SUCCESS) + { + return status; + } + + status = this->process_ke_payload (this,ke_payload); + if (status != SUCCESS) + { + return status; + } + + /* derive all the keys used in the IKE_SA */ + status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->sent_nonce, this->received_nonce); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Transform objects could not be created from selected proposal. Deleting IKE_SA"); + return DELETE_ME; + } + + /* apply the address on wich we really received the packet */ + connection = this->ike_sa->get_connection(this->ike_sa); + me = ike_sa_init_reply->get_destination(ike_sa_init_reply); + connection->update_my_host(connection, me->clone(me)); + policy = this->ike_sa->get_policy(this->ike_sa); + policy->update_my_ts(policy, me); + + /* build empty message */ + this->ike_sa->build_message(this->ike_sa, IKE_AUTH, TRUE, &request); + + status = this->build_id_payload(this, &id_payload, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_idr_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_auth_payload(this, (id_payload_t*)id_payload, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_sa_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_tsi_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + status = this->build_tsr_payload(this, request); + if (status != SUCCESS) + { + request->destroy(request); + return status; + } + + /* message can now be sent (must not be destroyed) */ + status = this->ike_sa->send_request(this->ike_sa, request); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Unable to send IKE_AUTH request. Deleting IKE_SA"); + request->destroy(request); + return DELETE_ME; + } + + this->ike_sa->set_last_replied_message_id(this->ike_sa,ike_sa_init_reply->get_message_id(ike_sa_init_reply)); + + ike_sa_init_reply_data = ike_sa_init_reply->get_packet_data(ike_sa_init_reply); + + /* state can now be changed */ + next_state = ike_auth_requested_create(this->ike_sa, this->sent_nonce, this->received_nonce, + ike_sa_init_reply_data, this->child_sa); + this->ike_sa->set_new_state(this->ike_sa,(state_t *) next_state); + + this->destroy_after_state_change(this); + return SUCCESS; +} + + +/** + * Implementation of private_ike_sa_init_requested_t.process_nonce_payload. + */ +status_t process_nonce_payload (private_ike_sa_init_requested_t *this, nonce_payload_t *nonce_payload) +{ + free(this->received_nonce.ptr); + this->received_nonce = nonce_payload->get_nonce(nonce_payload); + return SUCCESS; +} + + +/** + * Implementation of private_ike_sa_init_requested_t.process_sa_payload. + */ +status_t process_sa_payload (private_ike_sa_init_requested_t *this, sa_payload_t *sa_payload) +{ + proposal_t *proposal; + linked_list_t *proposal_list; + connection_t *connection; + + connection = this->ike_sa->get_connection(this->ike_sa); + + /* get the list of selected proposals, the peer has to select only one proposal */ + proposal_list = sa_payload->get_proposals (sa_payload); + if (proposal_list->get_count(proposal_list) != 1) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response did not contain a single proposal. Deleting IKE_SA"); + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + return DELETE_ME; + } + + /* we have to re-check if the others selection is valid */ + this->proposal = connection->select_proposal(connection, proposal_list); + while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + + if (this->proposal == NULL) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained selected proposal we did not offer. Deleting IKE_SA"); + return DELETE_ME; + } + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.process_ke_payload. + */ +status_t process_ke_payload (private_ike_sa_init_requested_t *this, ke_payload_t *ke_payload) +{ + this->diffie_hellman->set_other_public_value(this->diffie_hellman, ke_payload->get_key_exchange_data(ke_payload)); + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.build_id_payload. + */ +static status_t build_id_payload (private_ike_sa_init_requested_t *this,id_payload_t **id_payload, message_t *request) +{ + policy_t *policy; + id_payload_t *new_id_payload; + identification_t *identification; + + policy = this->ike_sa->get_policy(this->ike_sa); + identification = policy->get_my_id(policy); + new_id_payload = id_payload_create_from_identification(TRUE, identification); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add ID payload to message"); + request->add_payload(request,(payload_t *) new_id_payload); + + *id_payload = new_id_payload; + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.build_idr_payload. + */ +static status_t build_idr_payload (private_ike_sa_init_requested_t *this, message_t *request) +{ + policy_t *policy; + id_payload_t *idr_payload; + identification_t *identification; + + policy = this->ike_sa->get_policy(this->ike_sa); + identification = policy->get_other_id(policy); + if (!identification->contains_wildcards(identification)) + { + idr_payload = id_payload_create_from_identification(FALSE, identification); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add IDr payload to message"); + request->add_payload(request,(payload_t *) idr_payload); + } + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.build_auth_payload. + */ +static status_t build_auth_payload (private_ike_sa_init_requested_t *this, id_payload_t *my_id_payload, message_t *request) +{ + authenticator_t *authenticator; + auth_payload_t *auth_payload; + status_t status; + + authenticator = authenticator_create(this->ike_sa); + status = authenticator->compute_auth_data(authenticator,&auth_payload,this->ike_sa_init_request_data,this->received_nonce,my_id_payload,TRUE); + authenticator->destroy(authenticator); + + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Could not generate AUTH data for IKE_AUTH request. Deleting IKE_SA"); + return DELETE_ME; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add AUTH payload to message"); + request->add_payload(request,(payload_t *) auth_payload); + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.build_sa_payload. + */ +static status_t build_sa_payload (private_ike_sa_init_requested_t *this, message_t *request) +{ + linked_list_t *proposal_list; + sa_payload_t *sa_payload; + policy_t *policy; + connection_t *connection; + + /* get proposals form config, add to payload */ + policy = this->ike_sa->get_policy(this->ike_sa); + proposal_list = policy->get_proposals(policy); + /* build child sa */ + connection = this->ike_sa->get_connection(this->ike_sa); + this->child_sa = child_sa_create(connection->get_my_host(connection), + connection->get_other_host(connection)); + if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA"); + return DELETE_ME; + } + + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add SA payload to message"); + request->add_payload(request,(payload_t *) sa_payload); + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.build_tsi_payload. + */ +static status_t build_tsi_payload (private_ike_sa_init_requested_t *this, message_t *request) +{ + linked_list_t *ts_list; + ts_payload_t *ts_payload; + policy_t *policy; + + policy = this->ike_sa->get_policy(this->ike_sa); + ts_list = policy->get_my_traffic_selectors(policy); + ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add TSi payload to message"); + request->add_payload(request,(payload_t *) ts_payload); + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.build_tsr_payload. + */ +static status_t build_tsr_payload (private_ike_sa_init_requested_t *this, message_t *request) +{ + linked_list_t *ts_list; + ts_payload_t *ts_payload; + policy_t *policy; + + policy = this->ike_sa->get_policy(this->ike_sa); + ts_list = policy->get_other_traffic_selectors(policy); + ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add TSr payload to message"); + request->add_payload(request,(payload_t *) ts_payload); + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_requested_t.process_notify_payload. + */ +static status_t process_notify_payload(private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload) +{ + notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s", + mapping_find(notify_message_type_m, notify_message_type)); + + switch (notify_message_type) + { + case NO_PROPOSAL_CHOSEN: + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained a NO_PROPOSAL_CHOSEN notify. Deleting IKE_SA"); + return DELETE_ME; + } + case INVALID_MAJOR_VERSION: + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT response contained a INVALID_MAJOR_VERSION notify. Deleting IKE_SA"); + return DELETE_ME; + } + case INVALID_KE_PAYLOAD: + { + initiator_init_t *initiator_init_state; + chunk_t notify_data; + diffie_hellman_group_t dh_group, old_dh_group; + connection_t *connection; + + connection = this->ike_sa->get_connection(this->ike_sa); + old_dh_group = connection->get_dh_group(connection); + notify_data = notify_payload->get_notification_data(notify_payload); + dh_group = ntohs(*((u_int16_t*)notify_data.ptr)); + + /* TODO: + * We are very restrictive here: If the other didn't accept + * our DH group, and we do not accept his offer, continuation + * is cancelled... + */ + + this->logger->log(this->logger, AUDIT, "Peer didn't accept %s, it requested %s!", + mapping_find(diffie_hellman_group_m, old_dh_group), + mapping_find(diffie_hellman_group_m, dh_group)); + /* check if we can accept this dh group */ + if (!connection->check_dh_group(connection, dh_group)) + { + this->logger->log(this->logger, AUDIT, + "Peer does only accept DH group %s, which we do not accept! Aborting", + mapping_find(diffie_hellman_group_m, dh_group)); + return DELETE_ME; + } + + /* Going to change state back to initiator_init_t */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Create next state object"); + initiator_init_state = initiator_init_create(this->ike_sa); + + /* buffer of sent and received messages has to get reseted */ + this->ike_sa->reset_message_buffers(this->ike_sa); + + /* state can now be changed */ + this->ike_sa->set_new_state(this->ike_sa,(state_t *) initiator_init_state); + + /* state has NOW changed :-) */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Destroy old sate object"); + this->logger->log(this->logger, CONTROL|LEVEL2, "Going to retry initialization of connection"); + + this->public.state_interface.destroy(&(this->public.state_interface)); + if (initiator_init_state->retry_initiate_connection (initiator_init_state, dh_group) != SUCCESS) + { + return DELETE_ME; + } + return FAILED; + } + default: + { + /* + * - In case of unknown error: IKE_SA gets destroyed. + * - In case of unknown status: logging + */ + if (notify_message_type < 16383) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT reply contained an unknown notify error (%d). Deleting IKE_SA", + notify_message_type); + return DELETE_ME; + } + else + { + this->logger->log(this->logger, CONTROL, "IKE_SA_INIT reply contained an unknown notify (%d), ignored.", + notify_message_type); + return SUCCESS; + } + } + } +} + +/** + * Implementation of state_t.get_state. + */ +static ike_sa_state_t get_state(private_ike_sa_init_requested_t *this) +{ + return IKE_SA_INIT_REQUESTED; +} + +/** + * Implementation of private_ike_sa_init_requested_t.destroy_after_state_change. + */ +static void destroy_after_state_change (private_ike_sa_init_requested_t *this) +{ + this->diffie_hellman->destroy(this->diffie_hellman); + chunk_free(&(this->ike_sa_init_request_data)); + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + free(this); +} + +/** + * Implementation state_t.destroy. + */ +static void destroy(private_ike_sa_init_requested_t *this) +{ + this->diffie_hellman->destroy(this->diffie_hellman); + free(this->sent_nonce.ptr); + free(this->received_nonce.ptr); + chunk_free(&(this->ike_sa_init_request_data)); + if (this->child_sa) + { + this->child_sa->destroy(this->child_sa); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + free(this); +} + +/* + * Described in header. + */ +ike_sa_init_requested_t *ike_sa_init_requested_create(protected_ike_sa_t *ike_sa, diffie_hellman_t *diffie_hellman, chunk_t sent_nonce,chunk_t ike_sa_init_request_data) +{ + private_ike_sa_init_requested_t *this = malloc_thing(private_ike_sa_init_requested_t); + + /* interface functions */ + this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message; + this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state; + this->public.state_interface.destroy = (void (*) (state_t *)) destroy; + + /* private functions */ + this->destroy_after_state_change = destroy_after_state_change; + this->process_nonce_payload = process_nonce_payload; + this->process_sa_payload = process_sa_payload; + this->process_ke_payload = process_ke_payload; + this->build_auth_payload = build_auth_payload; + this->build_tsi_payload = build_tsi_payload; + this->build_tsr_payload = build_tsr_payload; + this->build_id_payload = build_id_payload; + this->build_idr_payload = build_idr_payload; + this->build_sa_payload = build_sa_payload; + this->process_notify_payload = process_notify_payload; + + /* private data */ + this->ike_sa = ike_sa; + this->received_nonce = CHUNK_INITIALIZER; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + this->diffie_hellman = diffie_hellman; + this->proposal = NULL; + this->sent_nonce = sent_nonce; + this->child_sa = NULL; + this->ike_sa_init_request_data = ike_sa_init_request_data; + + return &(this->public); +} diff --git a/src/charon/charon/sa/states/ike_sa_init_requested.h b/src/charon/charon/sa/states/ike_sa_init_requested.h new file mode 100644 index 000000000..0a43afad1 --- /dev/null +++ b/src/charon/charon/sa/states/ike_sa_init_requested.h @@ -0,0 +1,68 @@ +/** + * @file ike_sa_init_requested.h + * + * @brief Interface of ike_sa_init_requestet_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_INIT_REQUESTED_H_ +#define IKE_SA_INIT_REQUESTED_H_ + +#include +#include +#include +#include + +typedef struct ike_sa_init_requested_t ike_sa_init_requested_t; + +/** + * @brief This class represents an IKE_SA state when + * requested an IKE_SA_INIT as initiator. + * + * @b Constructors: + * - ike_sa_init_requested_create() + * + * @todo Include valid child sa SPIs in proposal + * + * @ingroup states + */ +struct ike_sa_init_requested_t { + /** + * The state_t interface. + */ + state_t state_interface; +}; + +/** + * Constructor of class ike_sa_init_requested_t. + * + * @param ike_sa assigned ike_sa + * @param diffie_hellman diffie_hellman object use to retrieve shared secret + * @param sent_nonce Sent nonce value + * @param ike_sa_init_request_data the binary representation of the IKE_SA_INIT request message + * @return created ike_sa_init_request_t object + * + * @ingroup states + */ +ike_sa_init_requested_t *ike_sa_init_requested_create(protected_ike_sa_t *ike_sa, + diffie_hellman_t *diffie_hellman, + chunk_t sent_nonce, + chunk_t ike_sa_init_request_data); + +#endif /*IKE_SA_INIT_REQUESTED_H_*/ diff --git a/src/charon/charon/sa/states/ike_sa_init_responded.c b/src/charon/charon/sa/states/ike_sa_init_responded.c new file mode 100644 index 000000000..e40b0cf22 --- /dev/null +++ b/src/charon/charon/sa/states/ike_sa_init_responded.c @@ -0,0 +1,695 @@ +/** + * @file ike_sa_init_responded.c + * + * @brief State of a IKE_SA after responding to an IKE_SA_INIT request + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "ike_sa_init_responded.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_ike_sa_init_responded_t private_ike_sa_init_responded_t; + +/** + * Private data of a ike_sa_init_responded_t object. + * + */ +struct private_ike_sa_init_responded_t { + /** + * Public interface of ike_sa_init_responded_t. + */ + ike_sa_init_responded_t public; + + /** + * Assigned IKE_SA. + */ + protected_ike_sa_t *ike_sa; + + /** + * Received nonce. + */ + chunk_t received_nonce; + + /** + * Sent nonce. + */ + chunk_t sent_nonce; + + /** + * Binary representation of the IKE_SA_INIT response. + */ + chunk_t ike_sa_init_response_data; + + /** + * Binary representation of the IKE_SA_INIT request. + */ + chunk_t ike_sa_init_request_data; + + /** + * SA config to use. + */ + policy_t *policy; + + /** + * CHILD_SA, if set up + */ + child_sa_t *child_sa; + + /** + * Traffic selectors applicable at our site + */ + linked_list_t *my_ts; + + /** + * Traffic selectors applicable at remote site + */ + linked_list_t *other_ts; + + /** + * Assigned logger. + * + * Is logger of ike_sa! + */ + logger_t *logger; + + /** + * Process received IDi and IDr payload and build IDr payload for IKE_AUTH response. + * + * @param this calling object + * @param request_idi ID payload representing initiator + * @param request_idr ID payload representing responder (May be zero) + * @param response The created IDr payload is added to this message_t object + * @param response_idr The created IDr payload is also written to this location + */ + status_t (*build_idr_payload) (private_ike_sa_init_responded_t *this, + id_payload_t *request_idi, + id_payload_t *request_idr, + message_t *response, + id_payload_t **response_idr); + + /** + * Process received SA payload and build SA payload for IKE_AUTH response. + * + * @param this calling object + * @param request SA payload received in IKE_AUTH request + * @param response The created SA payload is added to this message_t object + */ + status_t (*build_sa_payload) (private_ike_sa_init_responded_t *this, sa_payload_t *request, message_t *response); + + /** + * Process received AUTH payload and build AUTH payload for IKE_AUTH response. + * + * @param this calling object + * @param request AUTH payload received in IKE_AUTH request + * @param other_id_payload other ID payload needed to verify AUTH data + * @param my_id_payload my ID payload needed to compute AUTH data + * @param response The created AUTH payload is added to this message_t object + */ + status_t (*build_auth_payload) (private_ike_sa_init_responded_t *this, auth_payload_t *request,id_payload_t *other_id_payload,id_payload_t *my_id_payload, message_t* response); + + /** + * Process received TS payload and build TS payload for IKE_AUTH response. + * + * @param this calling object + * @param is_initiator type of TS payload. TRUE for TSi, FALSE for TSr + * @param request TS payload received in IKE_AUTH request + * @param response the created TS payload is added to this message_t object + */ + status_t (*build_ts_payload) (private_ike_sa_init_responded_t *this, bool ts_initiator, ts_payload_t *request, message_t *response); + + /** + * Sends a IKE_AUTH reply containing a notify payload. + * + * @param this calling object + * @param notify_payload payload to process + * @return + * - DELETE_ME if IKE_SA should be deleted + * - SUCCSS if processed successfull + */ + status_t (*process_notify_payload) (private_ike_sa_init_responded_t *this, notify_payload_t* notify_payload); + + /** + * Destroy function called internally of this class after state change to + * state IKE_SA_ESTABLISHED succeeded. + * + * This destroy function does not destroy objects which were passed to the new state. + * + * @param this calling object + */ + void (*destroy_after_state_change) (private_ike_sa_init_responded_t *this); +}; + +/** + * Implements state_t.get_state + */ +static status_t process_message(private_ike_sa_init_responded_t *this, message_t *request) +{ + id_payload_t *idi_request = NULL, *idr_request = NULL,*idr_response; + ts_payload_t *tsi_request = NULL, *tsr_request = NULL; + auth_payload_t *auth_request = NULL; + sa_payload_t *sa_request = NULL; + iterator_t *payloads; + message_t *response; + crypter_t *crypter; + signer_t *signer; + status_t status; + host_t *my_host, *other_host; + connection_t *connection; + + if (request->get_exchange_type(request) != IKE_AUTH) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state ike_sa_init_responded", + mapping_find(exchange_type_m,request->get_exchange_type(request))); + return FAILED; + } + + if (!request->get_request(request)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH responses not allowed state ike_sa_init_responded"); + return FAILED; + } + + /* get signer for verification and crypter for decryption */ + signer = this->ike_sa->get_signer_initiator(this->ike_sa); + crypter = this->ike_sa->get_crypter_initiator(this->ike_sa); + + status = request->parse_body(request, crypter, signer); + if (status != SUCCESS) + { + if (status == NOT_SUPPORTED) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_AUTH request contains unsupported payload with critical flag set." + "Deleting IKE_SA"); + this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER); + return DELETE_ME; + } + else + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH request decryption faild. Ignoring message"); + } + return status; + } + + /* iterate over incoming payloads. Message is verified, we can be sure there are the required payloads */ + payloads = request->get_payload_iterator(request); + while (payloads->has_next(payloads)) + { + payload_t *payload; + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + case ID_INITIATOR: + { + idi_request = (id_payload_t*)payload; + break; + } + case AUTHENTICATION: + { + auth_request = (auth_payload_t*)payload; + break; + } + case ID_RESPONDER: + { + idr_request = (id_payload_t*)payload; + break; + } + case SECURITY_ASSOCIATION: + { + sa_request = (sa_payload_t*)payload; + break; + } + case TRAFFIC_SELECTOR_INITIATOR: + { + tsi_request = (ts_payload_t*)payload; + break; + } + case TRAFFIC_SELECTOR_RESPONDER: + { + tsr_request = (ts_payload_t*)payload; + break; + } + case NOTIFY: + { + notify_payload_t *notify_payload = (notify_payload_t *) payload; + status = this->process_notify_payload(this, notify_payload); + if (status != SUCCESS) + { + payloads->destroy(payloads); + return status; + } + } + case CERTIFICATE: + { + /* TODO handle cert payloads */ + } + case CERTIFICATE_REQUEST: + { + /* TODO handle certrequest payloads */ + } + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload)); + break; + } + } + } + /* iterator can be destroyed */ + payloads->destroy(payloads); + + /* check if we have all payloads */ + if (!(idi_request && sa_request && auth_request && tsi_request && tsr_request)) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA"); + return DELETE_ME; + } + + /* build response */ + this->ike_sa->build_message(this->ike_sa, IKE_AUTH, FALSE, &response); + + /* add payloads to it */ + status = this->build_idr_payload(this, idi_request, idr_request, response,&idr_response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->build_auth_payload(this, auth_request,idi_request, idr_response,response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->build_sa_payload(this, sa_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->build_ts_payload(this, TRUE, tsi_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + status = this->build_ts_payload(this, FALSE, tsr_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + + status = this->ike_sa->send_response(this->ike_sa, response); + /* message can now be sent (must not be destroyed) */ + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Unable to send IKE_AUTH reply. Deleting IKE_SA"); + response->destroy(response); + return DELETE_ME; + } + + /* install child SA policies */ + if (!this->child_sa) + { + this->logger->log(this->logger, CONTROL, "Proposal negotiation failed, no CHILD_SA built"); + } + else if (this->my_ts->get_count(this->my_ts) == 0 || this->other_ts->get_count(this->other_ts) == 0) + { + this->logger->log(this->logger, CONTROL, "Traffic selector negotiation failed, no CHILD_SA built"); + this->child_sa->destroy(this->child_sa); + this->child_sa = NULL; + } + else + { + status = this->child_sa->add_policies(this->child_sa, this->my_ts, this->other_ts); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA policy! Deleting IKE_SA"); + return DELETE_ME; + } + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + } + + /* create new state */ + this->ike_sa->set_new_state(this->ike_sa, (state_t*)ike_sa_established_create(this->ike_sa)); + this->destroy_after_state_change(this); + + connection = this->ike_sa->get_connection(this->ike_sa); + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + this->logger->log(this->logger, AUDIT, "IKE_SA established between %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_responded_t.build_idr_payload. + */ +static status_t build_idr_payload(private_ike_sa_init_responded_t *this, id_payload_t *request_idi, id_payload_t *request_idr, message_t *response,id_payload_t **response_idr) +{ + identification_t *other_id, *my_id = NULL; + connection_t *connection; + id_payload_t *idr_response; + + connection = this->ike_sa->get_connection(this->ike_sa); + + /* update adresses, as connection may contain wildcards, or wrong IDs */ + other_id = request_idi->get_identification(request_idi); + if (request_idr) + { + my_id = request_idr->get_identification(request_idr); + connection->update_my_id(connection, my_id); + } + else + { + my_id = connection->get_my_id(connection); + } + connection->update_other_id(connection, other_id); + + /* build new sa config */ + this->policy = charon->policies->get_policy(charon->policies, my_id, other_id); + if (this->policy == NULL) + { + this->logger->log(this->logger, AUDIT, "We don't have a policy for IDs %s - %s. Deleting IKE_SA", + my_id->get_string(my_id), other_id->get_string(other_id)); + return DELETE_ME; + } + + /* get my id from policy, which must contain a fully qualified valid id */ + my_id = this->policy->get_my_id(this->policy); + + /* update others traffic selectors with actually used address */ + this->policy->update_other_ts(this->policy, response->get_destination(response)); + + /* set policy in ike_sa for other states */ + this->ike_sa->set_policy(this->ike_sa, this->policy); + + /* build response */ + idr_response = id_payload_create_from_identification(FALSE, my_id); + response->add_payload(response, (payload_t*)idr_response); + *response_idr = idr_response; + + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_responded_t.build_sa_payload. + */ +static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_payload_t *request, message_t *response) +{ + proposal_t *proposal, *proposal_tmp; + linked_list_t *proposal_list; + sa_payload_t *sa_response; + chunk_t seed; + prf_plus_t *prf_plus; + status_t status; + connection_t *connection; + + /* get proposals from request */ + proposal_list = request->get_proposals(request); + if (proposal_list->get_count(proposal_list) == 0) + { + /* if the other side did not offer any proposals, we do not create child sa's */ + this->logger->log(this->logger, AUDIT, "IKE_AUH request did not contain any proposals. No CHILD_SA created"); + sa_response = sa_payload_create(); + response->add_payload(response, (payload_t*)sa_response); + proposal_list->destroy(proposal_list); + return SUCCESS; + } + + /* now select a proposal */ + this->logger->log(this->logger, CONTROL|LEVEL1, "Selecting proposals:"); + proposal = this->policy->select_proposal(this->policy, proposal_list); + /* list is not needed anymore */ + while (proposal_list->remove_last(proposal_list, (void**)&proposal_tmp) == SUCCESS) + { + proposal_tmp->destroy(proposal_tmp); + } + proposal_list->destroy(proposal_list); + /* do we have a proposal */ + if (proposal == NULL) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH request did not contain any proposals we accept. Deleting IKE_SA"); + this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER); + return DELETE_ME; + } + + /* set up child sa */ + seed = chunk_alloc(this->received_nonce.len + this->sent_nonce.len); + memcpy(seed.ptr, this->received_nonce.ptr, this->received_nonce.len); + memcpy(seed.ptr + this->received_nonce.len, this->sent_nonce.ptr, this->sent_nonce.len); + prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + chunk_free(&seed); + + connection = this->ike_sa->get_connection(this->ike_sa); + this->child_sa = child_sa_create(connection->get_my_host(connection), + connection->get_other_host(connection)); + + status = this->child_sa->add(this->child_sa, proposal, prf_plus); + prf_plus->destroy(prf_plus); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA"); + return DELETE_ME; + } + + /* create payload with selected propsal */ + sa_response = sa_payload_create_from_proposal(proposal); + response->add_payload(response, (payload_t*)sa_response); + proposal->destroy(proposal); + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_responded_t.build_auth_payload. + */ +static status_t build_auth_payload(private_ike_sa_init_responded_t *this, auth_payload_t *auth_request,id_payload_t *other_id_payload,id_payload_t *my_id_payload, message_t* response) +{ + authenticator_t *authenticator; + auth_payload_t *auth_reply; + status_t status; + + authenticator = authenticator_create(this->ike_sa); + status = authenticator->verify_auth_data(authenticator,auth_request, this->ike_sa_init_request_data,this->sent_nonce,other_id_payload,TRUE); + + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH request verification failed. Deleting IKE_SA"); + this->ike_sa->send_notify(this->ike_sa, IKE_AUTH, AUTHENTICATION_FAILED, CHUNK_INITIALIZER); + authenticator->destroy(authenticator); + return DELETE_ME; + } + + status = authenticator->compute_auth_data(authenticator,&auth_reply, this->ike_sa_init_response_data,this->received_nonce,my_id_payload,FALSE); + authenticator->destroy(authenticator); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Unable to build authentication data for IKE_AUTH reply. Deleting IKE_SA"); + return DELETE_ME; + + } + + response->add_payload(response, (payload_t *)auth_reply); + return SUCCESS; +} + +/** + * Implementation of private_ike_sa_init_responded_t.build_ts_payload. + */ +static status_t build_ts_payload(private_ike_sa_init_responded_t *this, bool ts_initiator, ts_payload_t *request, message_t* response) +{ + linked_list_t *ts_received, *ts_selected; + traffic_selector_t *ts; + status_t status = SUCCESS; + ts_payload_t *ts_response; + + /* build a reply payload with selected traffic selectors */ + ts_received = request->get_traffic_selectors(request); + /* select ts depending on payload type */ + if (ts_initiator) + { + ts_selected = this->policy->select_other_traffic_selectors(this->policy, ts_received); + this->other_ts = ts_selected; + } + else + { + ts_selected = this->policy->select_my_traffic_selectors(this->policy, ts_received); + this->my_ts = ts_selected; + } + + ts_response = ts_payload_create_from_traffic_selectors(ts_initiator, ts_selected); + response->add_payload(response, (payload_t*)ts_response); + + /* cleanup */ + while (ts_received->remove_last(ts_received, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + ts_received->destroy(ts_received); + + return status; +} + +static status_t process_notify_payload(private_ike_sa_init_responded_t *this, notify_payload_t *notify_payload) +{ + notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s", + mapping_find(notify_message_type_m, notify_message_type)); + + switch (notify_message_type) + { + case SET_WINDOW_SIZE: + /* + * TODO Increase window size. + */ + case INITIAL_CONTACT: + /* + * TODO Delete existing IKE_SA's with other Identity. + */ + default: + { + this->logger->log(this->logger, AUDIT, "IKE_AUTH request contained an unknown notify (%d), ignored.", notify_message_type); + } + } + + return SUCCESS; +} + +/** + * Implementation of state_t.get_state. + */ +static ike_sa_state_t get_state(private_ike_sa_init_responded_t *this) +{ + return IKE_SA_INIT_RESPONDED; +} + +/** + * Implementation of state_t.destroy. + */ +static void destroy(private_ike_sa_init_responded_t *this) +{ + chunk_free(&(this->received_nonce)); + chunk_free(&(this->sent_nonce)); + chunk_free(&(this->ike_sa_init_response_data)); + chunk_free(&(this->ike_sa_init_request_data)); + if (this->my_ts) + { + traffic_selector_t *ts; + while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->my_ts->destroy(this->my_ts); + } + if (this->other_ts) + { + traffic_selector_t *ts; + while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->other_ts->destroy(this->other_ts); + } + if (this->child_sa) + { + this->child_sa->destroy(this->child_sa); + } + + free(this); +} +/** + * Implementation of private_ike_sa_init_responded.destroy_after_state_change. + */ +static void destroy_after_state_change(private_ike_sa_init_responded_t *this) +{ + chunk_free(&(this->received_nonce)); + chunk_free(&(this->sent_nonce)); + chunk_free(&(this->ike_sa_init_response_data)); + chunk_free(&(this->ike_sa_init_request_data)); + if (this->my_ts) + { + traffic_selector_t *ts; + while (this->my_ts->remove_last(this->my_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->my_ts->destroy(this->my_ts); + } + if (this->other_ts) + { + traffic_selector_t *ts; + while (this->other_ts->remove_last(this->other_ts, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + this->other_ts->destroy(this->other_ts); + } + + free(this); +} + +/* + * Described in header. + */ +ike_sa_init_responded_t *ike_sa_init_responded_create(protected_ike_sa_t *ike_sa, chunk_t received_nonce, chunk_t sent_nonce,chunk_t ike_sa_init_request_data, chunk_t ike_sa_init_response_data) +{ + private_ike_sa_init_responded_t *this = malloc_thing(private_ike_sa_init_responded_t); + + /* interface functions */ + this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message; + this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state; + this->public.state_interface.destroy = (void (*) (state_t *)) destroy; + + /* private functions */ + this->build_idr_payload = build_idr_payload; + this->build_sa_payload = build_sa_payload; + this->build_auth_payload = build_auth_payload; + this->build_ts_payload = build_ts_payload; + this->process_notify_payload = process_notify_payload; + this->destroy_after_state_change = destroy_after_state_change; + + /* private data */ + this->ike_sa = ike_sa; + this->received_nonce = received_nonce; + this->sent_nonce = sent_nonce; + this->ike_sa_init_response_data = ike_sa_init_response_data; + this->ike_sa_init_request_data = ike_sa_init_request_data; + this->my_ts = NULL; + this->other_ts = NULL; + this->child_sa = NULL; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + + return &(this->public); +} diff --git a/src/charon/charon/sa/states/ike_sa_init_responded.h b/src/charon/charon/sa/states/ike_sa_init_responded.h new file mode 100644 index 000000000..43aecf26f --- /dev/null +++ b/src/charon/charon/sa/states/ike_sa_init_responded.h @@ -0,0 +1,73 @@ +/** + * @file ike_sa_init_responded.h + * + * @brief Interface of ike_sa_init_responded_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_INIT_RESPONDED_H_ +#define IKE_SA_INIT_RESPONDED_H_ + +#include +#include + +typedef struct ike_sa_init_responded_t ike_sa_init_responded_t; + +/** + * @brief This class represents an IKE_SA state when + * responded to an IKE_SA_INIT request. + * + * The state accpets IKE_AUTH requests. It proves the authenticity + * and sets up the first child sa. Then it sends back an IKE_AUTH + * reply and changes to the IKE_SA_ESTABLISHED state. + * + * @b Constructors: + * - ike_sa_init_response_data() + * + * @todo Implement handling of SET_WINDOW_SIZE notify + * + * @todo Implement handling of INITIAL_CONTACT notify + * + * @ingroup states + */ +struct ike_sa_init_responded_t { + /** + * The state_t interface. + */ + state_t state_interface; + +}; + +/** + * @brief Constructor of class ike_sa_init_responded_t + * + * @param ike_sa assigned IKE_SA + * @param received_nonce received nonce data in IKE_SA_INIT request + * @param sent_nonce sent nonce data in IKE_SA_INIT response + * @param ike_sa_init_request_data binary representation of received IKE_SA_INIT request + * @param ike_sa_init_response_data binary representation of sent IKE_SA_INIT response + * + * @ingroup states + */ +ike_sa_init_responded_t *ike_sa_init_responded_create(protected_ike_sa_t *ike_sa, + chunk_t received_nonce, + chunk_t sent_nonce, + chunk_t ike_sa_init_request_data, + chunk_t ike_sa_init_response_data); + +#endif /*IKE_SA_INIT_RESPONDED_H_*/ diff --git a/src/charon/charon/sa/states/initiator_init.c b/src/charon/charon/sa/states/initiator_init.c new file mode 100644 index 000000000..35d15235d --- /dev/null +++ b/src/charon/charon/sa/states/initiator_init.c @@ -0,0 +1,360 @@ +/** + * @file initiator_init.c + * + * @brief Implementation of initiator_init_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "initiator_init.h" + + +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_initiator_init_t private_initiator_init_t; + +/** + * Private data of a initiator_init_t object.. + * + */ +struct private_initiator_init_t { + /** + * Methods of the state_t interface. + */ + initiator_init_t public; + + /** + * Assigned IKE_SA. + */ + protected_ike_sa_t *ike_sa; + + /** + * Diffie hellman object used to generate public DH value. + * This objet is passed to the next state of type IKE_SA_INIT_REQUESTED. + */ + diffie_hellman_t *diffie_hellman; + + /** + * Sent nonce. + * This nonce is passed to the next state of type IKE_SA_INIT_REQUESTED. + */ + chunk_t sent_nonce; + + /** + * Assigned logger. + * + * Is logger of ike_sa! + */ + logger_t *logger; + + /** + * Builds the SA payload for this state. + * + * @param this calling object + * @param request message_t object to add the SA payload + */ + void (*build_sa_payload) (private_initiator_init_t *this, message_t *request); + + /** + * Builds the KE payload for this state. + * + * @param this calling object + * @param request message_t object to add the KE payload + */ + void (*build_ke_payload) (private_initiator_init_t *this, message_t *request); + + /** + * Builds the NONCE payload for this state. + * + * @param this calling object + * @param request message_t object to add the NONCE payload + */ + status_t (*build_nonce_payload) (private_initiator_init_t *this,message_t *request); + + /** + * Destroy function called internally of this class after state change to state + * IKE_SA_INIT_REQUESTED succeeded. + * + * This destroy function does not destroy objects which were passed to the new state. + * + * @param this calling object + */ + void (*destroy_after_state_change) (private_initiator_init_t *this); +}; + +/** + * Implementation of initiator_init_t.initiate_connection. + */ +static status_t initiate_connection (private_initiator_init_t *this, connection_t *connection) +{ + policy_t *policy; + diffie_hellman_group_t dh_group; + host_t *my_host, *other_host; + identification_t *my_id, *other_id; + + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + my_id = connection->get_my_id(connection); + other_id = connection->get_other_id(connection); + + this->logger->log(this->logger, CONTROL, "Initiating connection between %s (%s) - %s (%s)", + my_id->get_string(my_id), my_host->get_address(my_host), + other_id->get_string(other_id), other_host->get_address(other_host)); + + this->ike_sa->set_connection(this->ike_sa, connection); + + /* get policy */ + policy = charon->policies->get_policy(charon->policies, my_id, other_id); + if (policy == NULL) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Could not get a policy for '%s - %s', aborting", + my_id->get_string(my_id), other_id->get_string(other_id)); + return DELETE_ME; + } + this->ike_sa->set_policy(this->ike_sa,policy); + + /* we must guess now a DH group. For that we choose our most preferred group */ + dh_group = connection->get_dh_group(connection); + + /* next step is done in retry_initiate_connection */ + return this->public.retry_initiate_connection(&this->public, dh_group); +} + +/** + * Implementation of initiator_init_t.retry_initiate_connection. + */ +status_t retry_initiate_connection (private_initiator_init_t *this, diffie_hellman_group_t dh_group) +{ + ike_sa_init_requested_t *next_state; + chunk_t ike_sa_init_request_data; + connection_t *connection; + ike_sa_id_t *ike_sa_id; + message_t *message; + status_t status; + + if (dh_group == MODP_UNDEFINED) + { + this->logger->log(this->logger, AUDIT, "No DH group acceptable for initialization, Aborting"); + return DELETE_ME; + } + + connection = this->ike_sa->get_connection(this->ike_sa); + this->diffie_hellman = diffie_hellman_create(dh_group); + ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); + ike_sa_id->set_responder_spi(ike_sa_id,0); + + /* going to build message */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Going to build message"); + this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, TRUE, &message); + + /* build SA payload */ + this->build_sa_payload(this, message); + + /* build KE payload */ + this->build_ke_payload(this, message); + + /* build Nonce payload */ + status = this->build_nonce_payload(this, message); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Building nonce payload failed. Aborting"); + message->destroy(message); + return DELETE_ME; + } + + /* message can now be sent (must not be destroyed) */ + status = this->ike_sa->send_request(this->ike_sa, message); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Unable to initiate connection, could not send message. Aborting"); + message->destroy(message); + return DELETE_ME; + } + + message = this->ike_sa->get_last_requested_message(this->ike_sa); + + ike_sa_init_request_data = message->get_packet_data(message); + + /* state can now be changed */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Create next state object"); + next_state = ike_sa_init_requested_create(this->ike_sa, this->diffie_hellman, this->sent_nonce,ike_sa_init_request_data); + this->ike_sa->set_new_state(this->ike_sa,(state_t *) next_state); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Destroy old sate object"); + this->destroy_after_state_change(this); + return SUCCESS; +} + +/** + * Implementation of private_initiator_init_t.build_sa_payload. + */ +static void build_sa_payload(private_initiator_init_t *this, message_t *request) +{ + sa_payload_t* sa_payload; + linked_list_t *proposal_list; + connection_t *connection; + + this->logger->log(this->logger, CONTROL|LEVEL1, "Building SA payload"); + + connection = this->ike_sa->get_connection(this->ike_sa); + + proposal_list = connection->get_proposals(connection); + + sa_payload = sa_payload_create_from_proposal_list(proposal_list); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add SA payload to message"); + request->add_payload(request, (payload_t *) sa_payload); +} + +/** + * Implementation of private_initiator_init_t.build_ke_payload. + */ +static void build_ke_payload(private_initiator_init_t *this, message_t *request) +{ + ke_payload_t *ke_payload; + chunk_t key_data; + diffie_hellman_group_t dh_group; + + this->logger->log(this->logger, CONTROL|LEVEL1, "Building KE payload"); + + this->diffie_hellman->get_my_public_value(this->diffie_hellman,&key_data); + dh_group = this->diffie_hellman->get_dh_group(this->diffie_hellman); + + ke_payload = ke_payload_create(); + ke_payload->set_dh_group_number(ke_payload, dh_group); + ke_payload->set_key_exchange_data(ke_payload, key_data); + + chunk_free(&key_data); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add KE payload to message"); + request->add_payload(request, (payload_t *) ke_payload); +} + +/** + * Implementation of private_initiator_init_t.build_nonce_payload. + */ +static status_t build_nonce_payload(private_initiator_init_t *this, message_t *request) +{ + nonce_payload_t *nonce_payload; + randomizer_t *randomizer; + status_t status; + + this->logger->log(this->logger, CONTROL|LEVEL1, "Building NONCE payload"); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Get pseudo random bytes for NONCE"); + randomizer = this->ike_sa->get_randomizer(this->ike_sa); + + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce)); + if (status != SUCCESS) + { + return status; + } + + this->logger->log(this->logger, RAW|LEVEL2, "Initiator NONCE",&(this->sent_nonce)); + + nonce_payload = nonce_payload_create(); + + nonce_payload->set_nonce(nonce_payload, this->sent_nonce); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add NONCE payload to message"); + request->add_payload(request, (payload_t *) nonce_payload); + return SUCCESS; +} + +/** + * Implementation of state_t.process_message. + */ +static status_t process_message(private_initiator_init_t *this, message_t *message) +{ + this->logger->log(this->logger, ERROR, "In state INITIATOR_INIT, no message is processed"); + return FAILED; +} + +/** + * Implementation of state_t.get_state. + */ +static ike_sa_state_t get_state(private_initiator_init_t *this) +{ + return INITIATOR_INIT; +} + +/** + * Implementation of state_t.destroy. + */ +static void destroy(private_initiator_init_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL3, "Going to destroy initiator_init_t state object"); + + /* destroy diffie hellman object */ + if (this->diffie_hellman != NULL) + { + this->diffie_hellman->destroy(this->diffie_hellman); + } + if (this->sent_nonce.ptr != NULL) + { + free(this->sent_nonce.ptr); + } + free(this); +} + +/** + * Implementation of private_initiator_init_t.destroy_after_state_change + */ +static void destroy_after_state_change (private_initiator_init_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL3, "Going to destroy initiator_init_t state object"); + free(this); +} + +/* + * Described in header. + */ +initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa) +{ + private_initiator_init_t *this = malloc_thing(private_initiator_init_t); + + /* interface functions */ + this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message; + this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state; + this->public.state_interface.destroy = (void (*) (state_t *)) destroy; + + /* public functions */ + this->public.initiate_connection = (status_t (*)(initiator_init_t *, connection_t*)) initiate_connection; + this->public.retry_initiate_connection = (status_t (*)(initiator_init_t *, int )) retry_initiate_connection; + + /* private functions */ + this->destroy_after_state_change = destroy_after_state_change; + this->build_nonce_payload = build_nonce_payload; + this->build_sa_payload = build_sa_payload; + this->build_ke_payload = build_ke_payload; + + /* private data */ + this->ike_sa = ike_sa; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + this->sent_nonce = CHUNK_INITIALIZER; + this->diffie_hellman = NULL; + + return &(this->public); +} diff --git a/src/charon/charon/sa/states/initiator_init.h b/src/charon/charon/sa/states/initiator_init.h new file mode 100644 index 000000000..6b4940a73 --- /dev/null +++ b/src/charon/charon/sa/states/initiator_init.h @@ -0,0 +1,84 @@ +/** + * @file initiator_init.h + * + * @brief Interface of initiator_init_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 INITIATOR_INIT_H_ +#define INITIATOR_INIT_H_ + +#include +#include + + +typedef struct initiator_init_t initiator_init_t; + +/** + * @brief This class represents an IKE_SA state when + * initializing a connection as initiator. + * + * @b Constructors: + * - initiator_init_create() + * + * @ingroup states + */ +struct initiator_init_t { + /** + * The state_t interface. + */ + state_t state_interface; + + /** + * Initiate a new connection with given connection_t object. + * + * @param this calling object + * @param connection connection to initiate + * @return + * - SUCCESS + * - DELETE_ME if something failed + */ + status_t (*initiate_connection) (initiator_init_t *this, connection_t *connection); + + /** + * Retry to initiate a new connection with a specific dh_group_priority. + * + * The dh_group_priority is starting at 1. + * + * @param this calling object + * @param dh_group_priority dh group priority to try with + * @return + * - SUCCESS + * - DELETE_ME if something failed (see log for error) + */ + status_t (*retry_initiate_connection) (initiator_init_t *this, int dh_group_priority); +}; + +/** + * @brief Constructor of class initiator_init_t. + * + * @param ike_sa assigned IKE_SA + * @return created initiator_init_t object + * + * @ingroup states + */ +initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa); + + +#endif /*INITIATOR_INIT_H_*/ diff --git a/src/charon/charon/sa/states/responder_init.c b/src/charon/charon/sa/states/responder_init.c new file mode 100644 index 000000000..10acf645c --- /dev/null +++ b/src/charon/charon/sa/states/responder_init.c @@ -0,0 +1,568 @@ +/** + * @file responder_init.c + * + * @brief Implementation of responder_init_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "responder_init.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct private_responder_init_t private_responder_init_t; + +/** + * Private data of a responder_init_t object. + * + */ +struct private_responder_init_t { + /** + * Methods of the state_t interface. + */ + responder_init_t public; + + /** + * Assigned IKE_SA. + */ + protected_ike_sa_t *ike_sa; + + /** + * Diffie Hellman object used to compute shared secret. + */ + diffie_hellman_t *diffie_hellman; + + /** + * Diffie Hellman group number from selected IKE proposal. + */ + u_int16_t dh_group_number; + + /** + * Priority used to get matching dh_group number. + */ + u_int16_t dh_group_priority; + + /** + * Sent nonce value. + * + * This value is passed to the next state of type IKE_SA_INIT_RESPONDED. + */ + chunk_t sent_nonce; + + /** + * Received nonce value + * + * This value is passed to the next state of type IKE_SA_INIT_RESPONDED. + */ + chunk_t received_nonce; + + /** + * Selected proposal + */ + proposal_t *proposal; + + /** + * Logger used to log data . + * + * Is logger of ike_sa! + */ + logger_t *logger; + + /** + * Handles received SA payload and builds the SA payload for the response. + * + * @param this calling object + * @param sa_request The received SA payload + * @param response the SA payload is added to this response message_t object. + * @return + * - DELETE_ME + * - SUCCESS + */ + status_t (*build_sa_payload) (private_responder_init_t *this,sa_payload_t *sa_request, message_t *response); + + /** + * Handles received KE payload and builds the KE payload for the response. + * + * @param this calling object + * @param ke_request The received KE payload + * @param response the KE payload is added to this response message_t object. + * - DELETE_ME + * - SUCCESS + */ + status_t (*build_ke_payload) (private_responder_init_t *this,ke_payload_t *ke_request, message_t *response); + + /** + * Handles received NONCE payload and builds the NONCE payload for the response. + * + * @param this calling object + * @param nonce_request The received NONCE payload + * @param response the NONCE payload is added to this response message_t object. + * - DELETE_ME + * - SUCCESS + */ + status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response); + + /** + * Sends a IKE_SA_INIT reply containing a notify payload. + * + * @param this calling object + * @param notify_payload notify_payload to process + */ + status_t (*process_notify_payload) (private_responder_init_t *this, notify_payload_t *notify_payload); + + /** + * Destroy function called internally of this class after change + * to state IKE_SA_INIT_RESPONDED succeeded. + * + * This destroy function does not destroy objects which were passed to the new state. + * + * @param this calling object + */ + void (*destroy_after_state_change) (private_responder_init_t *this); + +}; + +/** + * Implementation of state_t.process_message. + */ +static status_t process_message(private_responder_init_t *this, message_t *message) +{ + ike_sa_init_responded_t *next_state; + chunk_t ike_sa_init_response_data; + chunk_t ike_sa_init_request_data; + sa_payload_t *sa_request = NULL; + ke_payload_t *ke_request = NULL; + nonce_payload_t *nonce_request = NULL; + host_t *source, *destination; + connection_t *connection; + iterator_t *payloads; + message_t *response; + status_t status; + + if (message->get_exchange_type(message) != IKE_SA_INIT) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Message of type %s not supported in state responder_init",mapping_find(exchange_type_m,message->get_exchange_type(message))); + return DELETE_ME; + } + if (!message->get_request(message)) + { + this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT responses not allowed state ike_sa_init_responded"); + return DELETE_ME; + } + + /* this is the first message to process, so get host infos */ + source = message->get_source(message); + destination = message->get_destination(message); + + connection = charon->connections->get_connection_by_hosts(charon->connections, destination, source); + if (connection == NULL) + { + /* no configuration matches given hosts */ + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request does not match any available connection. Deleting IKE_SA"); + /* TODO: inform requestor */ + return DELETE_ME; + } + this->ike_sa->set_connection(this->ike_sa,connection); + + /* parse incoming message */ + status = message->parse_body(message, NULL, NULL); + if (status != SUCCESS) + { + if (status == NOT_SUPPORTED) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contains unsupported payload with critical flag set. " + "Deleting IKE_SA"); + this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER); + } + else + { + this->logger->log(this->logger, AUDIT, "Unable to parse IKE_SA_INIT request. Deleting IKE_SA"); + } + return DELETE_ME; + } + + payloads = message->get_payload_iterator(message); + while (payloads->has_next(payloads)) + { + payload_t *payload; + + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + case SECURITY_ASSOCIATION: + { + sa_request = (sa_payload_t*)payload; + break; + } + case KEY_EXCHANGE: + { + ke_request = (ke_payload_t*)payload; + break; + } + case NONCE: + { + nonce_request = (nonce_payload_t*)payload; + break; + } + case NOTIFY: + { + notify_payload_t *notify_payload = (notify_payload_t *) payload; + status = this->process_notify_payload(this, notify_payload); + if (status != SUCCESS) + { + payloads->destroy(payloads); + return status; + } + } + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload)); + break; + } + } + } + payloads->destroy(payloads); + + /* check if we have all payloads */ + if (!(sa_request && ke_request && nonce_request)) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. Deleting IKE_SA"); + return DELETE_ME; + } + + this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, FALSE, &response); + + status = this->build_sa_payload(this, sa_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + + status = this->build_ke_payload(this, ke_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + + status = this->build_nonce_payload(this, nonce_request, response); + if (status != SUCCESS) + { + response->destroy(response); + return status; + } + + /* derive all the keys used in the IKE_SA */ + status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->received_nonce, this->sent_nonce); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Transform objects could not be created from selected proposal. Deleting IKE_SA"); + return DELETE_ME; + } + + /* message can now be sent (must not be destroyed) */ + status = this->ike_sa->send_response(this->ike_sa, response); + if (status != SUCCESS) + { + this->logger->log(this->logger, AUDIT, "Unable to send IKE_SA_INIT response. Deleting IKE_SA"); + response->destroy(response); + return DELETE_ME; + } + + /* state can now be changed */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Create next state object of type IKE_SA_INIT_RESPONDED"); + + response = this->ike_sa->get_last_responded_message(this->ike_sa); + ike_sa_init_response_data = response->get_packet_data(response); + ike_sa_init_request_data = message->get_packet_data(message); + + next_state = ike_sa_init_responded_create(this->ike_sa, this->received_nonce, this->sent_nonce,ike_sa_init_request_data, + ike_sa_init_response_data); + + /* state can now be changed */ + this->ike_sa->set_new_state(this->ike_sa, (state_t *) next_state); + this->destroy_after_state_change(this); + + return SUCCESS; +} + +/** + * Implementation of private_initiator_init_t.build_sa_payload. + */ +static status_t build_sa_payload(private_responder_init_t *this,sa_payload_t *sa_request, message_t *response) +{ + proposal_t *proposal; + linked_list_t *proposal_list; + connection_t *connection; + sa_payload_t* sa_payload; + algorithm_t *algo; + + connection = this->ike_sa->get_connection(this->ike_sa); + + this->logger->log(this->logger, CONTROL | LEVEL2, "Process received SA payload"); + + /* get the list of suggested proposals */ + proposal_list = sa_request->get_proposals (sa_request); + + /* select proposal */ + this->proposal = connection->select_proposal(connection, proposal_list); + while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS) + { + proposal->destroy(proposal); + } + proposal_list->destroy(proposal_list); + if (this->proposal == NULL) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain any acceptable proposals. Deleting IKE_SA"); + this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER); + return DELETE_ME; + } + /* get selected DH group to force policy, this is very restrictive!? */ + this->proposal->get_algorithm(this->proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, &algo); + this->dh_group_number = algo->algorithm; + + this->logger->log(this->logger, CONTROL | LEVEL2, "SA Payload processed"); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Building SA payload"); + sa_payload = sa_payload_create_from_proposal(this->proposal); + this->logger->log(this->logger, CONTROL|LEVEL2, "add SA payload to message"); + response->add_payload(response,(payload_t *) sa_payload); + + return SUCCESS; +} + +/** + * Implementation of private_initiator_init_t.build_ke_payload. + */ +static status_t build_ke_payload(private_responder_init_t *this,ke_payload_t *ke_request, message_t *response) +{ + diffie_hellman_group_t group; + ke_payload_t *ke_payload; + diffie_hellman_t *dh; + chunk_t key_data; + + this->logger->log(this->logger, CONTROL | LEVEL2, "Process received KE payload"); + group = ke_request->get_dh_group_number(ke_request); + + if (group == MODP_UNDEFINED) + { + this->logger->log(this->logger, AUDIT, "No diffie hellman group to select. Deleting IKE_SA"); + return DELETE_ME; + } + + if (this->dh_group_number != group) + { + u_int16_t accepted_group; + chunk_t accepted_group_chunk; + /* group not same as selected one + * Maybe key exchange payload is before SA payload */ + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain a acceptable diffie hellman group. Deleting IKE_SA"); + + accepted_group = htons(this->dh_group_number); + accepted_group_chunk.ptr = (u_int8_t*) &(accepted_group); + accepted_group_chunk.len = 2; + this->ike_sa->send_notify(this->ike_sa,IKE_SA_INIT,INVALID_KE_PAYLOAD,accepted_group_chunk); + return DELETE_ME; + } + + /* create diffie hellman object to handle DH exchange */ + dh = diffie_hellman_create(group); + if (dh == NULL) + { + this->logger->log(this->logger, AUDIT, "Could not generate DH object with group %d. Deleting IKE_SA", + mapping_find(diffie_hellman_group_m,group) ); + return DELETE_ME; + } + this->logger->log(this->logger, CONTROL | LEVEL2, "Set other DH public value"); + + dh->set_other_public_value(dh, ke_request->get_key_exchange_data(ke_request)); + + this->diffie_hellman = dh; + + this->logger->log(this->logger, CONTROL | LEVEL2, "KE Payload processed."); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Building KE payload"); + this->diffie_hellman->get_my_public_value(this->diffie_hellman,&key_data); + + ke_payload = ke_payload_create(); + ke_payload->set_key_exchange_data(ke_payload,key_data); + ke_payload->set_dh_group_number(ke_payload, this->dh_group_number); + chunk_free(&key_data); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add KE payload to message"); + response->add_payload(response,(payload_t *) ke_payload); + + return SUCCESS; +} + +/** + * Implementation of private_responder_init_t.build_nonce_payload. + */ +static status_t build_nonce_payload(private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response) +{ + nonce_payload_t *nonce_payload; + randomizer_t *randomizer; + status_t status; + + this->logger->log(this->logger, CONTROL | LEVEL2, "Process received NONCE payload"); + free(this->received_nonce.ptr); + this->received_nonce = CHUNK_INITIALIZER; + + this->logger->log(this->logger, CONTROL | LEVEL2, "Get NONCE value and store it"); + this->received_nonce = nonce_request->get_nonce(nonce_request); + + this->logger->log(this->logger, CONTROL | LEVEL2, "Create new NONCE value."); + + randomizer = this->ike_sa->get_randomizer(this->ike_sa); + status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce)); + if (status != SUCCESS) + { + return status; + } + + this->logger->log(this->logger, CONTROL|LEVEL2, "Building NONCE payload"); + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, this->sent_nonce); + + this->logger->log(this->logger, CONTROL|LEVEL2, "Add NONCE payload to message"); + response->add_payload(response,(payload_t *) nonce_payload); + + return SUCCESS; +} + +/** + * Implementation of private_responder_init_t.process_notify_payload. + */ +static status_t process_notify_payload(private_responder_init_t *this, notify_payload_t *notify_payload) +{ + notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); + + this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s", + mapping_find(notify_message_type_m, notify_message_type)); + + if (notify_payload->get_protocol_id(notify_payload) != PROTO_IKE) + { + this->logger->log(this->logger, ERROR | LEVEL1, "Notify reply not for IKE protocol."); + return FAILED; + } + switch (notify_message_type) + { + default: + { + this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.", + notify_message_type); + return SUCCESS; + } + } +} + +/** + * Implementation of state_t.get_state. + */ +static ike_sa_state_t get_state(private_responder_init_t *this) +{ + return RESPONDER_INIT; +} + +/** + * Implementation of state_t.destroy. + */ +static void destroy(private_responder_init_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder init state object"); + + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy sent nonce"); + chunk_free(&(this->sent_nonce)); + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy received nonce"); + chunk_free(&(this->received_nonce)); + + if (this->diffie_hellman != NULL) + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy diffie_hellman_t hellman object"); + this->diffie_hellman->destroy(this->diffie_hellman); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy object"); + free(this); +} + +/** + * Implementation of private_responder_init_t.destroy_after_state_change + */ +static void destroy_after_state_change (private_responder_init_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder_init_t state object"); + + /* destroy diffie hellman object */ + if (this->diffie_hellman != NULL) + { + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy diffie_hellman_t object"); + this->diffie_hellman->destroy(this->diffie_hellman); + } + if (this->proposal) + { + this->proposal->destroy(this->proposal); + } + + this->logger->log(this->logger, CONTROL | LEVEL2, "Destroy object"); + free(this); +} + +/* + * Described in header. + */ +responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa) +{ + private_responder_init_t *this = malloc_thing(private_responder_init_t); + + /* interface functions */ + this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message; + this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state; + this->public.state_interface.destroy = (void (*) (state_t *)) destroy; + + /* private functions */ + this->build_sa_payload = build_sa_payload; + this->build_ke_payload = build_ke_payload; + this->build_nonce_payload = build_nonce_payload; + this->destroy_after_state_change = destroy_after_state_change; + this->process_notify_payload = process_notify_payload; + + /* private data */ + this->ike_sa = ike_sa; + this->logger = logger_manager->get_logger(logger_manager, IKE_SA); + this->sent_nonce = CHUNK_INITIALIZER; + this->received_nonce = CHUNK_INITIALIZER; + this->dh_group_number = MODP_UNDEFINED; + this->diffie_hellman = NULL; + this->proposal = NULL; + + return &(this->public); +} diff --git a/src/charon/charon/sa/states/responder_init.h b/src/charon/charon/sa/states/responder_init.h new file mode 100644 index 000000000..c8ba73ea3 --- /dev/null +++ b/src/charon/charon/sa/states/responder_init.h @@ -0,0 +1,68 @@ +/** + * @file responder_init.h + * + * @brief Interface of responder_init_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 RESPONDER_INIT_H_ +#define RESPONDER_INIT_H_ + +#include +#include + + +typedef struct responder_init_t responder_init_t; + +/** + * @brief This class represents an IKE_SA state when + * initializing a connection as responder. + * + * @b Constructors: + * - responder_init_create() + * + * @ingroup states + */ +struct responder_init_t { + /** + * The state_t interface. + */ + state_t state_interface; +}; + +/** + * Constructor of class responder_init_t. + * + * The following functions of the assigned protected_ike_sa_t object are being called with + * valid values after successfully processing a received message and before changing + * to next state IKE_SA_INIT_RESPONDED: + * - protected_ike_sa_t.set_connection() + * - protected_ike_sa_t.set_my_host() + * - protected_ike_sa_t.set_other_host() + * - protected_ike_sa_t.compute_secrets() + * - protected_ike_sa_t.create_transforms_from_proposal() + * + * @param ike_sa assigned IKE_SA + * + * @return responder_init_t object + * + * @ingroup states + */ +responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa); + +#endif /*RESPONDER_INIT_H_*/ diff --git a/src/charon/charon/sa/states/state.c b/src/charon/charon/sa/states/state.c new file mode 100644 index 000000000..595f5abbb --- /dev/null +++ b/src/charon/charon/sa/states/state.c @@ -0,0 +1,37 @@ +/** + * @file state.c + * + * @brief Interface state_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "state.h" + + +/** + * String mappings for ike_sa_state_t. + */ +mapping_t ike_sa_state_m[] = { + {INITIATOR_INIT, "INITIATOR_INIT"}, + {RESPONDER_INIT, "RESPONDER_INIT"}, + {IKE_SA_INIT_REQUESTED, "IKE_SA_INIT_REQUESTED"}, + {IKE_SA_INIT_RESPONDED, "IKE_SA_INIT_RESPONDED"}, + {IKE_AUTH_REQUESTED, "IKE_AUTH_REQUESTED"}, + {IKE_SA_ESTABLISHED, "IKE_SA_ESTABLISHED"}, + {MAPPING_END, NULL} +}; diff --git a/src/charon/charon/sa/states/state.h b/src/charon/charon/sa/states/state.h new file mode 100644 index 000000000..c93068d35 --- /dev/null +++ b/src/charon/charon/sa/states/state.h @@ -0,0 +1,166 @@ +/** + * @file state.h + * + * @brief Interface state_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 STATE_H_ +#define STATE_H_ + +#include +#include +#include + +typedef enum ike_sa_state_t ike_sa_state_t; + +/** + * States in which a IKE_SA can be. + * + * @todo Support of more states (CHILD_SA_REQUESTED, etc...) + * + * @ingroup states + */ +enum ike_sa_state_t { + + /** + * @brief IKE_SA is in initial state as initiator and is going to initiate a new connection. + * + * Next state following this state is IKE_SA_INIT_REQUESTED. + * + * Implemented in class initiator_init_t. + */ + INITIATOR_INIT = 1, + + /** + * @brief IKE_SA is in initial state as responder and is going to respond to a initiated connection. + * + * Next state following this state is IKE_SA_INIT_RESPONDED. + * + * Implemented in class responder_init_t. + */ + RESPONDER_INIT = 2, + + /** + * @brief A IKE_SA_INIT request was sent. In this state a reply of type IKE_SA_INIT is expected. + * + * Two states are possible as next states: + * - IKE_AUTH_REQUESTED if IKE_SA_INIT reply could successfully processed and IKE_AUTH request could be sent. + * - INITIATOR_INIT if selected DH group was not the one selected by other peer. + * + * Implemented in class ike_sa_init_requested_t. + */ + IKE_SA_INIT_REQUESTED = 3, + + /** + * @brief A IKE_SA_INIT response was sent. In this state a request of type IKE_AUTH is expected. + * + * Next state following this state is IKE_SA_ESTABLISHED. + * + * Implemented in class ike_sa_init_responded_t. + */ + IKE_SA_INIT_RESPONDED = 4, + + /** + * @brief An IKE_AUTH request was sent after a successful IKE_SA_INIT-exchange. + * + * Next state following this state is IKE_SA_ESTABLISHED. + * + * Implemented in class ike_auth_requested_t. + */ + IKE_AUTH_REQUESTED = 5, + + /** + * @brief An IKE_AUTH exchange was successfuly handled either as initiator or responder. + * + * In this state, all the informations for an IKE_SA and one CHILD_SA are known. + * + * Implemented in class ike_sa_established_t. + */ + IKE_SA_ESTABLISHED = 6 +}; + + +/** + * String mappings for ike_sa_state_t. + */ +extern mapping_t ike_sa_state_m[]; + + +typedef struct state_t state_t; + +/** + * @brief This interface represents an IKE_SA state. + * + * A state_t object is responsible to handle incoming messages. + * + * It's the responsibility of the state_t object to parse the body of the message and to process each + * payload. + * + * Needed Configurations and transform objects can be retrieved over an internal stored protected_ike_sa_t object + * which is passed to a state_t object when creating it (see different constructors). + * + * The following states are supported and implemented: + * - INITIATOR_INIT: implemented in initiator_init_t + * - RESPONDER_INIT: implemented in responder_init_t + * - IKE_SA_INIT_REQUESTED: implemented in ike_sa_init_requested_t + * - IKE_SA_INIT_RESPONDED: implemented in ike_sa_init_responded_t + * - IKE_AUTH_REQUESTED: implemented in ike_auth_requested_t + * - IKE_SA_ESTABLISHED: implemented in ike_sa_established_t + * + * @b Constructors: + * - initiator_init_create() + * - responder_init_create() + * - ike_sa_init_requested_create() + * - ike_sa_init_responded_create() + * - ike_auth_requested_create() + * - ike_sa_established_create() + * + * @ingroup states + */ +struct state_t { + + /** + * @brief Processes a incoming IKEv2-Message of type message_t. + * + * @param this calling object + * @param[in] message message_t object to process + * @return + * - SUCCESSFUL + * - FAILED + * - DELETE_ME if belonging IKE_SA should be deleted + */ + status_t (*process_message) (state_t *this,message_t *message); + + /** + * @brief Get the current state representing by this state_t object. + * + * @param this calling object + * @return state + */ + ike_sa_state_t (*get_state) (state_t *this); + + /** + * @brief Destroys a state_t object. + * + * @param this calling object + */ + void (*destroy) (state_t *this); +}; + +#endif /*STATE_H_*/ diff --git a/src/charon/charon/threads/Makefile.threads b/src/charon/charon/threads/Makefile.threads new file mode 100644 index 000000000..949c1ad24 --- /dev/null +++ b/src/charon/charon/threads/Makefile.threads @@ -0,0 +1,39 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +THREADS_DIR= $(CHARON_DIR)threads/ + +CHARON_OBJS+= $(BUILD_DIR)receiver.o +$(BUILD_DIR)receiver.o : $(THREADS_DIR)receiver.c $(THREADS_DIR)receiver.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)scheduler.o +$(BUILD_DIR)scheduler.o : $(THREADS_DIR)scheduler.c $(THREADS_DIR)scheduler.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)sender.o +$(BUILD_DIR)sender.o : $(THREADS_DIR)sender.c $(THREADS_DIR)sender.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)thread_pool.o +$(BUILD_DIR)thread_pool.o : $(THREADS_DIR)thread_pool.c $(THREADS_DIR)thread_pool.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)kernel_interface.o +$(BUILD_DIR)kernel_interface.o :$(THREADS_DIR)kernel_interface.c $(THREADS_DIR)kernel_interface.h + $(CC) $(CFLAGS) -c -o $@ $< + +CHARON_OBJS+= $(BUILD_DIR)stroke_interface.o +$(BUILD_DIR)stroke_interface.o :$(THREADS_DIR)stroke_interface.c $(THREADS_DIR)stroke_interface.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/charon/threads/kernel_interface.c b/src/charon/charon/threads/kernel_interface.c new file mode 100644 index 000000000..679cf69ee --- /dev/null +++ b/src/charon/charon/threads/kernel_interface.c @@ -0,0 +1,729 @@ +/** + * @file kernel_interface.c + * + * @brief Implementation of kernel_interface_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2003 Herbert Xu. + * + * Contains modified parts 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 "kernel_interface.h" + +#include +#include + + +#define KERNEL_ESP 50 +#define KERNEL_AH 51 + +#define SPD_PRIORITY 1024 + +#define XFRM_DATA_LENGTH 512 + + +typedef struct xfrm_data_t xfrm_data_t; + +/** + * Lenght/Type/data struct for userdata in xfrm + * We dont use the "I-don't-know-where-they-come-from"-structs + * used in the kernel. + */ +struct xfrm_data_t { + /** + * length of the data + */ + u_int16_t length; + + /** + * type of data + */ + u_int16_t type; + + /** + * and the data itself, for different purposes + */ + union { + /** algorithm */ + struct xfrm_algo algo; + /** policy tmpl */ + struct xfrm_user_tmpl tmpl[2]; + }; +}; + + +typedef struct netlink_message_t netlink_message_t; + +/** + * Representation of ANY netlink message used + */ +struct netlink_message_t { + + /** + * header of the netlink message + */ + struct nlmsghdr hdr; + + union { + /** error message */ + struct nlmsgerr e; + /** message for spi allocation */ + struct xfrm_userspi_info spi; + /** message for SA manipulation */ + struct xfrm_usersa_id sa_id; + /** message for SA installation */ + struct xfrm_usersa_info sa; + /** message for policy manipulation */ + struct xfrm_userpolicy_id policy_id; + /** message for policy installation */ + struct xfrm_userpolicy_info policy; + }; + u_int8_t data[XFRM_DATA_LENGTH]; +}; + + +typedef struct private_kernel_interface_t private_kernel_interface_t; + + /** + * @brief 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; + + /** + * Netlink communication socket. + */ + int socket; + + /** + * Process id of kernel thread + */ + pid_t pid; + + /** + * Sequence number for messages. + */ + u_int32_t seq; + + /** + * List of responded messages. + */ + linked_list_t *responses; + + /** + * Thread which receives messages. + */ + pthread_t thread; + + /** + * Mutex locks access to replies list. + */ + pthread_mutex_t mutex; + + /** + * Condvar allows signaling of threads waiting for a reply. + */ + pthread_cond_t condvar; + + /** + * Logger for XFRM stuff + */ + logger_t *logger; + + /** + * Function for the thread, receives messages. + */ + void (*receive_messages) (private_kernel_interface_t *this); + + /** + * Sends a netlink_message_t down to the kernel and wait for reply. + */ + status_t (*send_message) (private_kernel_interface_t *this, netlink_message_t *request, netlink_message_t **response); +}; + +/** + * In the kernel, algorithms are identified as strings, we use our + * mapping functions... + * Algorithms for encryption. + * TODO: Add missing algorithm strings + */ +mapping_t kernel_encryption_algs_m[] = { + {ENCR_DES_IV64, ""}, + {ENCR_DES, "des"}, + {ENCR_3DES, "des3_ede"}, + {ENCR_RC5, ""}, + {ENCR_IDEA, "idea"}, + {ENCR_CAST, "cast128"}, + {ENCR_BLOWFISH, "blowfish"}, + {ENCR_3IDEA, ""}, + {ENCR_DES_IV32, ""}, + {ENCR_NULL, ""}, + {ENCR_AES_CBC, "aes"}, + {ENCR_AES_CTR, ""}, + {MAPPING_END, NULL} +}; +/** + * In the kernel, algorithms are identified as strings, we use our + * mapping functions... + * Algorithms for integrity protection. + * TODO: Add missing algorithm strings + */ +mapping_t kernel_integrity_algs_m[] = { + {AUTH_HMAC_MD5_96, "md5"}, + {AUTH_HMAC_SHA1_96, "sha1"}, + {AUTH_DES_MAC, ""}, + {AUTH_KPDK_MD5, ""}, + {AUTH_AES_XCBC_96, ""}, + {MAPPING_END, NULL} +}; + + +/** + * Implementation of kernel_interface_t.get_spi. + */ +static status_t get_spi(private_kernel_interface_t *this, + host_t *src, host_t *dest, + protocol_id_t protocol, u_int32_t reqid, + u_int32_t *spi) +{ + netlink_message_t request, *response; + status_t status = SUCCESS; + + + this->logger->log(this->logger, CONTROL|LEVEL2, "getting spi"); + + memset(&request, 0, sizeof(request)); + request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.spi))); + request.hdr.nlmsg_flags = NLM_F_REQUEST; + request.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI; + request.spi.info.saddr = src->get_xfrm_addr(src); + request.spi.info.id.daddr = dest->get_xfrm_addr(dest); + request.spi.info.mode = TRUE; /* tunnel mode */ + request.spi.info.reqid = reqid; + request.spi.info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + request.spi.info.family = PF_INET; + request.spi.min = 0xc0000000; + request.spi.max = 0xcFFFFFFF; + + if (this->send_message(this, &request, &response) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "netlink communication failed"); + return FAILED; + } + else if (response->hdr.nlmsg_type == NLMSG_ERROR) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got an error: %s", + strerror(-response->e.error)); + status = FAILED; + } + else if (response->hdr.nlmsg_type != XFRM_MSG_NEWSA) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got a unknown reply"); + status = FAILED; + } + else if (response->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(response->sa))) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got an invalid reply"); + status = FAILED; + } + else + { + *spi = response->sa.id.spi; + } + free(response); + + return status; +} + +/** + * Implementation of kernel_interface_t.add_sa. + */ +static status_t add_sa( private_kernel_interface_t *this, + host_t *me, + host_t *other, + u_int32_t spi, + int protocol, + u_int32_t reqid, + encryption_algorithm_t enc_alg, + chunk_t encryption_key, + integrity_algorithm_t int_alg, + chunk_t integrity_key, + bool replace) +{ + netlink_message_t request, *response; + memset(&request, 0, sizeof(request)); + status_t status = SUCCESS; + + this->logger->log(this->logger, CONTROL|LEVEL2, "adding SA"); + + request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + request.hdr.nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + + request.sa.saddr = me->get_xfrm_addr(me); + request.sa.id.daddr = other->get_xfrm_addr(other); + + request.sa.id.spi = spi; + request.sa.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + request.sa.family = me->get_family(me); + request.sa.mode = TRUE; /* tunnel mode */ + request.sa.replay_window = 32; + request.sa.reqid = reqid; + request.sa.lft.soft_byte_limit = XFRM_INF; + request.sa.lft.soft_packet_limit = XFRM_INF; + request.sa.lft.hard_byte_limit = XFRM_INF; + request.sa.lft.hard_packet_limit = XFRM_INF; + + request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.sa))); + + if (enc_alg != ENCR_UNDEFINED) + { + xfrm_data_t *data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); + + data->type = XFRMA_ALG_CRYPT; + data->length = 4 + sizeof(data->algo) + encryption_key.len; + data->algo.alg_key_len = encryption_key.len * 8; + request.hdr.nlmsg_len += data->length; + if (request.hdr.nlmsg_len > sizeof(request)) + { + return FAILED; + } + strcpy(data->algo.alg_name, mapping_find(kernel_encryption_algs_m, enc_alg)); + memcpy(data->algo.alg_key, encryption_key.ptr, encryption_key.len); + } + + if (int_alg != AUTH_UNDEFINED) + { + xfrm_data_t *data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); + + data->type = XFRMA_ALG_AUTH; + data->length = 4 + sizeof(data->algo) + integrity_key.len; + data->algo.alg_key_len = integrity_key.len * 8; + request.hdr.nlmsg_len += data->length; + if (request.hdr.nlmsg_len > sizeof(request)) + { + return FAILED; + } + strcpy(data->algo.alg_name, mapping_find(kernel_integrity_algs_m, int_alg)); + memcpy(data->algo.alg_key, integrity_key.ptr, integrity_key.len); + } + + /* TODO: add IPComp here*/ + + if (this->send_message(this, &request, &response) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "netlink communication failed"); + return FAILED; + } + else if (response->hdr.nlmsg_type != NLMSG_ERROR) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA not acknowledged"); + status = FAILED; + } + else if (response->e.error) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA got error %s", + strerror(-response->e.error)); + status = FAILED; + } + + free(response); + return status; +} + +static status_t del_sa( private_kernel_interface_t *this, + host_t *dst, + u_int32_t spi, + protocol_id_t protocol) +{ + netlink_message_t request, *response; + memset(&request, 0, sizeof(request)); + status_t status = SUCCESS; + + this->logger->log(this->logger, CONTROL|LEVEL2, "deleting SA"); + + request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + request.hdr.nlmsg_type = XFRM_MSG_DELSA; + + request.sa_id.daddr = dst->get_xfrm_addr(dst); + + request.sa_id.spi = spi; + request.sa_id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + request.sa_id.family = dst->get_family(dst); + + request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.sa_id))); + + if (this->send_message(this, &request, &response) != SUCCESS) + { + return FAILED; + } + else if (response->hdr.nlmsg_type != NLMSG_ERROR) + { + status = FAILED; + } + else if (response->e.error) + { + status = FAILED; + } + + free(response); + return status; +} + +/** + * Implementation of kernel_interface_t.add_policy. + */ +static status_t add_policy(private_kernel_interface_t *this, + host_t *me, host_t *other, + host_t *src, host_t *dst, + u_int8_t src_hostbits, u_int8_t dst_hostbits, + int direction, int upper_proto, + bool ah, bool esp, + u_int32_t reqid) +{ + netlink_message_t request, *response; + status_t status = SUCCESS; + + this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy"); + + memset(&request, 0, sizeof(request)); + request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + request.policy.sel.sport = htons(src->get_port(src)); + request.policy.sel.dport = htons(dst->get_port(dst)); + request.policy.sel.sport_mask = (request.policy.sel.sport) ? ~0 : 0; + request.policy.sel.dport_mask = (request.policy.sel.dport) ? ~0 : 0; + request.policy.sel.saddr = src->get_xfrm_addr(src); + request.policy.sel.daddr = dst->get_xfrm_addr(dst); + request.policy.sel.prefixlen_s = src_hostbits; + request.policy.sel.prefixlen_d = dst_hostbits; + request.policy.sel.proto = upper_proto; + request.policy.sel.family = src->get_family(src); + + request.hdr.nlmsg_type = XFRM_MSG_NEWPOLICY; + request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.policy))); + + request.policy.dir = direction; + request.policy.priority = SPD_PRIORITY; + request.policy.action = XFRM_POLICY_ALLOW; + request.policy.share = XFRM_SHARE_ANY; + + request.policy.lft.soft_byte_limit = XFRM_INF; + request.policy.lft.soft_packet_limit = XFRM_INF; + request.policy.lft.hard_byte_limit = XFRM_INF; + request.policy.lft.hard_packet_limit = XFRM_INF; + + if (esp || ah) + { + xfrm_data_t *data; + int tmpl_pos = 0; + data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); + data->type = XFRMA_TMPL; + if (esp) + { + data->tmpl[tmpl_pos].reqid = reqid; + data->tmpl[tmpl_pos].id.proto = KERNEL_ESP; + data->tmpl[tmpl_pos].aalgos = data->tmpl[tmpl_pos].ealgos = data->tmpl[tmpl_pos].calgos = ~0; + data->tmpl[tmpl_pos].mode = TRUE; + + data->tmpl[tmpl_pos].saddr = me->get_xfrm_addr(me); + data->tmpl[tmpl_pos].id.daddr = me->get_xfrm_addr(other); + + tmpl_pos++; + } + if (ah) + { + data->tmpl[tmpl_pos].reqid = reqid; + data->tmpl[tmpl_pos].id.proto = KERNEL_AH; + data->tmpl[tmpl_pos].aalgos = data->tmpl[tmpl_pos].ealgos = data->tmpl[tmpl_pos].calgos = ~0; + data->tmpl[tmpl_pos].mode = TRUE; + + data->tmpl[tmpl_pos].saddr = me->get_xfrm_addr(me); + data->tmpl[tmpl_pos].id.daddr = other->get_xfrm_addr(other); + + tmpl_pos++; + } + data->length = 4 + sizeof(struct xfrm_user_tmpl) * tmpl_pos; + request.hdr.nlmsg_len += data->length; + } + + if (this->send_message(this, &request, &response) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "netlink communication failed"); + return FAILED; + } + else if (response->hdr.nlmsg_type != NLMSG_ERROR) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY not acknowledged"); + status = FAILED; + } + else if (response->e.error) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY got error %s", + strerror(-response->e.error)); + status = FAILED; + } + + free(response); + return status; +} + +/** + * Implementation of kernel_interface_t.del_policy. + */ +static status_t del_policy(private_kernel_interface_t *this, + host_t *me, host_t *other, + host_t *src, host_t *dst, + u_int8_t src_hostbits, u_int8_t dst_hostbits, + int direction, int upper_proto) +{ + netlink_message_t request, *response; + status_t status = SUCCESS; + + + this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy"); + + memset(&request, 0, sizeof(request)); + request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + request.policy_id.sel.sport = htons(src->get_port(src)); + request.policy_id.sel.dport = htons(dst->get_port(dst)); + request.policy_id.sel.sport_mask = (request.policy.sel.sport) ? ~0 : 0; + request.policy_id.sel.dport_mask = (request.policy.sel.dport) ? ~0 : 0; + request.policy_id.sel.saddr = src->get_xfrm_addr(src); + request.policy_id.sel.daddr = dst->get_xfrm_addr(dst); + request.policy_id.sel.prefixlen_s = src_hostbits; + request.policy_id.sel.prefixlen_d = dst_hostbits; + request.policy_id.sel.proto = upper_proto; + request.policy_id.sel.family = src->get_family(src); + + request.policy_id.dir = direction; + + request.hdr.nlmsg_type = XFRM_MSG_DELPOLICY; + request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.policy_id))); + + if (this->send_message(this, &request, &response) != SUCCESS) + { + return FAILED; + } + else if (response->hdr.nlmsg_type != NLMSG_ERROR) + { + status = FAILED; + } + else if (response->e.error) + { + status = FAILED; + } + + free(response); + return status; +} + +/** + * Implementation of private_kernel_interface_t.send_message. + */ +static status_t send_message(private_kernel_interface_t *this, netlink_message_t *request, netlink_message_t **response) +{ + size_t length; + struct sockaddr_nl addr; + + request->hdr.nlmsg_seq = ++this->seq; + request->hdr.nlmsg_pid = this->pid; + + memset(&addr, 0, sizeof(struct sockaddr_nl)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + length = sendto(this->socket,(void *)request, request->hdr.nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); + + if (length < 0) + { + return FAILED; + } + else if (length != request->hdr.nlmsg_len) + { + return FAILED; + } + + pthread_mutex_lock(&(this->mutex)); + + while (TRUE) + { + iterator_t *iterator; + bool found = FALSE; + /* search list, break if found */ + iterator = this->responses->create_iterator(this->responses, TRUE); + while (iterator->has_next(iterator)) + { + netlink_message_t *listed_response; + iterator->current(iterator, (void**)&listed_response); + if (listed_response->hdr.nlmsg_seq == request->hdr.nlmsg_seq) + { + /* matches our request, this is the reply */ + *response = listed_response; + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (found) + { + break; + } + /* TODO: we should time out, if something goes wrong!??? */ + pthread_cond_wait(&(this->condvar), &(this->mutex)); + } + + pthread_mutex_unlock(&(this->mutex)); + + return SUCCESS; +} + +/** + * Implementation of private_kernel_interface_t.receive_messages. + */ +static void receive_messages(private_kernel_interface_t *this) +{ + while(TRUE) + { + netlink_message_t response, *listed_response; + while (TRUE) + { + struct sockaddr_nl addr; + socklen_t addr_length; + size_t length; + + addr_length = sizeof(addr); + + response.hdr.nlmsg_type = XFRM_MSG_NEWSA; + length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length); + if (length < 0) + { + if (errno == EINTR) + { + /* interrupted, try again */ + continue; + } + charon->kill(charon, "receiving from netlink socket failed"); + } + if (!NLMSG_OK(&response.hdr, length)) + { + /* bad netlink message */ + continue; + } + if (addr.nl_pid != 0) + { + /* not from kernel. not interested, try another one */ + continue; + } + break; + } + + /* got a valid message. + * requests are handled on our own, + * responses are listed for the requesters + */ + if (response.hdr.nlmsg_flags & NLM_F_REQUEST) + { + /* handle request */ + } + else + { + /* add response to queue */ + listed_response = malloc(sizeof(response)); + memcpy(listed_response, &response, sizeof(response)); + + pthread_mutex_lock(&(this->mutex)); + this->responses->insert_last(this->responses, (void*)listed_response); + pthread_mutex_unlock(&(this->mutex)); + /* signal ALL waiting threads */ + pthread_cond_broadcast(&(this->condvar)); + } + /* get the next one */ + } +} + +/** + * Implementation of kernel_interface_t.destroy. + */ +static void destroy(private_kernel_interface_t *this) +{ + pthread_cancel(this->thread); + pthread_join(this->thread, NULL); + close(this->socket); + this->responses->destroy(this->responses); + free(this); +} + +/* + * Described in header. + */ +kernel_interface_t *kernel_interface_create() +{ + private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t); + + /* 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,encryption_algorithm_t,chunk_t,integrity_algorithm_t,chunk_t,bool))add_sa; + this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*, host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,bool,bool,u_int32_t))add_policy; + this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; + this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int))del_policy; + + this->public.destroy = (void(*)(kernel_interface_t*)) destroy; + + /* private members */ + this->receive_messages = receive_messages; + this->send_message = send_message; + this->pid = getpid(); + this->responses = linked_list_create(); + this->logger = logger_manager->get_logger(logger_manager, XFRM); + pthread_mutex_init(&(this->mutex),NULL); + pthread_cond_init(&(this->condvar),NULL); + this->seq = 0; + this->socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket <= 0) + { + this->responses->destroy(this->responses); + free(this); + charon->kill(charon, "Unable to create netlink socket"); + } + + if (pthread_create(&(this->thread), NULL, (void*(*)(void*))this->receive_messages, this) != 0) + { + this->responses->destroy(this->responses); + close(this->socket); + free(this); + charon->kill(charon, "Unable to create netlink thread"); + } + + return (&this->public); +} diff --git a/src/charon/charon/threads/kernel_interface.h b/src/charon/charon/threads/kernel_interface.h new file mode 100644 index 000000000..ceafa6468 --- /dev/null +++ b/src/charon/charon/threads/kernel_interface.h @@ -0,0 +1,185 @@ +/** + * @file kernel_interface.h + * + * @brief Interface of kernel_interface_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 KERNEL_INTERFACE_H_ +#define KERNEL_INTERFACE_H_ + +#include + +#include +#include + +typedef struct kernel_interface_t kernel_interface_t; + +/** + * @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. + * + * @b Constructors: + * - kernel_interface_create() + * + * @ingroup threads + */ +struct kernel_interface_t { + + /** + * @brief Get a SPI from the kernel. + * + * @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. + * + * @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 enc_alg Algorithm to use for encryption (ESP only) + * @param enc_key Key to use for encryption + * @param int_alg Algorithm to use for integrity protection + * @param int_key Key for integrity protection + * @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, + encryption_algorithm_t enc_alg, + chunk_t enc_key, + integrity_algorithm_t int_alg, + chunk_t int_key, + bool replace); + /** + * @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, so + * traffic applied to a policy. Traffic which + * matches a policy is handled by the SA with the same + * reqid. + * + * @param this calling object + * @param me address of local peer + * @param other address of remote peer + * @param src src address of traffic this policy applies + * @param dst dest address of traffic this policy applies + * @param src_hostbits subnetmask to use for src address + * @param dst_hostbits subnetmask to use for dst address + * @param direction direction of traffic, XFRM_POLICY_OUT, XFRM_POLICY_IN, XFRM_POLICY_FWD + * @param upper_proto upper layer protocol of traffic for this policy (TCP, UDP, ICMP, ...) + * @param ah protect traffic with AH? + * @param esp protect traffic with ESP? + * @param reqid uniqe ID of an SA to use to enforce policy + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*add_policy) (kernel_interface_t *this, + host_t *me, host_t *other, + host_t *src, host_t *dst, + u_int8_t src_hostbits, u_int8_t dst_hostbits, + int direction, int upper_proto, + bool ah, bool esp, + u_int32_t reqid); + + /** + * @brief Remove a policy from the SPD. + * + * @param this calling object + * @param me address of local peer + * @param other address of remote peer + * @param src src address of traffic this policy applies + * @param dst dest address of traffic this policy applies + * @param src_hostbits subnetmask to use for src address + * @param dst_hostbits subnetmask to use for dst address + * @param direction direction of traffic, XFRM_POLICY_OUT, XFRM_POLICY_IN, XFRM_POLICY_FWD + * @param upper_proto upper layer protocol of traffic for this policy (TCP, UDP, ICMP, ...) + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*del_policy) (kernel_interface_t *this, + host_t *me, host_t *other, + host_t *src, host_t *dst, + u_int8_t src_hostbits, u_int8_t dst_hostbits, + int direction, int upper_proto); + + /** + * @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(); + +#endif /*KERNEL_INTERFACE_H_*/ diff --git a/src/charon/charon/threads/receiver.c b/src/charon/charon/threads/receiver.c new file mode 100644 index 000000000..0cf8b7bde --- /dev/null +++ b/src/charon/charon/threads/receiver.c @@ -0,0 +1,128 @@ +/** + * @file receiver.c + * + * @brief Implementation of receiver_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "receiver.h" + +#include +#include +#include +#include +#include +#include +#include + + +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; + + /** + * @brief Thread function started at creation of the receiver object. + * + * @param this calling object + */ + void (*receive_packets) (private_receiver_t *this); + + /** + * Assigned thread. + */ + pthread_t assigned_thread; + + /** + * A logger for the receiver_t object. + */ + logger_t *logger; +}; + +/** + * Implementation of receiver_t.receive_packets. + */ +static void receive_packets(private_receiver_t * this) +{ + packet_t * current_packet; + job_t *current_job; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + this->logger->log(this->logger, CONTROL, "Receiver thread running, thread_id %u", (int)pthread_self()); + + while (1) + { + while (charon->socket->receive(charon->socket,¤t_packet) == SUCCESS) + { + this->logger->log(this->logger, CONTROL | LEVEL1, "Creating job from packet"); + current_job = (job_t *) incoming_packet_job_create(current_packet); + + charon->job_queue->add(charon->job_queue,current_job); + + } + /* bad bad, rebuild the socket ? */ + this->logger->log(this->logger, ERROR, "Receiving from socket failed!"); + } +} + +/** + * Implementation of receiver_t.destroy. + */ +static void destroy(private_receiver_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to terminate receiver thread"); + pthread_cancel(this->assigned_thread); + + pthread_join(this->assigned_thread, NULL); + this->logger->log(this->logger, CONTROL | LEVEL1, "Receiver thread terminated"); + + free(this); +} + +/* + * Described in header. + */ +receiver_t * receiver_create() +{ + private_receiver_t *this = malloc_thing(private_receiver_t); + + this->public.destroy = (void(*)(receiver_t*)) destroy; + this->receive_packets = receive_packets; + + this->logger = logger_manager->get_logger(logger_manager, RECEIVER); + + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))this->receive_packets, this) != 0) + { + this->logger->log(this->logger, ERROR, "Receiver thread could not be started"); + free(this); + charon->kill(charon, "Unable to create receiver thread"); + } + + return &(this->public); +} diff --git a/src/charon/charon/threads/receiver.h b/src/charon/charon/threads/receiver.h new file mode 100644 index 000000000..932774f5f --- /dev/null +++ b/src/charon/charon/threads/receiver.h @@ -0,0 +1,67 @@ +/** + * @file receiver.h + * + * @brief Interface of receiver_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 RECEIVER_H_ +#define RECEIVER_H_ + +#include + + +typedef struct receiver_t receiver_t; + +/** + * @brief Receives packets from the socket and adds them to the job queue. + * + * The receiver starts a thread, wich reads on the blocking socket. If + * data is available, a packet_t object is created , wrapped + * in an incoming_packet_job_t and added to the job queue. + * + * @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 + * - NULL of thread could not be started + * + * @ingroup threads + */ +receiver_t * receiver_create(); + +#endif /*RECEIVER_H_*/ diff --git a/src/charon/charon/threads/scheduler.c b/src/charon/charon/threads/scheduler.c new file mode 100644 index 000000000..47c5d6fb9 --- /dev/null +++ b/src/charon/charon/threads/scheduler.c @@ -0,0 +1,124 @@ +/** + * @file scheduler.c + * + * @brief Implementation of scheduler_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "scheduler.h" + +#include +#include +#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; + + /** + * @brief Get events from the event queue and add them to to job queue. + * + * Thread function started at creation of the scheduler object. + * + * @param this calling object + */ + void (*get_events) (private_scheduler_t *this); + + /** + * Assigned thread. + */ + pthread_t assigned_thread; + + /** + * A logger. + */ + logger_t *logger; +}; + +/** + * 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); + + this->logger->log(this->logger, CONTROL, "Scheduler thread running, thread_id %u", (int)pthread_self()); + + for (;;) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "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 */ + charon->job_queue->add(charon->job_queue, current_job); + this->logger->log(this->logger, CONTROL | LEVEL1, "Got event, added job %s to job-queue.", + mapping_find(job_type_m, current_job->get_type(current_job))); + } +} + +/** + * Implementation of scheduler_t.destroy. + */ +static void destroy(private_scheduler_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to terminate scheduler thread"); + pthread_cancel(this->assigned_thread); + + pthread_join(this->assigned_thread, NULL); + this->logger->log(this->logger, CONTROL | LEVEL1, "Scheduler thread terminated"); + + 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; + this->get_events = get_events; + + this->logger = logger_manager->get_logger(logger_manager, SCHEDULER); + + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))this->get_events, this) != 0) + { + /* thread could not be created */ + this->logger->log(this->logger, ERROR, "Scheduler thread could not be created!"); + free(this); + charon->kill(charon, "Unable to create scheduler thread"); + } + + return &(this->public); +} diff --git a/src/charon/charon/threads/scheduler.h b/src/charon/charon/threads/scheduler.h new file mode 100644 index 000000000..0165a718b --- /dev/null +++ b/src/charon/charon/threads/scheduler.h @@ -0,0 +1,67 @@ +/** + * @file scheduler.h + * + * @brief Interface of scheduler_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SCHEDULER_H_ +#define SCHEDULER_H_ + +#include + +typedef struct scheduler_t scheduler_t; + +/** + * @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(); + +#endif /*SCHEDULER_H_*/ diff --git a/src/charon/charon/threads/sender.c b/src/charon/charon/threads/sender.c new file mode 100644 index 000000000..42d11beb9 --- /dev/null +++ b/src/charon/charon/threads/sender.c @@ -0,0 +1,126 @@ +/** + * @file sender.c + * + * @brief Implementation of sender_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "sender.h" + +#include +#include +#include +#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; + + /** + * @brief The thread function, sends out packets. + * + * @param this calling object + */ + void (*send_packets) (private_sender_t * this); + + /** + * A logger for this sender_t object. + */ + logger_t *logger; + +}; + +/** + * Implementation of private_sender_t.send_packets. + */ +static void send_packets(private_sender_t * this) +{ + packet_t * current_packet; + status_t status; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + this->logger->log(this->logger, CONTROL, "Sender thread running, thread_id %u", (int)pthread_self()); + + while (1) + { + current_packet = charon->send_queue->get(charon->send_queue); + this->logger->log(this->logger, CONTROL|LEVEL1, "Got a packet, sending it"); + status = charon->socket->send(charon->socket,current_packet); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Sending failed, socket returned %s", + mapping_find(status_m, status)); + } + current_packet->destroy(current_packet); + } +} + +/** + * Implementation of sender_t.destroy. + */ +static void destroy(private_sender_t *this) +{ + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to terminate sender thread"); + pthread_cancel(this->assigned_thread); + + pthread_join(this->assigned_thread, NULL); + this->logger->log(this->logger, CONTROL | LEVEL1, "Sender thread terminated"); + + free(this); +} + +/* + * Described in header. + */ +sender_t * sender_create() +{ + private_sender_t *this = malloc_thing(private_sender_t); + + this->send_packets = send_packets; + this->public.destroy = (void(*)(sender_t*)) destroy; + + this->logger = logger_manager->get_logger(logger_manager, SENDER); + + if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))this->send_packets, this) != 0) + { + this->logger->log(this->logger, ERROR, "Sender thread could not be created"); + free(this); + charon->kill(charon, "Unable to create sender thread"); + } + + return &(this->public); +} diff --git a/src/charon/charon/threads/sender.h b/src/charon/charon/threads/sender.h new file mode 100644 index 000000000..ea8124147 --- /dev/null +++ b/src/charon/charon/threads/sender.h @@ -0,0 +1,63 @@ +/** + * @file sender.h + * + * @brief Interface of sender_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SENDER_H_ +#define SENDER_H_ + +#include + +typedef struct sender_t sender_t; + +/** + * @brief Thread responsible for sending packets over the socket. + * + * @b Constructors: + * - sender_create() + * + * @ingroup threads + */ +struct sender_t { + + /** + * @brief Destroys a sender object. + * + * @param sender calling object + */ + void (*destroy) (sender_t *sender); +}; + + +/** + * @brief Create the sender thread. + * + * The thread will start to work, getting packets + * from the send queue and sends them out. + * + * @return + * - sender_t object + * - NULL of thread could not be started + * + * @ingroup threads + */ +sender_t * sender_create(); + +#endif /*SENDER_H_*/ diff --git a/src/charon/charon/threads/stroke_interface.c b/src/charon/charon/threads/stroke_interface.c new file mode 100755 index 000000000..ef5d5f1f6 --- /dev/null +++ b/src/charon/charon/threads/stroke_interface.c @@ -0,0 +1,661 @@ +/** + * @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 "stroke_interface.h" + +#include +#include +#include +#include +#include + + +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; + + /** + * Assigned logger_t object in charon. + */ + logger_t *logger; + + /** + * Logger which logs to stroke + */ + logger_t *stroke_logger; + + /** + * Unix socket to listen for strokes + */ + int socket; + + /** + * Thread which reads from the socket + */ + pthread_t assigned_thread; + + /** + * Read from the socket and handle stroke messages + */ + void (*stroke_receive) (private_stroke_t *this); +}; + +/** + * 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) +{ + /* check for sanity of string pointer and string */ + if (*string == NULL) + { + *string = ""; + } + else if (string < (char**)msg || + string > (char**)msg + sizeof(stroke_msg_t) || + *string < (char*)msg->buffer - (u_int)msg || + *string > (char*)(u_int)msg->length) + { + *string = "(invalid char* in stroke msg)"; + } + else + { + *string = (char*)msg + (u_int)*string; + } +} + +/** + * Add a connection to the configuration list + */ +static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg) +{ + connection_t *connection; + policy_t *policy; + identification_t *my_id, *other_id; + host_t *my_host, *other_host, *my_subnet, *other_subnet; + proposal_t *proposal; + traffic_selector_t *my_ts, *other_ts; + x509_t *cert; + + 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.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.subnet); + pop_string(msg, &msg->add_conn.other.subnet); + + this->logger->log(this->logger, CONTROL, "received stroke: add connection \"%s\"", msg->add_conn.name); + + my_host = host_create(AF_INET, msg->add_conn.me.address, 500); + if (my_host == NULL) + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid host: %s", msg->add_conn.me.address); + return; + } + other_host = host_create(AF_INET, msg->add_conn.other.address, 500); + if (other_host == NULL) + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid host: %s", msg->add_conn.other.address); + my_host->destroy(my_host); + return; + } + 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) + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid id: %s", msg->add_conn.me.id); + my_host->destroy(my_host); + other_host->destroy(other_host); + return; + } + 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) + { + my_host->destroy(my_host); + other_host->destroy(other_host); + my_id->destroy(my_id); + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid id: %s", msg->add_conn.other.id); + return; + } + + my_subnet = host_create(AF_INET, *msg->add_conn.me.subnet ? msg->add_conn.me.subnet : msg->add_conn.me.address, 500); + if (my_subnet == NULL) + { + my_host->destroy(my_host); + other_host->destroy(other_host); + my_id->destroy(my_id); + other_id->destroy(other_id); + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid subnet: %s", msg->add_conn.me.subnet); + return; + } + + other_subnet = host_create(AF_INET, *msg->add_conn.other.subnet ? msg->add_conn.other.subnet : msg->add_conn.other.address, 500); + if (other_subnet == NULL) + { + my_host->destroy(my_host); + other_host->destroy(other_host); + my_id->destroy(my_id); + other_id->destroy(other_id); + my_subnet->destroy(my_subnet); + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid subnet: %s", msg->add_conn.me.subnet); + return; + } + + my_ts = traffic_selector_create_from_subnet(my_subnet, *msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 32); + my_subnet->destroy(my_subnet); + other_ts = traffic_selector_create_from_subnet(other_subnet, *msg->add_conn.other.subnet ? msg->add_conn.other.subnet_mask : 32); + other_subnet->destroy(other_subnet); + + if (charon->socket->is_listening_on(charon->socket, other_host)) + { + this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "left is other host, switching"); + + host_t *tmp_host = my_host; + identification_t *tmp_id = my_id; + traffic_selector_t *tmp_ts = my_ts; + char *tmp_cert = msg->add_conn.me.cert; + + my_host = other_host; + other_host = tmp_host; + my_id = other_id; + other_id = tmp_id; + my_ts = other_ts; + other_ts = tmp_ts; + msg->add_conn.me.cert = msg->add_conn.other.cert; + msg->add_conn.other.cert = tmp_cert; + } + else if (charon->socket->is_listening_on(charon->socket, my_host)) + { + this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "left is own host, not switching"); + } + else + { + this->stroke_logger->log(this->stroke_logger, ERROR, "left nor right host is our, aborting"); + + my_host->destroy(my_host); + other_host->destroy(other_host); + my_id->destroy(my_id); + other_id->destroy(other_id); + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); + return; + } + + if (msg->add_conn.me.cert) + { + char file[128]; + snprintf(file, sizeof(file), "%s%s", CERTIFICATE_DIR, msg->add_conn.me.cert); + cert = x509_create_from_file(file); + if (cert) + { + my_id->destroy(my_id); + my_id = cert->get_subject(cert); + my_id = my_id->clone(my_id); + cert->destroy(cert); + this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, + "defined a valid certificate, using its ID \"%s\"", + my_id->get_string(my_id)); + } + } + if (msg->add_conn.other.cert) + { + char file[128]; + snprintf(file, sizeof(file), "%s%s", CERTIFICATE_DIR, msg->add_conn.other.cert); + cert = x509_create_from_file(file); + if (cert) + { + other_id->destroy(other_id); + other_id = cert->get_subject(cert); + other_id = other_id->clone(other_id); + cert->destroy(cert); + this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, + "defined a valid certificate, using its ID \"%s\"", + other_id->get_string(other_id)); + } + } + + connection = connection_create(msg->add_conn.name, + my_host, other_host, + my_id->clone(my_id), other_id->clone(other_id), + RSA_DIGITAL_SIGNATURE); + proposal = proposal_create(1); + proposal->add_algorithm(proposal, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + proposal->add_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + proposal->add_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + proposal->add_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0); + proposal->add_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0); + proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0); + proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0); + proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0); + connection->add_proposal(connection, proposal); + /* add to global connection list */ + charon->connections->add_connection(charon->connections, connection); + + policy = policy_create(my_id, other_id); + proposal = proposal_create(1); + proposal->add_algorithm(proposal, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + proposal->add_algorithm(proposal, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + proposal->add_algorithm(proposal, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0); + policy->add_proposal(policy, proposal); + policy->add_my_traffic_selector(policy, my_ts); + policy->add_other_traffic_selector(policy, other_ts); + /* add to global policy list */ + charon->policies->add_policy(charon->policies, policy); + + this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "connection \"%s\" added", msg->add_conn.name); +} + +/** + * initiate a connection by name + */ +static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg) +{ + initiate_ike_sa_job_t *job; + connection_t *connection; + + pop_string(msg, &(msg->initiate.name)); + this->logger->log(this->logger, CONTROL, "received stroke: initiate \"%s\"", msg->initiate.name); + connection = charon->connections->get_connection_by_name(charon->connections, msg->initiate.name); + if (connection == NULL) + { + this->stroke_logger->log(this->stroke_logger, ERROR, "could not find a connection named \"%s\"", msg->initiate.name); + } + else + { + job = initiate_ike_sa_job_create(connection); + charon->job_queue->add(charon->job_queue, (job_t*)job); + } +} + +/** + * terminate a connection by name + */ +static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg) +{ + connection_t *connection; + ike_sa_t *ike_sa; + host_t *my_host, *other_host; + status_t status; + + pop_string(msg, &(msg->terminate.name)); + this->logger->log(this->logger, CONTROL, "received stroke: terminate \"%s\"", msg->terminate.name); + connection = charon->connections->get_connection_by_name(charon->connections, msg->terminate.name); + + if (connection) + { + my_host = connection->get_my_host(connection); + other_host = connection->get_other_host(connection); + + /* TODO: Do this directly by name now */ + /* TODO: terminate any instance of the name */ + status = charon->ike_sa_manager->checkout_by_hosts(charon->ike_sa_manager, + my_host, other_host, &ike_sa); + + if (status == SUCCESS) + { + this->stroke_logger->log(this->stroke_logger, CONTROL, "deleting IKE SA between %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + + charon->ike_sa_manager->checkin_and_delete(charon->ike_sa_manager, ike_sa); + } + else + { + this->stroke_logger->log(this->stroke_logger, ERROR, "no active connection found between %s - %s", + my_host->get_address(my_host), other_host->get_address(other_host)); + } + } + else + { + this->stroke_logger->log(this->stroke_logger, ERROR, "could not find a connection named \"%s\"", msg->terminate.name); + } + +} + +/** + * show status of (established) connections + */ +static void stroke_status(private_stroke_t *this, stroke_msg_t *msg) +{ + if (msg->status.name) + { + pop_string(msg, &(msg->status.name)); + } + charon->ike_sa_manager->log_status(charon->ike_sa_manager, this->stroke_logger, msg->status.name); +} + +logger_context_t get_context(char *context) +{ + if (strcasecmp(context, "ALL") == 0) return ALL_LOGGERS; + else if (strcasecmp(context, "PARSR") == 0) return PARSER; + else if (strcasecmp(context, "GNRAT") == 0) return GENERATOR; + else if (strcasecmp(context, "IKESA") == 0) return IKE_SA; + else if (strcasecmp(context, "SAMGR") == 0) return IKE_SA_MANAGER; + else if (strcasecmp(context, "CHDSA") == 0) return CHILD_SA; + else if (strcasecmp(context, "MESSG") == 0) return MESSAGE; + else if (strcasecmp(context, "TPOOL") == 0) return THREAD_POOL; + else if (strcasecmp(context, "WORKR") == 0) return WORKER; + else if (strcasecmp(context, "SCHED") == 0) return SCHEDULER; + else if (strcasecmp(context, "SENDR") == 0) return SENDER; + else if (strcasecmp(context, "RECVR") == 0) return RECEIVER; + else if (strcasecmp(context, "SOCKT") == 0) return SOCKET; + else if (strcasecmp(context, "TESTR") == 0) return TESTER; + else if (strcasecmp(context, "DAEMN") == 0) return DAEMON; + else if (strcasecmp(context, "CONFG") == 0) return CONFIG; + else if (strcasecmp(context, "ENCPL") == 0) return ENCRYPTION_PAYLOAD; + else if (strcasecmp(context, "PAYLD") == 0) return PAYLOAD; + else return -2; +} + +/** + * set the type of logged messages in a context + */ +static void stroke_logtype(private_stroke_t *this, stroke_msg_t *msg) +{ + pop_string(msg, &(msg->logtype.context)); + pop_string(msg, &(msg->logtype.type)); + + this->logger->log(this->logger, CONTROL, "received stroke: logtype for %s", msg->logtype.context); + + log_level_t level; + logger_context_t context = get_context(msg->logtype.context); + if (context == -2) + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->logtype.context); + return; + } + + if (strcasecmp(msg->logtype.type, "CONTROL") == 0) level = CONTROL; + else if (strcasecmp(msg->logtype.type, "ERROR") == 0) level = ERROR; + else if (strcasecmp(msg->logtype.type, "AUDIT") == 0) level = AUDIT; + else if (strcasecmp(msg->logtype.type, "RAW") == 0) level = RAW; + else if (strcasecmp(msg->logtype.type, "PRIVATE") == 0) level = PRIVATE; + else + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid type (%s)!", msg->logtype.type); + return; + } + + if (msg->logtype.enable) + { + logger_manager->enable_log_level(logger_manager, context, level); + } + else + { + logger_manager->disable_log_level(logger_manager, context, level); + } +} + +/** + * set the verbosity of a logger + */ +static void stroke_loglevel(private_stroke_t *this, stroke_msg_t *msg) +{ + pop_string(msg, &(msg->loglevel.context)); + + this->logger->log(this->logger, CONTROL, "received stroke: loglevel for %s", msg->loglevel.context); + + log_level_t level; + logger_context_t context = get_context(msg->loglevel.context); + + if (context == -2) + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->loglevel.context); + return; + } + + if (msg->loglevel.level == 0) + { + level = LEVEL0; + } + else if (msg->loglevel.level == 1) + { + level = LEVEL1; + } + else if (msg->loglevel.level == 2) + { + level = LEVEL2; + } + else if (msg->loglevel.level == 3) + { + level = LEVEL3; + } + else + { + this->stroke_logger->log(this->stroke_logger, ERROR, "invalid level (%d)!", msg->loglevel.level); + return; + } + + logger_manager->enable_log_level(logger_manager, context, level); +} + +/** + * Implementation of private_stroke_t.stroke_receive. + */ +static void stroke_receive(private_stroke_t *this) +{ + stroke_msg_t *msg; + u_int16_t msg_length; + struct sockaddr_un strokeaddr; + int strokeaddrlen = sizeof(strokeaddr); + ssize_t bytes_read; + int strokefd; + FILE *strokefile; + int oldstate; + + /* disable cancellation by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + while (1) + { + /* 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) + { + this->logger->log(this->logger, ERROR, "accepting stroke connection failed: %s", strerror(errno)); + continue; + } + + /* peek the length */ + bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK); + if (bytes_read != sizeof(msg_length)) + { + this->logger->log(this->logger, ERROR, "reading lenght of stroke message failed"); + close(strokefd); + continue; + } + + /* read message */ + msg = malloc(msg_length); + bytes_read = recv(strokefd, msg, msg_length, 0); + if (bytes_read != msg_length) + { + this->logger->log(this->logger, ERROR, "reading stroke message failed: %s"); + close(strokefd); + continue; + } + + strokefile = fdopen(dup(strokefd), "w"); + if (strokefile == NULL) + { + this->logger->log(this->logger, ERROR, "opening stroke output channel failed:", strerror(errno)); + close(strokefd); + free(msg); + continue; + } + + this->stroke_logger = logger_create("-", CONTROL|ERROR, FALSE, strokefile); + + this->logger->log_bytes(this->logger, RAW, "stroke message", (void*)msg, msg_length); + + switch (msg->type) + { + case STR_INITIATE: + { + stroke_initiate(this, msg); + break; + } + case STR_TERMINATE: + { + stroke_terminate(this, msg); + break; + } + case STR_STATUS: + { + stroke_status(this, msg); + break; + } + case STR_STATUS_ALL: + { + this->stroke_logger->enable_level(this->stroke_logger, LEVEL1); + stroke_status(this, msg); + break; + } + case STR_ADD_CONN: + { + stroke_add_conn(this, msg); + break; + } + case STR_LOGTYPE: + { + stroke_logtype(this, msg); + break; + } + case STR_LOGLEVEL: + { + stroke_loglevel(this, msg); + break; + } + default: + this->logger->log(this->logger, ERROR, "received invalid stroke"); + } + this->stroke_logger->destroy(this->stroke_logger); + fclose(strokefile); + close(strokefd); + free(msg); + } +} + +/** + * 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; + + /* private functions */ + this->stroke_receive = stroke_receive; + + this->logger = logger_manager->get_logger(logger_manager, CONFIG); + + /* set up unix socket */ + this->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (this->socket == -1) + { + this->logger->log(this->logger, ERROR, "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) + { + this->logger->log(this->logger, ERROR, "could not bind stroke socket: %s", strerror(errno)); + close(this->socket); + free(this); + return NULL; + } + umask(old); + + if (listen(this->socket, 0) < 0) + { + this->logger->log(this->logger, ERROR, "could not listen on stroke socket: %s", strerror(errno)); + 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*))this->stroke_receive, this) != 0) + { + this->logger->log(this->logger, ERROR, "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/charon/threads/stroke_interface.h b/src/charon/charon/threads/stroke_interface.h new file mode 100644 index 000000000..f8efc9c67 --- /dev/null +++ b/src/charon/charon/threads/stroke_interface.h @@ -0,0 +1,86 @@ +/** + * @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_ + +#include +#include +#include + + +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). stroke_t implements the connections_t + * and the policies_t interface, which means it acts as a + * configuration backend for those too. stroke_t uses an own + * thread to read from the socket. + * + * @warning DO NOT cast stroke_t to any of the implemented interfaces! + * stroke_t implements multiple interfaces, so you must use + * stroke_t.interface_xy to access the specific interface! You have + * been warned... + * + * @todo Add clean thread cancellation + * + * @b Constructors: + * - stroke_create() + * + * @ingroup threads + */ +struct stroke_t { + + /** + * Implements policy_store_t interface + */ + policy_store_t policies; + + /** + * Implements credential_store_t interfacce + */ + credential_store_t credentials; + + /** + * @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(); + +#endif /* STROKE_INTERFACE_H_ */ diff --git a/src/charon/charon/threads/thread_pool.c b/src/charon/charon/threads/thread_pool.c new file mode 100644 index 000000000..4482e795f --- /dev/null +++ b/src/charon/charon/threads/thread_pool.c @@ -0,0 +1,623 @@ +/** + * @file thread_pool.c + * + * @brief Implementation of thread_pool_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "thread_pool.h" + +#include +#include +#include +#include +#include +#include +#include +#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; + + /** + * @brief Main processing function for worker threads. + * + * Gets a job from the job queue and calls corresponding + * function for processing. + * + * @param this calling object + */ + void (*process_jobs) (private_thread_pool_t *this); + + /** + * @brief Process a INCOMING_PACKET job. + * + * @param this calling object + * @param job incoming_packet_job_t object + */ + void (*process_incoming_packet_job) (private_thread_pool_t *this, incoming_packet_job_t *job); + + /** + * @brief Process a INITIATE_IKE_SA job. + * + * @param this calling object + * @param job initiate_ike_sa_job_t object + */ + void (*process_initiate_ike_sa_job) (private_thread_pool_t *this, initiate_ike_sa_job_t *job); + + /** + * @brief Process a DELETE_HALF_OPEN_IKE_SA job. + * + * @param this calling object + * @param job delete__half_open_ike_sa_job_t object + */ + void (*process_delete_half_open_ike_sa_job) (private_thread_pool_t *this, delete_half_open_ike_sa_job_t *job); + + /** + * @brief Process a DELETE_ESTABLISHED_IKE_SA job. + * + * @param this calling object + * @param job delete_established_ike_sa_job_t object + */ + void (*process_delete_established_ike_sa_job) (private_thread_pool_t *this, delete_established_ike_sa_job_t *job); + + /** + * @brief Process a RETRANSMIT_REQUEST job. + * + * @param this calling object + * @param job retransmit_request_job_t object + */ + void (*process_retransmit_request_job) (private_thread_pool_t *this, retransmit_request_job_t *job); + + /** + * Creates a job of type DELETE_HALF_OPEN_IKE_SA. + * + * This job is used to delete IKE_SA's which are still in state INITIATOR_INIT, + * RESPONDER_INIT, IKE_AUTH_REQUESTED, IKE_INIT_REQUESTED or IKE_INIT_RESPONDED. + * + * @param ike_sa_id ID of IKE_SA to delete + * @param delay Delay in ms after a half open IKE_SA gets deleted! + */ + void (*create_delete_half_open_ike_sa_job) (private_thread_pool_t *this,ike_sa_id_t *ike_sa_id, u_int32_t delay); + + /** + * Number of running threads. + */ + size_t pool_size; + + /** + * Array of thread ids. + */ + pthread_t *threads; + + /** + * Logger of the thread pool. + */ + logger_t *pool_logger; + + /** + * Logger of the worker threads. + */ + logger_t *worker_logger; +} ; + +/** + * Implementation of private_thread_pool_t.process_jobs. + */ +static void process_jobs(private_thread_pool_t *this) +{ + job_t *job; + job_type_t job_type; + timeval_t start_time; + timeval_t end_time; + + /* cancellation disabled by default */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + this->worker_logger->log(this->worker_logger, CONTROL, "Worker thread running, thread_id: %u", (int)pthread_self()); + + for (;;) { + + job = charon->job_queue->get(charon->job_queue); + job_type = job->get_type(job); + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL2, "Process job of type %s", + mapping_find(job_type_m,job_type)); + gettimeofday(&start_time,NULL); + switch (job_type) + { + case INCOMING_PACKET: + { + this->process_incoming_packet_job(this, (incoming_packet_job_t*)job); + job->destroy(job); + break; + } + case INITIATE_IKE_SA: + { + this->process_initiate_ike_sa_job(this, (initiate_ike_sa_job_t*)job); + job->destroy(job); + break; + } + case DELETE_HALF_OPEN_IKE_SA: + { + this->process_delete_half_open_ike_sa_job(this, (delete_half_open_ike_sa_job_t*)job); + job->destroy(job); + break; + } + case DELETE_ESTABLISHED_IKE_SA: + { + this->process_delete_established_ike_sa_job(this, (delete_established_ike_sa_job_t*)job); + job->destroy(job); + break; + } + case RETRANSMIT_REQUEST: + { + this->process_retransmit_request_job(this, (retransmit_request_job_t*)job); + break; + } + default: + { + this->worker_logger->log(this->worker_logger, ERROR, "Job of type %s not supported!", + mapping_find(job_type_m,job_type)); + job->destroy(job); + break; + } + } + gettimeofday(&end_time,NULL); + + this->worker_logger->log(this->worker_logger, CONTROL | LEVEL2, "Processed job of type %s in %d us", + mapping_find(job_type_m,job_type), + (((end_time.tv_sec - start_time.tv_sec) * 1000000) + (end_time.tv_usec - start_time.tv_usec))); + + + } +} + +/** + * Implementation of private_thread_pool_t.process_incoming_packet_job. + */ +static void process_incoming_packet_job(private_thread_pool_t *this, incoming_packet_job_t *job) +{ + packet_t *packet; + message_t *message; + ike_sa_t *ike_sa; + ike_sa_id_t *ike_sa_id; + status_t status; + + + packet = job->get_packet(job); + + message = message_create_from_packet(packet); + + status = message->parse_header(message); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Message header could not be verified!"); + message->destroy(message); + return; + } + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL2, "Message is a %s %s", + mapping_find(exchange_type_m, message->get_exchange_type(message)), + message->get_request(message) ? "request" : "reply"); + + if ((message->get_major_version(message) != IKE_MAJOR_VERSION) || + (message->get_minor_version(message) != IKE_MINOR_VERSION)) + { + this->worker_logger->log(this->worker_logger, ERROR | LEVEL2, "IKE version %d.%d not supported", + message->get_major_version(message), + message->get_minor_version(message)); + /* + * This check is not handled in state_t object of IKE_SA to increase speed. + */ + if ((message->get_exchange_type(message) == IKE_SA_INIT) && (message->get_request(message))) + { + message_t *response; + message->get_ike_sa_id(message, &ike_sa_id); + ike_sa_id->switch_initiator(ike_sa_id); + response = message_create_notify_reply(message->get_destination(message), + message->get_source(message), + IKE_SA_INIT, + FALSE,ike_sa_id,INVALID_MAJOR_VERSION); + message->destroy(message); + ike_sa_id->destroy(ike_sa_id); + status = response->generate(response, NULL, NULL, &packet); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Could not generate packet from message"); + response->destroy(response); + return; + } + this->worker_logger->log(this->worker_logger, ERROR, "Send notify reply of type INVALID_MAJOR_VERSION"); + charon->send_queue->add(charon->send_queue, packet); + response->destroy(response); + return; + } + message->destroy(message); + return; + } + + message->get_ike_sa_id(message, &ike_sa_id); + + ike_sa_id->switch_initiator(ike_sa_id); + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL3, "Checking out IKE SA %lld:%lld, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkout(charon->ike_sa_manager,ike_sa_id, &ike_sa); + if ((status != SUCCESS) && (status != CREATED)) + { + this->worker_logger->log(this->worker_logger, ERROR, "IKE SA could not be checked out"); + ike_sa_id->destroy(ike_sa_id); + message->destroy(message); + + /* + * TODO send notify reply of type INVALID_IKE_SPI if SPI could not be found ? + */ + + return; + } + + if (status == CREATED) + { + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL3, + "Create Job to delete half open IKE_SA."); + this->create_delete_half_open_ike_sa_job(this,ike_sa_id, + charon->configuration->get_half_open_ike_sa_timeout(charon->configuration)); + } + + status = ike_sa->process_message(ike_sa, message); + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL3, "%s IKE SA %lld:%lld, role %s", + (status == DELETE_ME) ? "Checkin and delete" : "Checkin", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); + ike_sa_id->destroy(ike_sa_id); + + if (status == DELETE_ME) + { + status = charon->ike_sa_manager->checkin_and_delete(charon->ike_sa_manager, ike_sa); + } + else + { + status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Checkin of IKE SA failed!"); + } + message->destroy(message); +} + +/** + * Implementation of private_thread_pool_t.process_initiate_ike_sa_job. + */ +static void process_initiate_ike_sa_job(private_thread_pool_t *this, initiate_ike_sa_job_t *job) +{ + /* + * Initiatie an IKE_SA: + * - is defined by a name of a configuration + * - create an empty IKE_SA via manager + * - call initiate_connection on this sa + */ + ike_sa_t *ike_sa; + status_t status; + + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL2, "Creating and checking out IKE SA"); + charon->ike_sa_manager->create_and_checkout(charon->ike_sa_manager, &ike_sa); + + status = ike_sa->initiate_connection(ike_sa, job->get_connection(job)); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Initiation returned %s, going to delete IKE_SA.", + mapping_find(status_m, status)); + charon->ike_sa_manager->checkin_and_delete(charon->ike_sa_manager, ike_sa); + return; + } + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL3, "Create Job to delete half open IKE_SA."); + this->create_delete_half_open_ike_sa_job(this,ike_sa->get_id(ike_sa), + charon->configuration->get_half_open_ike_sa_timeout(charon->configuration)); + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL2, "Checking in IKE SA"); + status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Could not checkin IKE_SA (%s)", + mapping_find(status_m, status)); + } +} + +/** + * Implementation of private_thread_pool_t.process_delete_ike_sa_job. + */ +static void process_delete_half_open_ike_sa_job(private_thread_pool_t *this, delete_half_open_ike_sa_job_t *job) +{ + ike_sa_id_t *ike_sa_id = job->get_ike_sa_id(job); + ike_sa_t *ike_sa; + status_t status; + status = charon->ike_sa_manager->checkout(charon->ike_sa_manager,ike_sa_id, &ike_sa); + if ((status != SUCCESS) && (status != CREATED)) + { + this->worker_logger->log(this->worker_logger, CONTROL | LEVEL3, "IKE SA seems to be allready deleted and so doesn't have to be deleted"); + return; + } + + + switch (ike_sa->get_state(ike_sa)) + { + case INITIATOR_INIT: + case RESPONDER_INIT: + case IKE_SA_INIT_REQUESTED: + case IKE_SA_INIT_RESPONDED: + case IKE_AUTH_REQUESTED: + { + /* IKE_SA is half open and gets deleted! */ + status = charon->ike_sa_manager->checkin_and_delete(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Could not checkin and delete checked out IKE_SA!"); + } + break; + } + default: + { + /* IKE_SA is established and so is not getting deleted! */ + status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Could not checkin a checked out IKE_SA!"); + } + break; + } + } +} + +/** + * Implementation of private_thread_pool_t.process_delete_established_ike_sa_job. + */ +static void process_delete_established_ike_sa_job(private_thread_pool_t *this, delete_established_ike_sa_job_t *job) +{ + ike_sa_id_t *ike_sa_id = job->get_ike_sa_id(job); + ike_sa_t *ike_sa; + status_t status; + status = charon->ike_sa_manager->checkout(charon->ike_sa_manager,ike_sa_id, &ike_sa); + if ((status != SUCCESS) && (status != CREATED)) + { + this->worker_logger->log(this->worker_logger, CONTROL | LEVEL3, "IKE SA seems to be allready deleted and so doesn't have to be deleted"); + return; + } + + switch (ike_sa->get_state(ike_sa)) + { + case INITIATOR_INIT: + case RESPONDER_INIT: + case IKE_SA_INIT_REQUESTED: + case IKE_SA_INIT_RESPONDED: + case IKE_AUTH_REQUESTED: + { + break; + } + default: + { + this->worker_logger->log(this->worker_logger, CONTROL, "Send delete request for IKE_SA."); + ike_sa->send_delete_ike_sa_request(ike_sa); + break; + } + } + this->worker_logger->log(this->worker_logger, CONTROL, "Delete established IKE_SA."); + status = charon->ike_sa_manager->checkin_and_delete(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Could not checkin and delete checked out IKE_SA!"); + } +} + + +/** + * Implementation of private_thread_pool_t.process_retransmit_request_job. + */ +static void process_retransmit_request_job(private_thread_pool_t *this, retransmit_request_job_t *job) +{ + + ike_sa_id_t *ike_sa_id = job->get_ike_sa_id(job); + u_int32_t message_id = job->get_message_id(job); + bool stop_retransmitting = FALSE; + u_int32_t timeout; + ike_sa_t *ike_sa; + status_t status; + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL2, "Checking out IKE SA %lld:%lld, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkout(charon->ike_sa_manager,ike_sa_id, &ike_sa); + if ((status != SUCCESS) && (status != CREATED)) + { + job->destroy(job); + this->worker_logger->log(this->worker_logger, ERROR, "IKE SA could not be checked out. Allready deleted?"); + return; + } + + status = ike_sa->retransmit_request(ike_sa, message_id); + + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, CONTROL | LEVEL3, "Message doesn't have to be retransmitted"); + stop_retransmitting = TRUE; + } + + this->worker_logger->log(this->worker_logger, CONTROL|LEVEL2, "Checkin IKE SA %lld:%lld, role %s", + ike_sa_id->get_initiator_spi(ike_sa_id), + ike_sa_id->get_responder_spi(ike_sa_id), + ike_sa_id->is_initiator(ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, ERROR, "Checkin of IKE SA failed!"); + } + + if (stop_retransmitting) + { + job->destroy(job); + return; + } + + job->increase_retransmit_count(job); + status = charon->configuration->get_retransmit_timeout (charon->configuration,job->get_retransmit_count(job),&timeout); + if (status != SUCCESS) + { + this->worker_logger->log(this->worker_logger, CONTROL | LEVEL2, "Message will not be anymore retransmitted"); + job->destroy(job); + /* + * TODO delete IKE_SA ? + */ + return; + } + charon->event_queue->add_relative(charon->event_queue,(job_t *) job,timeout); +} + + + +/** + * Implementation of private_thread_pool_t.create_delete_half_open_ike_sa_job. + */ +static void create_delete_half_open_ike_sa_job(private_thread_pool_t *this,ike_sa_id_t *ike_sa_id, u_int32_t delay) +{ + job_t *delete_job; + + this->worker_logger->log(this->worker_logger, CONTROL | LEVEL2, "Going to create job to delete half open IKE_SA in %d ms", delay); + + delete_job = (job_t *) delete_half_open_ike_sa_job_create(ike_sa_id); + charon->event_queue->add_relative(charon->event_queue,delete_job, delay); +} + + +/** + * Implementation of thread_pool_t.get_pool_size. + */ +static size_t get_pool_size(private_thread_pool_t *this) +{ + return this->pool_size; +} + +/** + * 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++) { + this->pool_logger->log(this->pool_logger, CONTROL, "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) + { + this->pool_logger->log(this->pool_logger, CONTROL, "worker thread #%d terminated", current+1); + } + else + { + this->pool_logger->log(this->pool_logger, ERROR, "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 = (size_t(*)(thread_pool_t*))get_pool_size; + + this->process_jobs = process_jobs; + this->process_initiate_ike_sa_job = process_initiate_ike_sa_job; + this->process_delete_half_open_ike_sa_job = process_delete_half_open_ike_sa_job; + this->process_delete_established_ike_sa_job = process_delete_established_ike_sa_job; + this->process_incoming_packet_job = process_incoming_packet_job; + this->process_retransmit_request_job = process_retransmit_request_job; + this->create_delete_half_open_ike_sa_job = create_delete_half_open_ike_sa_job; + + this->pool_size = pool_size; + + this->threads = malloc(sizeof(pthread_t) * pool_size); + + this->pool_logger = logger_manager->get_logger(logger_manager, THREAD_POOL); + + this->worker_logger = logger_manager->get_logger(logger_manager, WORKER); + + /* try to create as many threads as possible, up tu pool_size */ + for (current = 0; current < pool_size; current++) + { + if (pthread_create(&(this->threads[current]), NULL, (void*(*)(void*))this->process_jobs, this) == 0) + { + this->pool_logger->log(this->pool_logger, CONTROL, "Created worker thread #%d", current+1); + } + else + { + /* creation failed, is it the first one? */ + if (current == 0) + { + this->pool_logger->log(this->pool_logger, ERROR, "Could not create any thread"); + free(this->threads); + free(this); + return NULL; + } + /* not all threads could be created, but at least one :-/ */ + this->pool_logger->log(this->pool_logger, ERROR, "Could only create %d from requested %d threads!", current, pool_size); + + this->pool_size = current; + return (thread_pool_t*)this; + } + } + return (thread_pool_t*)this; +} diff --git a/src/charon/charon/threads/thread_pool.h b/src/charon/charon/threads/thread_pool.h new file mode 100644 index 000000000..b33be08e3 --- /dev/null +++ b/src/charon/charon/threads/thread_pool.h @@ -0,0 +1,78 @@ +/** + * @file thread_pool.h + * + * @brief Interface of thread_pool_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 THREAD_POOL_H_ +#define THREAD_POOL_H_ + +#include + +#include + + +typedef struct thread_pool_t thread_pool_t; + +/** + * @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 + */ + size_t (*get_pool_size) (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/charon/doc/Architecture.txt b/src/charon/doc/Architecture.txt new file mode 100644 index 000000000..14b99274c --- /dev/null +++ b/src/charon/doc/Architecture.txt @@ -0,0 +1,56 @@ +/** @mainpage + +@section design strongSwans overall design + +IKEv1 and IKEv2 is handled in different keying daemons. The ole IKEv1 stuff is +completely handled in pluto, as it was all the times. IKEv2 is handled in the +new keying daemon, which is called #charon. +Daemon control is done over unix sockets. Pluto uses whack, as it did for years. +Charon uses another socket interface, called stroke. Stroke uses another +format as whack and therefore is not compatible to whack. The starter utility, +wich does fast configuration parsing, speaks both the protocols, whack and +stroke. It also handles daemon startup and termination. +Pluto uses starter for some commands, for other it uses the whack utility. To be +as close to pluto as possible, charon has the same split up of commands to +starter and stroke. All commands are wrapped together in the ipsec script, which +allows transparent control of both daemons. +@verbatim + + +-----------------------------------------+ + | ipsec | + +-----+--------------+---------------+----+ + | | | + | | | + | +-----+-----+ | + +-----+----+ | | +-----+----+ + | | | starter | | | + | stroke | | | | whack | + | | +---+--+----+ | | + +------+---+ | | +--+-------+ + | | | | + +---+------+ | | +------+--+ + | | | | | | + | charon +----+ +----+ pluto | + | | | | + +-----+----+ +----+----+ + | | + +-----+----+ | + | LSF | | + +-----+----+ | + | | + +-----+----+ +----+----+ + | RAW Sock | | UDP/500 | + +----------+ +---------+ + +@endverbatim +Since IKEv2 uses the same port as IKEv1, both daemons must listen to UDP port +500. Under Linux, there is no clean way to set up two sockets at the same port. +To reslove this problem, charon uses a RAW socket, as they are used in network +sniffers. An installed Linux Socket Filter (LSF) filters out all none-IKEv2 +traffic. Pluto receives any IKE message, independant of charons behavior. +Therefore plutos behavior is changed to discard any IKEv2 traffic silently. + +To gain some reusability of the code, generic crypto and utility functions are +separeted in a shared library, libstrongswan. + +*/ \ No newline at end of file diff --git a/src/charon/doc/Known-bugs.txt b/src/charon/doc/Known-bugs.txt new file mode 100644 index 000000000..3f594ad79 --- /dev/null +++ b/src/charon/doc/Known-bugs.txt @@ -0,0 +1,6 @@ + Known bugs in charon +====================== + +- intiating the same connection twice makes trouble +- leak_detective gets confused from libpthread (invalid frees) +- installing to many SAs in the kernel at the same time causes troubles. Threading issue? diff --git a/src/charon/doc/Todo-list.txt b/src/charon/doc/Todo-list.txt new file mode 100644 index 000000000..11b30fb7d --- /dev/null +++ b/src/charon/doc/Todo-list.txt @@ -0,0 +1,49 @@ + Todo-List for charon +====================== + ++ = done, / = partial, - = todo, ordered by priority + + ++ private key loading: der, without passphrase ++ load all private keys from ipsec.d/private/ in stroke.c ++ handle leftcert and rightcert in starterstroke.c/stroke.c ++ load specified certs in stroke.c ++ extract public keys from certs ++ public key authentication ++ release for Andreas + ++ stroke loglevels ++ stroke up ++ ike_sa_manager checkout_by_hosts ++ stroke down ++ stroke output redirection ++ stroke status + ++ libx509 + + new charon build - libstrong? + + transforms + + utils (plus host) + + logger_manager instance in lib + + leak detective usable for charon and pluto and anything else + + integrate asn1 parser/oid (asn1/oid) + + integrate basic PEM loading + + port x509 stuff + ++ doxygen cleanup (charon/lib) + +/ useable certificate support + + more id types (use atodn from pluto) + + rewrite certificate storage the clean way + - further subjectAltName support + - certificate validation/chaining + - certificate exchange + +- implement 3DES to load encrypted pem files +- ipsec.secrets parsing + +- trapping +- delete notify, when to send? +- notifys on connection setup failure +- create child sa message/rekeying + +- new build environment (autotools?) diff --git a/src/charon/lib/Makefile.lib b/src/charon/lib/Makefile.lib new file mode 100644 index 000000000..80a44ff69 --- /dev/null +++ b/src/charon/lib/Makefile.lib @@ -0,0 +1,31 @@ +# 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. +# + +LIB_DIR= $(MAIN_DIR)lib/ + +include $(MAIN_DIR)lib/utils/Makefile.utils +include $(MAIN_DIR)lib/crypto/Makefile.transforms +include $(MAIN_DIR)lib/asn1/Makefile.asn1 + +LIB_OBJS+= $(BUILD_DIR)types.o +$(BUILD_DIR)types.o : $(LIB_DIR)types.c $(LIB_DIR)types.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)definitions.o +$(BUILD_DIR)definitions.o : $(LIB_DIR)definitions.c $(LIB_DIR)definitions.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)library.o +$(BUILD_DIR)library.o : $(LIB_DIR)library.c $(LIB_DIR)library.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/lib/asn1/Makefile.asn1 b/src/charon/lib/asn1/Makefile.asn1 new file mode 100644 index 000000000..3a5450d50 --- /dev/null +++ b/src/charon/lib/asn1/Makefile.asn1 @@ -0,0 +1,29 @@ +# 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. +# + +ASN1_DIR= $(LIB_DIR)asn1/ + + +LIB_OBJS+= $(BUILD_DIR)oid.o +$(BUILD_DIR)oid.o : $(ASN1_DIR)oid.c $(ASN1_DIR)oid.h + $(CC) $(CFLAGS) -c -o $@ $< +LIB_OBJS+= $(BUILD_DIR)asn1.o +$(BUILD_DIR)asn1.o : $(ASN1_DIR)asn1.c $(ASN1_DIR)asn1.h + $(CC) $(CFLAGS) -c -o $@ $< +LIB_OBJS+= $(BUILD_DIR)pem.o +$(BUILD_DIR)pem.o : $(ASN1_DIR)pem.c $(ASN1_DIR)pem.h + $(CC) $(CFLAGS) -c -o $@ $< +LIB_OBJS+= $(BUILD_DIR)ttodata.o +$(BUILD_DIR)ttodata.o : $(ASN1_DIR)ttodata.c $(ASN1_DIR)ttodata.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/lib/asn1/asn1.c b/src/charon/lib/asn1/asn1.c new file mode 100644 index 000000000..c847461b6 --- /dev/null +++ b/src/charon/lib/asn1/asn1.c @@ -0,0 +1,738 @@ +/* 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 + +static logger_t *logger; + +/* Names of the months */ +static const char* months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +/* 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_INITIALIZER; + } +} + +/* + * 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) + { + logger->log(logger, ERROR|LEVEL1, "number of length octets is larger than ASN.1 object"); + return ASN1_INVALID_LENGTH; + } + + if (n > sizeof(len)) + { + logger->log(logger, ERROR|LEVEL1, "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; +} + +/* + * Display a date either in local or UTC time + * TODO: Does not seem to be thread save + */ +char* timetoa(const time_t *time, bool utc) +{ + static char buf[30]; + + if (*time == 0) + 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; +} + +/* + * 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) +{ + logger = logger_manager->get_logger(logger_manager, ASN1); + ctx->blobs[0] = blob; + ctx->level0 = level0; + ctx->implicit = implicit; + 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) +{ + int oid; + time_t time; + + switch (type) + { + case ASN1_OID: + oid = known_oid(object); + if (oid != OID_UNKNOWN) + { + logger->log(logger, CONTROL|LEVEL1, " '%s'", oid_names[oid].name); + return; + } + break; + case ASN1_UTF8STRING: + case ASN1_IA5STRING: + case ASN1_PRINTABLESTRING: + case ASN1_T61STRING: + case ASN1_VISIBLESTRING: + logger->log(logger, CONTROL|LEVEL1, " '%.*s'", (int)object.len, object.ptr); + return; + case ASN1_UTCTIME: + case ASN1_GENERALIZEDTIME: + time = asn1totime(&object, type); + logger->log(logger, CONTROL|LEVEL1, " '%s'", timetoa(&time, TRUE)); + return; + default: + break; + } + logger->log_chunk(logger, RAW|LEVEL1, "", 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_INITIALIZER; + + 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 */ + logger->log(logger, CONTROL|LEVEL1, "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) + { + logger->log(logger, ERROR|LEVEL1, "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) + { + logger->log(logger, ERROR|LEVEL1, "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) + { + logger->log(logger, CONTROL|LEVEL1, "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)) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + *level, obj.name, obj.type, *start_ptr); + logger->log_bytes(logger, RAW|LEVEL1, "", start_ptr, (u_int)(blob->ptr - start_ptr)); + return FALSE; + } + + logger->log(logger, CONTROL|LEVEL1, "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); + logger->log_chunk(logger, RAW|LEVEL1, "", *object); + } + else if (obj.flags & ASN1_BODY) + { + *object = *blob1; + debug_asn1_simple_object(*object, obj.type); + } + 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) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: ASN.1 object smaller than 2 octets", + level, name); + return FALSE; + } + + if (*object->ptr != type) + { + logger->log(logger, ERROR|LEVEL1, "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) + { + logger->log(logger, ERROR|LEVEL1, "L%d - %s: length of ASN.1 object invalid or too large", + level, name); + return FALSE; + } + + logger->log(logger, CONTROL|LEVEL1, "L%d - %s:", level, name); + debug_asn1_simple_object(*object, type); + 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); + + 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) + { + logger->log(logger, ERROR|LEVEL2, " file content is not binary ASN.1"); + return FALSE; + } + len = asn1_length(&blob); + if (len != blob.len) + { + logger->log(logger, ERROR|LEVEL2, " 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[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); +} diff --git a/src/charon/lib/asn1/asn1.h b/src/charon/lib/asn1/asn1.h new file mode 100644 index 000000000..556bb2b05 --- /dev/null +++ b/src/charon/lib/asn1/asn1.h @@ -0,0 +1,137 @@ +/* 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; + 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; + +#define TIMETOA_BUF 30 + +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); +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, ...); +extern chunk_t timetoasn1(const time_t *time, asn1_t type); + +#endif /* _ASN1_H */ diff --git a/src/charon/lib/asn1/oid.c b/src/charon/lib/asn1/oid.c new file mode 100644 index 000000000..4b0632de2 --- /dev/null +++ b/src/charon/lib/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/charon/lib/asn1/oid.h b/src/charon/lib/asn1/oid.h new file mode 100644 index 000000000..a9265d43f --- /dev/null +++ b/src/charon/lib/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/charon/lib/asn1/oid.pl b/src/charon/lib/asn1/oid.pl new file mode 100644 index 000000000..a3725e57d --- /dev/null +++ b/src/charon/lib/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))/8), $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/charon/lib/asn1/oid.txt b/src/charon/lib/asn1/oid.txt new file mode 100644 index 000000000..eed46d59d --- /dev/null +++ b/src/charon/lib/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/charon/lib/asn1/pem.c b/src/charon/lib/asn1/pem.c new file mode 100755 index 000000000..b02268dd9 --- /dev/null +++ b/src/charon/lib/asn1/pem.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2005 Jan Hutter, 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 "pem.h" +#include "ttodata.h" + +#include +#include + + +/* + * 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 = CHUNK_INITIALIZER; + + 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)) + { + 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 = CHUNK_INITIALIZER; + + 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) +{ + /* 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 status_t pem_decrypt(chunk_t *blob, chunk_t *iv, char *passphrase) +{ + hasher_t *hasher; + crypter_t *crypter; + chunk_t hash; + chunk_t decrypted; + chunk_t pass = {passphrase, strlen(passphrase)}; + chunk_t key = {alloca(24), 24}; + u_int8_t padding, *last_padding_pos, *first_padding_pos; + + /* 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, pass, NULL); + hasher->get_hash(hasher, *iv, hash.ptr); + + memcpy(key.ptr, hash.ptr, hash.len); + + hasher->get_hash(hasher, hash, NULL); + hasher->get_hash(hasher, pass, NULL); + hasher->get_hash(hasher, *iv, hash.ptr); + + memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len); + + hasher->destroy(hasher); + + /* decrypt blob */ + crypter = crypter_create(ENCR_3DES, 0); + crypter->set_key(crypter, key); + crypter->decrypt(crypter, *blob, *iv, &decrypted); + 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 FALSE; + } + /* remove padding */ + blob->len -= padding; + return TRUE; +} + +/* Converts a PEM encoded file into its binary form + * + * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 + * RFC 934 Message Encapsulation, January 1985 + */ +status_t pemtobin(chunk_t *blob, char *pass) +{ + 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 = CHUNK_INITIALIZER; + chunk_t iv = CHUNK_INITIALIZER; + + 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) + { + chunk_t name = CHUNK_INITIALIZER; + chunk_t value = CHUNK_INITIALIZER; + + /* 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 NOT_SUPPORTED; + + eat_whitespace(&value); + ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len); + if (ugh) + return PARSE_ERROR; + + 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; + } + + 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 PARSE_ERROR; + + if (encrypted) + return pem_decrypt(blob, &iv, pass); + else + return SUCCESS; +} diff --git a/src/charon/lib/asn1/pem.h b/src/charon/lib/asn1/pem.h new file mode 100755 index 000000000..a4332fd34 --- /dev/null +++ b/src/charon/lib/asn1/pem.h @@ -0,0 +1,25 @@ +/* + * 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 PEM_H_ +#define PEM_H_ + +#include + +#include + +status_t pemtobin(chunk_t *blob, char *pass); + +#endif /*PEM_H_*/ diff --git a/src/charon/lib/asn1/ttodata.c b/src/charon/lib/asn1/ttodata.c new file mode 100644 index 000000000..5e8149955 --- /dev/null +++ b/src/charon/lib/asn1/ttodata.c @@ -0,0 +1,374 @@ +/* + * 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)) + +/* + - 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; +} diff --git a/src/charon/lib/asn1/ttodata.h b/src/charon/lib/asn1/ttodata.h new file mode 100644 index 000000000..d57244ef5 --- /dev/null +++ b/src/charon/lib/asn1/ttodata.h @@ -0,0 +1,30 @@ +/* + * 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 */ + +typedef const char *err_t; /* error message, or NULL for success */ + +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/charon/lib/crypto/Makefile.transforms b/src/charon/lib/crypto/Makefile.transforms new file mode 100644 index 000000000..af0b147da --- /dev/null +++ b/src/charon/lib/crypto/Makefile.transforms @@ -0,0 +1,37 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +CRYPTO_DIR= $(LIB_DIR)crypto/ + +include $(CRYPTO_DIR)crypters/Makefile.crypters +include $(CRYPTO_DIR)hashers/Makefile.hashers +include $(CRYPTO_DIR)prfs/Makefile.prfs +include $(CRYPTO_DIR)signers/Makefile.signers +include $(CRYPTO_DIR)rsa/Makefile.rsa + +LIB_OBJS+= $(BUILD_DIR)diffie_hellman.o +$(BUILD_DIR)diffie_hellman.o : $(CRYPTO_DIR)diffie_hellman.c $(CRYPTO_DIR)diffie_hellman.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)hmac.o +$(BUILD_DIR)hmac.o : $(CRYPTO_DIR)hmac.c $(CRYPTO_DIR)hmac.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)prf_plus.o +$(BUILD_DIR)prf_plus.o : $(CRYPTO_DIR)prf_plus.c $(CRYPTO_DIR)prf_plus.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)x509.o +$(BUILD_DIR)x509.o : $(CRYPTO_DIR)x509.c $(CRYPTO_DIR)x509.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/lib/crypto/crypters/Makefile.crypters b/src/charon/lib/crypto/crypters/Makefile.crypters new file mode 100644 index 000000000..612477de8 --- /dev/null +++ b/src/charon/lib/crypto/crypters/Makefile.crypters @@ -0,0 +1,23 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +CRYPTERS_DIR= $(CRYPTO_DIR)crypters/ + +LIB_OBJS+= $(BUILD_DIR)crypter.o +$(BUILD_DIR)crypter.o : $(CRYPTERS_DIR)crypter.c $(CRYPTERS_DIR)crypter.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)aes_cbc_crypter.o +$(BUILD_DIR)aes_cbc_crypter.o : $(CRYPTERS_DIR)aes_cbc_crypter.c $(CRYPTERS_DIR)aes_cbc_crypter.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/lib/crypto/crypters/aes_cbc_crypter.c b/src/charon/lib/crypto/crypters/aes_cbc_crypter.c new file mode 100644 index 000000000..9b7b07c62 --- /dev/null +++ b/src/charon/lib/crypto/crypters/aes_cbc_crypter.c @@ -0,0 +1,1627 @@ +/** + * @file aes_cbc_crypter.c + * + * @brief Implementation of aes_cbc_crypter_t + * + */ + + /* + * Copyright (C) 2001 Dr B. R. Gladman + * Copyright (C) 2005 Jan Hutter, 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 "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]; + + /** + * The number of columns in the cipher state. + */ + u_int32_t aes_Ncol; + + /** + * 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 (this->aes_Ncol) + +// 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 > (this->aes_Ncol) ? this->aes_Nkey : (this->aes_Ncol)) + 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_Ncol = 8; + this->aes_Nkey = 8; + break; + case 24: /* bytes */ + this->aes_Ncol = 6; + this->aes_Nkey = 6; + break; + case 16: /* bytes */ + this->aes_Ncol = 4; + 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/charon/lib/crypto/crypters/aes_cbc_crypter.h b/src/charon/lib/crypto/crypters/aes_cbc_crypter.h new file mode 100644 index 000000000..d7a3c0f5b --- /dev/null +++ b/src/charon/lib/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 Jan Hutter, 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 AES_CBC_CRYPTER_H_ +#define AES_CBC_CRYPTER_H_ + +#include + + +typedef struct aes_cbc_crypter_t aes_cbc_crypter_t; + +/** + * @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/charon/lib/crypto/crypters/crypter.c b/src/charon/lib/crypto/crypters/crypter.c new file mode 100644 index 000000000..827d10228 --- /dev/null +++ b/src/charon/lib/crypto/crypters/crypter.c @@ -0,0 +1,63 @@ +/** + * @file crypter.c + * + * @brief Generic constructor for crypter_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "crypter.h" + +#include + + +/** + * String mappings for encryption_algorithm_t. + */ +mapping_t encryption_algorithm_m[] = { +{ENCR_UNDEFINED, "ENCR_UNDEFINED"}, +{ENCR_DES_IV64, "ENCR_DES_IV64"}, +{ENCR_DES, "ENCR_DES"}, +{ENCR_3DES, "ENCR_3DES"}, +{ENCR_RC5, "ENCR_RC5"}, +{ENCR_IDEA, "ENCR_IDEA"}, +{ENCR_CAST, "ENCR_CAST"}, +{ENCR_BLOWFISH, "ENCR_BLOWFISH"}, +{ENCR_3IDEA, "ENCR_3IDEA"}, +{ENCR_DES_IV32, "ENCR_DES_IV32"}, +{ENCR_NULL, "ENCR_NULL"}, +{ENCR_AES_CBC, "ENCR_AES_CBC"}, +{ENCR_AES_CTR, "ENCR_AES_CTR"}, +{MAPPING_END, NULL} +}; + +/* + * 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); + } + default: + return NULL; + } +} diff --git a/src/charon/lib/crypto/crypters/crypter.h b/src/charon/lib/crypto/crypters/crypter.h new file mode 100644 index 000000000..9c219f5cc --- /dev/null +++ b/src/charon/lib/crypto/crypters/crypter.h @@ -0,0 +1,153 @@ +/** + * @file crypter.h + * + * @brief Interface crypter_t + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 CRYPTER_H_ +#define CRYPTER_H_ + +#include + +typedef enum encryption_algorithm_t encryption_algorithm_t; + +/** + * @brief Encryption algorithm, as in IKEv2 RFC 3.3.2. + * + * Currently only the following algorithms are implemented and therefore supported: + * - ENCR_AES_CBC + * + * @todo Implement more enryption algorithms, such as 3DES + * + * @ingroup crypters + */ +enum encryption_algorithm_t { + ENCR_UNDEFINED = 1024, + ENCR_DES_IV64 = 1, + ENCR_DES = 2, + 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 +}; + +/** + * String mappings for encryption_algorithm_t. + */ +extern mapping_t encryption_algorithm_m[]; + + +typedef struct crypter_t crypter_t; + +/** + * @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 and therefore supported: + * - ENCR_AES_CBC + * + * 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/charon/lib/crypto/diffie_hellman.c b/src/charon/lib/crypto/diffie_hellman.c new file mode 100644 index 000000000..e458fb80f --- /dev/null +++ b/src/charon/lib/crypto/diffie_hellman.c @@ -0,0 +1,615 @@ +/** + * @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 Jan Hutter, 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 "diffie_hellman.h" + +#include +#include + + +/** + * String mappings for diffie_hellman_group_t. + */ +mapping_t diffie_hellman_group_m[] = { + {MODP_UNDEFINED, "MODP_UNDEFINED"}, + {MODP_768_BIT, "MODP_768_BIT"}, + {MODP_1024_BIT, "MODP_1024_BIT"}, + {MODP_1536_BIT, "MODP_1536_BIT"}, + {MODP_2048_BIT, "MODP_2048_BIT"}, + {MODP_3072_BIT, "MODP_3072_BIT"}, + {MODP_4096_BIT, "MODP_4096_BIT"}, + {MODP_6144_BIT, "MODP_6144_BIT"}, + {MODP_8192_BIT, "MODP_8192_BIT"}, + {MAPPING_END, NULL} +}; + + +/** + * 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/charon/lib/crypto/diffie_hellman.h b/src/charon/lib/crypto/diffie_hellman.h new file mode 100644 index 000000000..48a165557 --- /dev/null +++ b/src/charon/lib/crypto/diffie_hellman.h @@ -0,0 +1,149 @@ +/** + * @file diffie_hellman.h + * + * @brief Interface of diffie_hellman_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 DIFFIE_HELLMAN_H_ +#define DIFFIE_HELLMAN_H_ + +#include + + +typedef enum diffie_hellman_group_t diffie_hellman_group_t; + +/** + * @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_UNDEFINED = 1024, + 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 +}; + +/** + * String mappings for diffie_hellman_group_t. + */ +extern mapping_t diffie_hellman_group_m[]; + + +typedef struct diffie_hellman_t diffie_hellman_t; + +/** + * @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/charon/lib/crypto/hashers/Makefile.hashers b/src/charon/lib/crypto/hashers/Makefile.hashers new file mode 100644 index 000000000..e05d41af3 --- /dev/null +++ b/src/charon/lib/crypto/hashers/Makefile.hashers @@ -0,0 +1,27 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +HASHERS_DIR= $(CRYPTO_DIR)hashers/ + +LIB_OBJS+= $(BUILD_DIR)hasher.o +$(BUILD_DIR)hasher.o : $(HASHERS_DIR)hasher.c $(HASHERS_DIR)hasher.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)sha1_hasher.o +$(BUILD_DIR)sha1_hasher.o : $(HASHERS_DIR)sha1_hasher.c $(HASHERS_DIR)sha1_hasher.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)md5_hasher.o +$(BUILD_DIR)md5_hasher.o : $(HASHERS_DIR)md5_hasher.c $(HASHERS_DIR)md5_hasher.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/lib/crypto/hashers/hasher.c b/src/charon/lib/crypto/hashers/hasher.c new file mode 100644 index 000000000..c15f41804 --- /dev/null +++ b/src/charon/lib/crypto/hashers/hasher.c @@ -0,0 +1,60 @@ +/** + * @file hasher.c + * + * @brief Generic constructor for hasher_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "hasher.h" + +#include +#include + +/** + * String mappings for hash_algorithm_t. + */ +mapping_t hash_algorithm_m[] = { + {HASH_MD2,"HASH_MD2"}, + {HASH_MD5,"HASH_MD5"}, + {HASH_SHA1,"HASH_SHA1"}, + {HASH_SHA256,"HASH_SHA256"}, + {HASH_SHA384,"HASH_SHA384"}, + {HASH_SHA512,"HASH_SHA512"}, + {MAPPING_END, NULL} +}; + +/* + * 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_MD5: + { + return (hasher_t*)md5_hasher_create(); + } + default: + return NULL; + } +} diff --git a/src/charon/lib/crypto/hashers/hasher.h b/src/charon/lib/crypto/hashers/hasher.h new file mode 100644 index 000000000..24683c01b --- /dev/null +++ b/src/charon/lib/crypto/hashers/hasher.h @@ -0,0 +1,147 @@ +/** + * @file hasher.h + * + * @brief Interface hasher_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HASHER_H_ +#define HASHER_H_ + + +#include + + +typedef enum hash_algorithm_t hash_algorithm_t; + +/** + * @brief Algorithms to use for hashing. + * + * Currently only the following algorithms are implemented and therefore supported: + * - HASH_MD5 + * - HASH_SHA1 + * + * @ingroup hashers + * + */ +enum hash_algorithm_t { + HASH_MD2, + /** + * Implemented in class md5_hasher_t. + */ + HASH_MD5, + /** + * Implemented in class sha1_hasher_t. + */ + HASH_SHA1, + HASH_SHA256, + HASH_SHA384, + HASH_SHA512, +}; + +/** + * String mappings for hash_algorithm_t. + */ +extern mapping_t hash_algorithm_m[]; + + +typedef struct hasher_t hasher_t; + +/** + * @brief Generic interface for all hash functions. + * + * @b Constructors: + * - hasher_create() + * - md5_hasher_create() + * - sha1_hasher_create() + * + * @see + * - md5_hasher_t + * - sha1_hasher_t + * + * @todo Implement more hash algorithms + * + * @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 reseted. + * + * @warning: 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 reseted. + * + * @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, which allows + * computation of a completely new hash. + * + * @param this calling object + */ + void (*reset) (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/charon/lib/crypto/hashers/md5_hasher.c b/src/charon/lib/crypto/hashers/md5_hasher.c new file mode 100644 index 000000000..bd3ab0c62 --- /dev/null +++ b/src/charon/lib/crypto/hashers/md5_hasher.c @@ -0,0 +1,394 @@ +/** + * @file md5_hasher.c + * + * @brief Implementation of md5_hasher_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * 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" + +#include + +#define BLOCK_SIZE_MD5 16 + + +/* 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(BLOCK_SIZE_MD5); + allocated_hash.len = BLOCK_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 BLOCK_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.destroy. + */ +static void destroy(private_md5_hasher_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +md5_hasher_t *md5_hasher_create() +{ + 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.destroy = (void (*) (hasher_t*))destroy; + + /* initialize */ + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + + return &(this->public); +} diff --git a/src/charon/lib/crypto/hashers/md5_hasher.h b/src/charon/lib/crypto/hashers/md5_hasher.h new file mode 100644 index 000000000..1e6d95d19 --- /dev/null +++ b/src/charon/lib/crypto/hashers/md5_hasher.h @@ -0,0 +1,60 @@ +/** + * @file md5_hasher.h + * + * @brief Interface for md5_hasher_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 MD5_HASHER_H_ +#define MD5_HASHER_H_ + +#include + + +typedef struct md5_hasher_t md5_hasher_t; + +/** + * @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(); + +#endif /*MD5_HASHER_H_*/ diff --git a/src/charon/lib/crypto/hashers/sha1_hasher.c b/src/charon/lib/crypto/hashers/sha1_hasher.c new file mode 100644 index 000000000..2b82ef4ba --- /dev/null +++ b/src/charon/lib/crypto/hashers/sha1_hasher.c @@ -0,0 +1,269 @@ +/** + * @file sha1_hasher.c + * + * @brief Implementation of hasher_sha_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * 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" + +#include + +#define BLOCK_SIZE_SHA1 20 + +/* + * 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(BLOCK_SIZE_SHA1); + allocated_hash.len = BLOCK_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 BLOCK_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.destroy. + */ +static void destroy(private_sha1_hasher_t *this) +{ + free(this); +} + + +/* + * Described in header. + */ +sha1_hasher_t *sha1_hasher_create() +{ + 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.destroy = (void (*) (hasher_t*))destroy; + + /* initialize */ + this->public.hasher_interface.reset(&(this->public.hasher_interface)); + + return &(this->public); +} diff --git a/src/charon/lib/crypto/hashers/sha1_hasher.h b/src/charon/lib/crypto/hashers/sha1_hasher.h new file mode 100644 index 000000000..5124ea1a8 --- /dev/null +++ b/src/charon/lib/crypto/hashers/sha1_hasher.h @@ -0,0 +1,60 @@ +/** + * @file sha1_hasher.h + * + * @brief Interface of sha1_hasher_t + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SHA1_HASHER_H_ +#define SHA1_HASHER_H_ + +#include + + +typedef struct sha1_hasher_t sha1_hasher_t; + +/** + * @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(); + +#endif /*SHA1_HASHER_H_*/ diff --git a/src/charon/lib/crypto/hmac.c b/src/charon/lib/crypto/hmac.c new file mode 100644 index 000000000..bb8880770 --- /dev/null +++ b/src/charon/lib/crypto/hmac.c @@ -0,0 +1,209 @@ +/** + * @file hmac.c + * + * @brief Implementation of hmac_t. + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 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: + this->b = 64; + 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/charon/lib/crypto/hmac.h b/src/charon/lib/crypto/hmac.h new file mode 100644 index 000000000..8945fc1fc --- /dev/null +++ b/src/charon/lib/crypto/hmac.h @@ -0,0 +1,118 @@ +/** + * @file hmac.h + * + * @brief Interface of hmac_t. + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HMAC_H_ +#define HMAC_H_ + +#include +#include + + +typedef struct hmac_t hmac_t; + +/** + * @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/charon/lib/crypto/prf_plus.c b/src/charon/lib/crypto/prf_plus.c new file mode 100644 index 000000000..d408d0517 --- /dev/null +++ b/src/charon/lib/crypto/prf_plus.c @@ -0,0 +1,157 @@ +/** + * @file prf_plus.c + * + * @brief Implementation of prf_plus_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "prf_plus.h" + +#include + +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/charon/lib/crypto/prf_plus.h b/src/charon/lib/crypto/prf_plus.h new file mode 100644 index 000000000..bdcd01966 --- /dev/null +++ b/src/charon/lib/crypto/prf_plus.h @@ -0,0 +1,93 @@ +/** + * @file prf_plus.h + * + * @brief Interface for prf_plus.h. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PRF_PLUS_H_ +#define PRF_PLUS_H_ + + +#include + + +typedef struct prf_plus_t prf_plus_t; + +/** + * @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/charon/lib/crypto/prfs/Makefile.prfs b/src/charon/lib/crypto/prfs/Makefile.prfs new file mode 100644 index 000000000..a98894346 --- /dev/null +++ b/src/charon/lib/crypto/prfs/Makefile.prfs @@ -0,0 +1,23 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +PRFS_DIR= $(CRYPTO_DIR)prfs/ + +LIB_OBJS+= $(BUILD_DIR)prf.o +$(BUILD_DIR)prf.o : $(PRFS_DIR)prf.c $(PRFS_DIR)prf.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)hmac_prf.o +$(BUILD_DIR)hmac_prf.o : $(PRFS_DIR)hmac_prf.c $(PRFS_DIR)hmac_prf.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/lib/crypto/prfs/hmac_prf.c b/src/charon/lib/crypto/prfs/hmac_prf.c new file mode 100644 index 000000000..2a7d34a3a --- /dev/null +++ b/src/charon/lib/crypto/prfs/hmac_prf.c @@ -0,0 +1,117 @@ +/** + * @file hmac_prf.c + * + * @brief Implementation for hmac_prf_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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) +{ + free(this); + this->hmac->destroy(this->hmac); +} + +/* + * 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/charon/lib/crypto/prfs/hmac_prf.h b/src/charon/lib/crypto/prfs/hmac_prf.h new file mode 100644 index 000000000..3a68960f7 --- /dev/null +++ b/src/charon/lib/crypto/prfs/hmac_prf.h @@ -0,0 +1,64 @@ +/** + * @file hmac_prf.h + * + * @brief Interface of hmac_prf_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PRF_HMAC_H_ +#define PRF_HMAC_H_ + +#include +#include +#include + +typedef struct hmac_prf_t hmac_prf_t; + +/** + * @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/charon/lib/crypto/prfs/prf.c b/src/charon/lib/crypto/prfs/prf.c new file mode 100644 index 000000000..bb7015e64 --- /dev/null +++ b/src/charon/lib/crypto/prfs/prf.c @@ -0,0 +1,67 @@ +/** + * @file prf.c + * + * @brief Generic constructor for all prf_t + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "prf.h" + +#include +#include + + +/** + * String mappings for encryption_algorithm_t. + */ +mapping_t pseudo_random_function_m[] = { +{PRF_UNDEFINED, "PRF_UNDEFINED"}, +{PRF_HMAC_MD5, "PRF_HMAC_MD5"}, +{PRF_HMAC_SHA1, "PRF_HMAC_SHA1"}, +{PRF_HMAC_TIGER, "PRF_HMAC_TIGER"}, +{PRF_AES128_CBC, "PRF_AES128_CBC"}, +{MAPPING_END, NULL} +}; + +/* + * 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_TIGER: + case PRF_AES128_CBC: + default: + return NULL; + } +} + + + + + diff --git a/src/charon/lib/crypto/prfs/prf.h b/src/charon/lib/crypto/prfs/prf.h new file mode 100644 index 000000000..b1c1e6a66 --- /dev/null +++ b/src/charon/lib/crypto/prfs/prf.h @@ -0,0 +1,136 @@ +/** + * @file prf.h + * + * @brief Interface prf_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PRF_H_ +#define PRF_H_ + +#include + +typedef enum pseudo_random_function_t pseudo_random_function_t; + +/** + * @brief Pseudo random function, as in IKEv2 RFC 3.3.2. + * + * Currently only the following algorithms are implemented and therefore supported: + * - PRF_HMAC_MD5 + * - PRF_HMAC_SHA1 + * + * @ingroup prfs + */ +enum pseudo_random_function_t { + PRF_UNDEFINED = 1024, + /** + * Implemented in class hmac_prf_t. + */ + PRF_HMAC_MD5 = 1, + /** + * Implemented in class hmac_prf_t. + */ + PRF_HMAC_SHA1 = 2, + PRF_HMAC_TIGER = 3, + PRF_AES128_CBC = 4 +}; + +/** + * String mappings for encryption_algorithm_t. + */ +extern mapping_t pseudo_random_function_m[]; + + +typedef struct prf_t prf_t; + +/** + * @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. + * + * @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/charon/lib/crypto/rsa/Makefile.rsa b/src/charon/lib/crypto/rsa/Makefile.rsa new file mode 100644 index 000000000..1a0204c83 --- /dev/null +++ b/src/charon/lib/crypto/rsa/Makefile.rsa @@ -0,0 +1,23 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +RSA_DIR= $(CRYPTO_DIR)rsa/ + +LIB_OBJS+= $(BUILD_DIR)rsa_private_key.o +$(BUILD_DIR)rsa_private_key.o : $(RSA_DIR)rsa_private_key.c $(RSA_DIR)rsa_private_key.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)rsa_public_key.o +$(BUILD_DIR)rsa_public_key.o : $(RSA_DIR)rsa_public_key.c $(RSA_DIR)rsa_public_key.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/lib/crypto/rsa/rsa_private_key.c b/src/charon/lib/crypto/rsa/rsa_private_key.c new file mode 100644 index 000000000..358653f0e --- /dev/null +++ b/src/charon/lib/crypto/rsa/rsa_private_key.c @@ -0,0 +1,772 @@ +/** + * @file rsa_private_key.c + * + * @brief Implementation of rsa_private_key_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "rsa_private_key.h" + +#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]; + + +/** + * 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; + + /** + * @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(); + +/** + * 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 oid; + chunk_t em; + + /* 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) +{ + if (mpz_cmp(this->n, *public->get_modulus(public)) == 0) + { + return TRUE; + } + return FALSE; +} + +/** + * 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->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); +} + +/** + * Internal generic constructor + */ +static private_rsa_private_key_t *rsa_private_key_create_empty() +{ + 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); + + 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; + + if (check(this) != SUCCESS) + { + destroy(this); + return NULL; + } + else + { + return &this->public; + } +} + +/* + * see header + * TODO: PEM files + */ +rsa_private_key_t *rsa_private_key_create_from_file(char *filename, char *passphrase) +{ + chunk_t chunk; + struct stat stb; + FILE *file; + char *buffer; + + if (stat(filename, &stb) == -1) + { + return NULL; + } + + buffer = alloca(stb.st_size); + + file = fopen(filename, "r"); + if (file == NULL) + { + return NULL; + } + + if (fread(buffer, stb.st_size, 1, file) != 1) + { + fclose(file); + return NULL; + } + fclose(file); + + chunk.ptr = buffer; + chunk.len = stb.st_size; + + return rsa_private_key_create_from_chunk(chunk); +} diff --git a/src/charon/lib/crypto/rsa/rsa_private_key.h b/src/charon/lib/crypto/rsa/rsa_private_key.h new file mode 100644 index 000000000..b3b8ae87f --- /dev/null +++ b/src/charon/lib/crypto/rsa/rsa_private_key.h @@ -0,0 +1,185 @@ +/** + * @file rsa_private_key.h + * + * @brief Interface of rsa_private_key_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_PRIVATE_KEY_H_ +#define RSA_PRIVATE_KEY_H_ + +#include +#include +#include +#include + + +typedef struct rsa_private_key_t rsa_private_key_t; + +/** + * @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 lenght. + * + * @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 + * @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, char *passphrase); + +#endif /*RSA_PRIVATE_KEY_H_*/ diff --git a/src/charon/lib/crypto/rsa/rsa_public_key.c b/src/charon/lib/crypto/rsa/rsa_public_key.c new file mode 100644 index 000000000..6601b6cda --- /dev/null +++ b/src/charon/lib/crypto/rsa/rsa_public_key.c @@ -0,0 +1,458 @@ +/** + * @file rsa_public_key.c + * + * @brief Implementation of rsa_public_key_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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 following hash. + * These values are also used in rsa_private_key.c. + * TODO: We may move them in asn1 sometime... + */ + +u_int8_t md2_oid[] = { + 0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x02,0x02,0x05,0x00, + 0x04,0x10 +}; + +u_int8_t md5_oid[] = { + 0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00, + 0x04,0x10 +}; + +u_int8_t sha1_oid[] = { + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e, + 0x03,0x02,0x1a,0x05,0x00,0x04,0x14 +}; + +u_int8_t sha256_oid[] = { + 0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, + 0x00,0x04,0x20 +}; + +u_int8_t sha384_oid[] = { + 0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05, + 0x00,0x04,0x30 +}; + +u_int8_t sha512_oid[] = { + 0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05, + 0x00,0x04,0x40 +}; + +/* 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; + + /** + * @brief Implements the RSAEP algorithm specified in PKCS#1. + * + * @param this calling object + * @param data data to process + * @return processed data + */ + chunk_t (*rsaep) (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) (private_rsa_public_key_t *this, chunk_t data); +}; + + +typedef struct rsa_public_key_info_t rsa_public_key_info_t; + +/** + * KeyInfo, as it appears in a public key file + */ +struct rsa_public_key_info_t { + /** + * Algorithm for this key + */ + chunk_t algorithm_oid; + + /** + * Public key, parseable with rsa_public_key_rules + */ + chunk_t public_key; +}; + +private_rsa_public_key_t *rsa_public_key_create_empty(); + +/** + * Implementation of private_rsa_public_key_t.rsaep and private_rsa_public_key_t.rsavp1 + */ +static chunk_t rsaep(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(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; + + 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)) + { + free(em.ptr); + return FAILED; + } + + /* 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 ?!*/ + free(em.ptr); + return FAILED; + } + pos++; + } + + if (pos + 20 > em.ptr + em.len) + { + /* not enought room for oid compare */ + free(em.ptr); + return FAILED; + } + + if (memcmp(md2_oid, pos, sizeof(md2_oid)) == 0) + { + hasher = hasher_create(HASH_MD2); + pos += sizeof(md2_oid); + } + else if (memcmp(md5_oid, pos, sizeof(md5_oid)) == 0) + { + hasher = hasher_create(HASH_MD5); + pos += sizeof(md5_oid); + } + else if (memcmp(sha1_oid, pos, sizeof(sha1_oid)) == 0) + { + hasher = hasher_create(HASH_SHA1); + pos += sizeof(sha1_oid); + } + else if (memcmp(sha256_oid, pos, sizeof(sha256_oid)) == 0) + { + hasher = hasher_create(HASH_SHA256); + pos += sizeof(sha256_oid); + } + else if (memcmp(sha384_oid, pos, sizeof(sha384_oid)) == 0) + { + hasher = hasher_create(HASH_SHA384); + pos += sizeof(sha384_oid); + } + else if (memcmp(sha512_oid, pos, sizeof(sha512_oid)) == 0) + { + hasher = hasher_create(HASH_SHA512); + pos += sizeof(sha512_oid); + } + + if (hasher == NULL) + { + /* not supported hash algorithm */ + free(em.ptr); + return NOT_SUPPORTED; + } + + if (pos + hasher->get_hash_size(hasher) != em.ptr + em.len) + { + /* bad length */ + free(em.ptr); + hasher->destroy(hasher); + return FAILED; + } + + /* build own hash for a compare */ + hasher->allocate_hash(hasher, data, &hash); + hasher->destroy(hasher); + + if (memcmp(hash.ptr, pos, hash.len) != 0) + { + /* hash does not equal */ + free(hash.ptr); + free(em.ptr); + return FAILED; + + } + + /* seems good */ + free(hash.ptr); + free(em.ptr); + return SUCCESS; +} + +/** + * Implementation of rsa_public_key.get_key. + */ +static status_t get_key(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(private_rsa_public_key_t *this, char *file) +{ + return NOT_SUPPORTED; +} + +/** + * Implementation of rsa_public_key.get_modulus. + */ +static mpz_t *get_modulus(private_rsa_public_key_t *this) +{ + return &this->n; +} + +/** + * Implementation of rsa_public_key.clone. + */ +static rsa_public_key_t* _clone(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->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); +} + +/** + * Generic private constructor + */ +private_rsa_public_key_t *rsa_public_key_create_empty() +{ + private_rsa_public_key_t *this = malloc_thing(private_rsa_public_key_t); + + /* public functions */ + this->public.verify_emsa_pkcs1_signature = (status_t (*) (rsa_public_key_t*,chunk_t,chunk_t))verify_emsa_pkcs1_signature; + this->public.get_key = (status_t (*) (rsa_public_key_t*,chunk_t*))get_key; + this->public.save_key = (status_t (*) (rsa_public_key_t*,char*))save_key; + this->public.get_modulus = (mpz_t *(*) (rsa_public_key_t*))get_modulus; + this->public.clone = (rsa_public_key_t* (*) (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; +} + +/* + * 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; + + this = rsa_public_key_create_empty(); + mpz_init(this->n); + mpz_init(this->e); + + asn1_init(&ctx, blob, 0, 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; + return &this->public; +} + +/* + * See header + */ +rsa_public_key_t *rsa_public_key_create_from_file(char *filename) +{ + struct stat stb; + FILE *file; + char *buffer; + chunk_t chunk; + + if (stat(filename, &stb) == -1) + { + return NULL; + } + + buffer = alloca(stb.st_size); + + file = fopen(filename, "r"); + if (file == NULL) + { + return NULL; + } + + if (fread(buffer, stb.st_size, 1, file) != 1) + { + return NULL; + } + + chunk.ptr = buffer; + chunk.len = stb.st_size; + + return rsa_public_key_create_from_chunk(chunk); +} diff --git a/src/charon/lib/crypto/rsa/rsa_public_key.h b/src/charon/lib/crypto/rsa/rsa_public_key.h new file mode 100644 index 000000000..ef79153d6 --- /dev/null +++ b/src/charon/lib/crypto/rsa/rsa_public_key.h @@ -0,0 +1,153 @@ +/** + * @file rsa_public_key.h + * + * @brief Interface of rsa_public_key_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_PUBLIC_KEY_H_ +#define RSA_PUBLIC_KEY_H_ + +#include + +#include +#include + + +typedef struct rsa_public_key_t rsa_public_key_t; + +/** + * @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) (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) (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) (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) (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) (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 struct (ASN1 DER encoded). + * + * @param chunk chunk containing the DER encoded key + * @return loaded rsa_public_key_t, or NULL + * + * @todo Check OID in PublicKeyInfo + * + * @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 + * + * @todo Implement PEM file loading + * + * @ingroup rsa + */ +rsa_public_key_t *rsa_public_key_create_from_file(char *filename); + +#endif /*RSA_PUBLIC_KEY_H_*/ diff --git a/src/charon/lib/crypto/signers/Makefile.signers b/src/charon/lib/crypto/signers/Makefile.signers new file mode 100644 index 000000000..8f161a09d --- /dev/null +++ b/src/charon/lib/crypto/signers/Makefile.signers @@ -0,0 +1,23 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +SIGNERS_DIR= $(CRYPTO_DIR)signers/ + +LIB_OBJS+= $(BUILD_DIR)signer.o +$(BUILD_DIR)signer.o : $(SIGNERS_DIR)signer.c $(SIGNERS_DIR)signer.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)hmac_signer.o +$(BUILD_DIR)hmac_signer.o : $(SIGNERS_DIR)hmac_signer.c $(SIGNERS_DIR)hmac_signer.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/lib/crypto/signers/hmac_signer.c b/src/charon/lib/crypto/signers/hmac_signer.c new file mode 100644 index 000000000..cb7d08244 --- /dev/null +++ b/src/charon/lib/crypto/signers/hmac_signer.c @@ -0,0 +1,169 @@ +/** + * @file hmac_signer.c + * + * @brief Implementation of hmac_signer_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "hmac_signer.h" + +#include + +/** + * This class represents a hmac signer with 12 byte (96 bit) output. + */ +#define BLOCK_SIZE 12 + +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; +}; + +/** + * 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 aka signature :-) */ + memcpy(buffer,full_mac,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(BLOCK_SIZE); + signature.len = BLOCK_SIZE; + + /* copy signature */ + memcpy(signature.ptr,full_mac,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 != BLOCK_SIZE) + { + return FALSE; + } + + /* compare mac aka signature :-) */ + if (memcmp(signature.ptr,full_mac,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 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) +{ + 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; + } + + /* 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/charon/lib/crypto/signers/hmac_signer.h b/src/charon/lib/crypto/signers/hmac_signer.h new file mode 100644 index 000000000..62427167e --- /dev/null +++ b/src/charon/lib/crypto/signers/hmac_signer.h @@ -0,0 +1,58 @@ +/** + * @file hmac_signer.h + * + * @brief Interface of hmac_signer_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HMAC_SIGNER_H_ +#define HMAC_SIGNER_H_ + +#include +#include + +typedef struct hmac_signer_t hmac_signer_t; + +/** + * @brief Implementation of signer_t interface using the + * HMAC algorithm in combination with either MD5 or SHA1. + * + * @ingroup signers + */ +struct hmac_signer_t { + + /** + * generic signer_t interface for this signer + */ + signer_t signer_interface; +}; + +/** + * @brief Creates a new hmac_signer_t. + * + * @param hash_algoritm Hash algorithm to use with signer + * @return + * - hmac_signer_t + * - NULL if hash algorithm not supported + * + * @ingroup signers + */ +hmac_signer_t *hmac_signer_create(hash_algorithm_t hash_algoritm); + + +#endif /*HMAC_SIGNER_H_*/ diff --git a/src/charon/lib/crypto/signers/signer.c b/src/charon/lib/crypto/signers/signer.c new file mode 100644 index 000000000..3e6378957 --- /dev/null +++ b/src/charon/lib/crypto/signers/signer.c @@ -0,0 +1,59 @@ +/** + * @file signer.c + * + * @brief Implementation of generic signer_t constructor. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "signer.h" + +#include + +/** + * String mappings for integrity_algorithm_t. + */ +mapping_t integrity_algorithm_m[] = { + {AUTH_UNDEFINED, "AUTH_UNDEFINED"}, + {AUTH_HMAC_MD5_96, "AUTH_HMAC_MD5_96"}, + {AUTH_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96"}, + {AUTH_DES_MAC, "AUTH_DES_MAC"}, + {AUTH_KPDK_MD5, "AUTH_KPDK_MD5"}, + {AUTH_AES_XCBC_96, "AUTH_AES_XCBC_96"}, + {MAPPING_END, NULL} +}; + + +/* + * 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)); + } + case AUTH_HMAC_MD5_96: + { + return ((signer_t *) hmac_signer_create(HASH_MD5)); + } + default: + return NULL; + } +} diff --git a/src/charon/lib/crypto/signers/signer.h b/src/charon/lib/crypto/signers/signer.h new file mode 100644 index 000000000..9625af813 --- /dev/null +++ b/src/charon/lib/crypto/signers/signer.h @@ -0,0 +1,147 @@ +/** + * @file signer.h + * + * @brief Interface for signer_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SIGNER_H_ +#define SIGNER_H_ + +#include +#include + +typedef enum integrity_algorithm_t integrity_algorithm_t; + +/** + * @brief Integrity algorithm, as in IKEv2 RFC 3.3.2. + * + * Currently only the following algorithms are implemented and therefore supported: + * - AUTH_HMAC_MD5_96 + * - AUTH_HMAC_SHA1_96 + * + * @ingroup signers + */ +enum integrity_algorithm_t { + AUTH_UNDEFINED = 1024, + /** + * Implemented in class hmac_signer_t. + */ + AUTH_HMAC_MD5_96 = 1, + /** + * Implemented in class hmac_signer_t. + */ + AUTH_HMAC_SHA1_96 = 2, + AUTH_DES_MAC = 3, + AUTH_KPDK_MD5 = 4, + AUTH_AES_XCBC_96 = 5 +}; + +/** + * String mappings for integrity_algorithm_t. + */ +extern mapping_t integrity_algorithm_m[]; + + +typedef struct signer_t signer_t; + +/** + * @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/charon/lib/crypto/x509.c b/src/charon/lib/crypto/x509.c new file mode 100755 index 000000000..86a595618 --- /dev/null +++ b/src/charon/lib/crypto/x509.c @@ -0,0 +1,937 @@ +/** + * @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 "x509.h" + +#include +#include +#include +#include + +typedef const char *err_t; /* error message, or NULL for success */ + + +#define BUF_LEN 512 +#define RSA_MIN_OCTETS (512 / 8) +#define RSA_MIN_OCTETS_UGH "RSA modulus too small for security: less than 512 bits" +#define RSA_MAX_OCTETS (8192 / 8) +#define RSA_MAX_OCTETS_UGH "RSA modulus too large: more than 8192 bits" + +logger_t *logger; + +typedef enum generalNames_t generalNames_t; + +/** + * Different kinds of generalNames + */ +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 generalName_t generalName_t; + +/** + * A generalName, chainable in a list + */ +struct generalName_t { + generalName_t *next; + generalNames_t kind; + chunk_t name; +}; + +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; + + /** + * Version of the X509 certificate + */ + u_int version; + + /** + * ID representing the certificates subject + */ + identification_t *subject; + + /** + * ID representing the certificate issuer + */ + identification_t *issuer; + + /** + * List of identification_t's representing subjectAltNames + */ + linked_list_t *subjectAltNames; + + /** + * List of identification_t's representing issuerAltNames + */ + linked_list_t *issuerAltNames; + + /** + * List of identification_t's representing crlDistributionPoints + */ + linked_list_t *crlDistributionPoints; + + /** + * Type of the subjects Key (currently RSA only) + */ + auth_method_t subjectPublicKeyAlgorithm; + + + /** + * Subjects RSA public key, if subjectPublicKeyAlgorithm == RSA + */ + rsa_public_key_t *public_key; + + + + + time_t installed; + u_char authority_flags; + chunk_t x509; + chunk_t tbsCertificate; + chunk_t serialNumber; + /* signature */ + int sigAlg; + /* validity */ + time_t notBefore; + time_t notAfter; + /* subjectPublicKeyInfo */ + chunk_t subjectPublicKey; + /* issuerUniqueID */ + /* subjectUniqueID */ + /* v3 extensions */ + /* extension */ + /* extension */ + /* extnID */ + /* critical */ + /* extnValue */ + bool isCA; + bool isOcspSigner; /* ocsp */ + chunk_t subjectKeyID; + chunk_t authKeyID; + chunk_t authKeySerialNumber; + chunk_t accessLocation; /* ocsp */ + /* signatureAlgorithm */ + int algorithm; + chunk_t signature; +}; + +/** + * 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(private_x509_t *this, private_x509_t *other) +{ + return chunk_equals(this->signature, other->signature); +} + +/** + * encode a linked list of subjectAltNames + */ +chunk_t build_subjectAltNames(generalName_t *subjectAltNames) +{ + u_char *pos; + chunk_t names; + size_t len = 0; + generalName_t *gn = subjectAltNames; + + /* compute the total size of the ASN.1 attributes object */ + while (gn != NULL) + { + len += gn->name.len; + gn = gn->next; + } + + pos = build_asn1_object(&names, ASN1_SEQUENCE, len); + + gn = subjectAltNames; + while (gn != NULL) + { + memcpy(pos, gn->name.ptr, gn->name.len); + pos += gn->name.len; + gn = gn->next; + } + + return asn1_wrap(ASN1_SEQUENCE, "cm", + ASN1_subjectAltName_oid, + asn1_wrap(ASN1_OCTET_STRING, "m", names) + ); +} + +/** + * free the dynamic memory used to store generalNames + */ +void free_generalNames(generalName_t* gn, bool free_name) +{ + while (gn != NULL) + { + generalName_t *gn_top = gn; + if (free_name) + { + free(gn->name.ptr); + } + gn = gn->next; + free(gn_top); + } +} + +/** + * 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); + + while (objectID < BASIC_CONSTRAINTS_ROOF) { + + if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx)) + { + break; + } + if (objectID == BASIC_CONSTRAINTS_CA) + { + isCA = object.len && *object.ptr; + logger->log(logger, RAW|LEVEL1, " %s", isCA ? "TRUE" : "FALSE"); + } + objectID++; + } + return isCA; +} + +/** + * 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); + + while (objectID < GENERAL_NAMES_ROOF) + { + if (!extract_object(generalNamesObjects, &objectID, &object, &level, &ctx)) + return; + + if (objectID == GENERAL_NAMES_GN) + { + list->insert_last(list, identification_create_from_encoding(ID_DER_ASN1_GN, object)); + } + 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); + + 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); + + 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); + 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, chunk_t *accessLocation) +{ + asn1_ctx_t ctx; + chunk_t object; + u_int level; + int objectID = 0; + + u_int accessMethod = OID_UNKNOWN; + + asn1_init(&ctx, blob, level0, 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) + { + if (asn1_length(&object) == ASN1_INVALID_LENGTH) + { + return; + } + logger->log(logger, RAW|LEVEL1, " '%.*s'",(int)object.len, object.ptr); + /* only HTTP(S) URIs accepted */ + if (strncasecmp(object.ptr, "http", 4) == 0) + { + *accessLocation = object; + return; + } + } + logger->log(logger, ERROR|LEVEL2, "ignoring OCSP InfoAccessLocation with unkown protocol"); + 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); + 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); + 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 x509 + */ +bool parse_x509cert(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); + 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->x509 = 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; + logger->log(logger, RAW|LEVEL1, " 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); + 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); + break; + case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM: + if (parse_algorithmIdentifier(object, level, NULL) == OID_RSA_ENCRYPTION) + { + cert->subjectPublicKeyAlgorithm = RSA_DIGITAL_SIGNATURE; + } + else + { + logger->log(logger, ERROR|LEVEL1, " 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 + { + logger->log(logger, ERROR|LEVEL1, " 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; + logger->log(logger, ERROR|LEVEL1, " %s", critical ? "TRUE" : "FALSE"); + break; + case X509_OBJ_EXTN_VALUE: + { + switch (extn_oid) { + case OID_SUBJECT_KEY_ID: + cert->subjectKeyID = 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->accessLocation); + 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++; + } + time(&cert->installed); + return TRUE; +} + +/** + * verify the validity of a x509 by + * checking the notBefore and notAfter dates + */ +err_t check_validity(const private_x509_t *cert, time_t *until) +{ + time_t current_time; + + time(¤t_time); + + if (cert->notAfter < *until) + { + *until = cert->notAfter; + } + if (current_time < cert->notBefore) + { + return "x509 is not valid yet"; + } + if (current_time > cert->notAfter) + { + return "x509 has expired"; + } + else + { + return NULL; + } +} + +/** + * Implements x509_t.get_public_key + */ +static rsa_public_key_t *get_public_key(private_x509_t *this) +{ + return this->public_key->clone(this->public_key);; +} + +/** + * Implements x509_t.get_subject + */ +static identification_t *get_subject(private_x509_t *this) +{ + return this->subject; +} + +/** + * Implements x509_t.get_issuer + */ +static identification_t *get_issuer(private_x509_t *this) +{ + return this->issuer; +} + +/** + * destroy + */ +static void destroy(private_x509_t *this) +{ + identification_t *id; + while (this->subjectAltNames->remove_last(this->subjectAltNames, (void**)&id) == SUCCESS) + { + id->destroy(id); + } + this->subjectAltNames->destroy(this->subjectAltNames); + while (this->issuerAltNames->remove_last(this->issuerAltNames, (void**)&id) == SUCCESS) + { + id->destroy(id); + } + this->issuerAltNames->destroy(this->issuerAltNames); + while (this->crlDistributionPoints->remove_last(this->crlDistributionPoints, (void**)&id) == SUCCESS) + { + id->destroy(id); + } + this->crlDistributionPoints->destroy(this->crlDistributionPoints); + if (this->issuer) + { + this->issuer->destroy(this->issuer); + } + if (this->subject) + { + this->subject->destroy(this->subject); + } + if (this->public_key) + { + this->public_key->destroy(this->public_key); + } + free(this); +} + +/* + * Described in header. + */ +x509_t *x509_create_from_chunk(chunk_t chunk) +{ + private_x509_t *this = malloc_thing(private_x509_t); + + /* public functions */ + this->public.equals = (bool (*) (x509_t*,x509_t*))equals; + this->public.destroy = (void (*) (x509_t*))destroy; + this->public.get_public_key = (rsa_public_key_t* (*) (x509_t*))get_public_key; + this->public.get_subject = (identification_t* (*) (x509_t*))get_subject; + this->public.get_issuer = (identification_t* (*) (x509_t*))get_issuer; + + /* initialize */ + this->subjectPublicKey = CHUNK_INITIALIZER; + this->public_key = NULL; + this->subject = NULL; + this->issuer = NULL; + this->subjectAltNames = linked_list_create(this->subjectAltNames); + this->issuerAltNames = linked_list_create(this->issuerAltNames); + this->crlDistributionPoints = linked_list_create(this->crlDistributionPoints); + + /* we do not use a per-instance logger right now, since its not always accessible */ + logger = logger_manager->get_logger(logger_manager, ASN1); + + if (!is_asn1(chunk) || + !parse_x509cert(chunk, 0, this)) + { + destroy(this); + return NULL; + } + + this->public_key = rsa_public_key_create_from_chunk(this->subjectPublicKey); + if (this->public_key == NULL) + { + destroy(this); + return NULL; + } + + return &this->public; +} + +/* + * Described in header. + */ +x509_t *x509_create_from_file(char *filename) +{ + struct stat stb; + FILE *file; + char *buffer; + chunk_t chunk; + + if (stat(filename, &stb) == -1) + { + return NULL; + } + + buffer = alloca(stb.st_size); + + file = fopen(filename, "r"); + if (file == NULL) + { + return NULL; + } + + if (fread(buffer, stb.st_size, 1, file) == -1) + { + fclose(file); + return NULL; + } + fclose(file); + + chunk.ptr = buffer; + chunk.len = stb.st_size; + + return x509_create_from_chunk(chunk); +} diff --git a/src/charon/lib/crypto/x509.h b/src/charon/lib/crypto/x509.h new file mode 100755 index 000000000..077238eab --- /dev/null +++ b/src/charon/lib/crypto/x509.h @@ -0,0 +1,136 @@ +/** + * @file x509.h + * + * @brief Interface 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. + */ + +#ifndef X509_H_ +#define X509_H_ + +#include +#include +#include +#include +#include + + +typedef struct x509_t x509_t; + +/** + * @brief X509 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 Get the RSA public key from the certificate. + * + * @param this calling object + * @return public_key + */ + rsa_public_key_t *(*get_public_key) (x509_t *this); + + /** + * @brief Get the certificate issuers 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) (x509_t *this); + + /** + * @brief Get the subjects ID. + * + * 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) (x509_t *this); + + /** + * @brief Check if a certificate is valid. + * + * This function uses the issuers public key to verify + * the validity of a certificate. + * + * @todo implement! + */ + bool (*verify) (x509_t *this, rsa_public_key_t *signer); + + /** + * @brief Get the key identifier of the public key. + * + * @todo implement! + */ + chunk_t (*get_subject_key_identifier) (x509_t *this); + + /** + * @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) (x509_t *this, x509_t *other); + + /** + * @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 invalid. + * + * @ingroup transforms + */ +x509_t *x509_create_from_chunk(chunk_t chunk); + +/** + * @brief Read a x509 certificate from a DER encoded file. + * + * @param filename file containing DER encoded data + * @return created x509_t certificate, or NULL if invalid. + * + * @ingroup transforms + */ +x509_t *x509_create_from_file(char *filename); + +#endif /* X509_H_ */ diff --git a/src/charon/lib/definitions.c b/src/charon/lib/definitions.c new file mode 100644 index 000000000..59c97a29b --- /dev/null +++ b/src/charon/lib/definitions.c @@ -0,0 +1,40 @@ +/** + * @file definitions.c + * + * @brief General purpose definitions and macros. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "definitions.h" + +/* + * Described in header. + */ +char *mapping_find(mapping_t * maps, int value) +{ + int i = 0; + while (maps[i].value != MAPPING_END) + { + if (maps[i].value == value) + { + return maps[i].string; + } + i++; + } + return "INVALID MAPPING"; +} diff --git a/src/charon/lib/definitions.h b/src/charon/lib/definitions.h new file mode 100644 index 000000000..c9ef066c1 --- /dev/null +++ b/src/charon/lib/definitions.h @@ -0,0 +1,120 @@ +/** + * @file definitions.h + * + * @brief General purpose definitions and macros. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * Copyright (C) 1998, 1999 D. Hugh Redelmeier. (Endian stuff) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT 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 DEFINITIONS_H_ +#define DEFINITIONS_H_ + + + +/* stolen from strongswan */ +#if linux +# if defined(i386) && !defined(__i386__) +# define __i386__ 1 +# define MYHACKFORTHIS 1 +# endif +# include +# ifdef MYHACKFORTHIS +# undef __i386__ +# undef MYHACKFORTHIS +# endif +#elif !(defined(BIG_ENDIAN) && defined(LITTLE_ENDIAN) && defined(BYTE_ORDER)) + /* we don't know how to do this, so we require the macros to be defined + * with compiler flags: + * -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234 -DBYTE_ORDER=BIG_ENDIAN + * or -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234 -DBYTE_ORDER=LITTLE_ENDIAN + * Thse match the GNU definitions + */ +# include +#endif + +#ifndef BIG_ENDIAN + #error "BIG_ENDIAN must be defined" +#endif + +#ifndef LITTLE_ENDIAN + #error "LITTLE_ENDIAN must be defined" +#endif + +#ifndef BYTE_ORDER + #error "BYTE_ORDER must be defined" +#endif + + +/** + * 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) + +/** + * Debug macro to follow control flow + */ +#define POS printf("%s, line %d\n", __FILE__, __LINE__) + +/** + * Macro to allocate a sized type. + * + * @param thing object on which a sizeof is performed + * @return poiner to allocated memory + */ +#define malloc_thing(thing) ((thing*)malloc(sizeof(thing))) + + +/** + * Mapping entry which defines the end of a mapping_t array. + */ +#define MAPPING_END (-1) + +typedef struct mapping_t mapping_t; + +/** + * @brief Mapping entry, where enum-to-string mappings are stored. + */ +struct mapping_t +{ + /** + * Enumeration value. + */ + int value; + + /** + * Mapped string. + */ + char *string; +}; + + +/** + * @brief Find a mapping_string in the mapping[]. + * + * @param mappings mappings array + * @param value enum-value to get the string from + * + */ +char *mapping_find(mapping_t *mappings, int value); + +#endif /*DEFINITIONS_H_*/ diff --git a/src/charon/lib/library.c b/src/charon/lib/library.c new file mode 100644 index 000000000..fa9c732bf --- /dev/null +++ b/src/charon/lib/library.c @@ -0,0 +1,42 @@ +/** + * @file library.c + * + * @brief Library (de-)initialization. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 + +/** + * Called whenever the library is linked from a process + */ +void __attribute__ ((constructor)) library_init() +{ + logger_manager_init(); + leak_detective_init(); +} + +/** + * Called whenever the library is unlinked from a process + */ +void __attribute__ ((destructor)) library_cleanup() +{ + leak_detective_cleanup(); + logger_manager_cleanup(); +} diff --git a/src/charon/lib/library.h b/src/charon/lib/library.h new file mode 100644 index 000000000..da96befe1 --- /dev/null +++ b/src/charon/lib/library.h @@ -0,0 +1,100 @@ +/** + * @file library.h + * + * @brief Global library header. + * + */ + +/* + * 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 lib lib + * + * libstrongswan: library with various crypto related things. + */ + +/** + * @defgroup asn1 asn1 + * + * ASN1 definitions, parser and generator functions. + * + * @ingroup lib + */ + +/** + * @defgroup crypto crypto + * + * Crypto algorithms of different kind. + * + * @ingroup lib + */ + +/** + * @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 lib + */ + + +#endif /* LIBRARY_H_ */ diff --git a/src/charon/lib/types.c b/src/charon/lib/types.c new file mode 100644 index 000000000..09ebf7310 --- /dev/null +++ b/src/charon/lib/types.c @@ -0,0 +1,115 @@ +/** + * @file types.c + * + * @brief Generic types. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "types.h" + + +/** + * String mappings for type status_t. + */ +mapping_t status_m[] = { + {SUCCESS, "SUCCESS"}, + {FAILED, "FAILED"}, + {OUT_OF_RES, "OUT_OF_RES"}, + {ALREADY_DONE, "ALREADY_DONE"}, + {NOT_SUPPORTED, "NOT_SUPPORTED"}, + {INVALID_ARG, "INVALID_ARG"}, + {NOT_FOUND, "NOT_FOUND"}, + {PARSE_ERROR, "PARSE_ERROR"}, + {VERIFY_ERROR, "VERIFY_ERROR"}, + {INVALID_STATE, "INVALID_STATE"}, + {DELETE_ME, "DELETE_ME"}, + {CREATED, "CREATED"}, + {MAPPING_END, NULL} +}; + +/** + * Empty chunk. + */ +chunk_t CHUNK_INITIALIZER = {NULL,0}; + +/** + * Described in header. + */ +chunk_t chunk_clone(chunk_t chunk) +{ + chunk_t clone = CHUNK_INITIALIZER; + + if (chunk.ptr && chunk.len > 0) + { + clone.ptr = malloc(chunk.len); + clone.len = chunk.len; + memcpy(clone.ptr, chunk.ptr, chunk.len); + } + + return clone; +} + +/** + * Described in header. + */ +void chunk_free(chunk_t *chunk) +{ + free(chunk->ptr); + chunk->ptr = NULL; + chunk->len = 0; +} + +/** + * Described in header. + */ +chunk_t chunk_alloc(size_t bytes) +{ + chunk_t new_chunk; + new_chunk.ptr = malloc(bytes); + new_chunk.len = bytes; + return new_chunk; +} + +/** + * Described in header. + */ +bool chunk_equals(chunk_t a, chunk_t b) +{ + if (a.ptr == NULL || b.ptr == NULL || + a.len != b.len || + memcmp(a.ptr, b.ptr, a.len) != 0) + { + return FALSE; + } + return TRUE; +} + +/** + * Described in header. + */ +void *clalloc(void * pointer, size_t size) +{ + void *data; + data = malloc(size); + + memcpy(data, pointer,size); + + return (data); +} diff --git a/src/charon/lib/types.h b/src/charon/lib/types.h new file mode 100644 index 000000000..0e0782b31 --- /dev/null +++ b/src/charon/lib/types.h @@ -0,0 +1,186 @@ +/** + * @file types.h + * + * @brief Generic types. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TYPES_H_ +#define TYPES_H_ + +#include +#include +#include + +#include + +/** + * 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 ressources. + */ + + OUT_OF_RES, + /** + * 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, + + /** + * Delete object which function belongs to. + */ + DELETE_ME, + + /** + * An object got created. + */ + CREATED, +}; + + +/** + * String mappings for type status_t. + */ +extern mapping_t status_m[]; + +/** + * 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; + +/** + * Use struct chunk_t as chunk_t. + */ +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; +}; + +/** + * {NULL, 0}-chunk, handy for initialization + * of chunks. + */ +extern chunk_t CHUNK_INITIALIZER; + +/** + * Initialize a chunk to a static buffer + */ +#define chunk_from_buf(str) { str, sizeof(str) } + +/** + * Clone chunk contents in a newly allocated chunk + */ +chunk_t chunk_clone(chunk_t chunk); + +/** + * Free contents of a chunk + */ +void chunk_free(chunk_t *chunk); + +/** + * Allocate a chunk + */ +chunk_t chunk_alloc(size_t bytes); + +/** + * Compare two chunks for equality, + * NULL chunks are never equal. + */ +bool chunk_equals(chunk_t a, chunk_t b); + +/** + * Clone a data to a newly allocated buffer + */ +void *clalloc(void *pointer, size_t size); + + +#endif /*TYPES_H_*/ diff --git a/src/charon/lib/utils/Makefile.utils b/src/charon/lib/utils/Makefile.utils new file mode 100644 index 000000000..1c82283d7 --- /dev/null +++ b/src/charon/lib/utils/Makefile.utils @@ -0,0 +1,47 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +UTILS_DIR= $(LIB_DIR)utils/ + +LIB_OBJS+= $(BUILD_DIR)leak_detective.o +$(BUILD_DIR)leak_detective.o : $(UTILS_DIR)leak_detective.c $(UTILS_DIR)leak_detective.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)linked_list.o +$(BUILD_DIR)linked_list.o : $(UTILS_DIR)linked_list.c $(UTILS_DIR)linked_list.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)logger.o +$(BUILD_DIR)logger.o : $(UTILS_DIR)logger.c $(UTILS_DIR)logger.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)logger_manager.o +$(BUILD_DIR)logger_manager.o : $(UTILS_DIR)logger_manager.c $(UTILS_DIR)logger_manager.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)randomizer.o +$(BUILD_DIR)randomizer.o : $(UTILS_DIR)randomizer.c $(UTILS_DIR)randomizer.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)tester.o +$(BUILD_DIR)tester.o : $(UTILS_DIR)tester.c $(UTILS_DIR)tester.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)identification.o +$(BUILD_DIR)identification.o : $(UTILS_DIR)identification.c $(UTILS_DIR)identification.h + $(CC) $(CFLAGS) -c -o $@ $< + +LIB_OBJS+= $(BUILD_DIR)host.o +$(BUILD_DIR)host.o : $(UTILS_DIR)host.c $(UTILS_DIR)host.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/lib/utils/host.c b/src/charon/lib/utils/host.c new file mode 100644 index 000000000..020ed27f3 --- /dev/null +++ b/src/charon/lib/utils/host.c @@ -0,0 +1,365 @@ +/** + * @file host.c + * + * @brief Implementation of host_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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; + + /** + * Address family to use, such as AF_INET or AF_INET6 + */ + int family; + + /** + * string representation of host + */ + char *string; + + /** + * low-lewel structure, wich stores the address + */ + union { + struct sockaddr address; + struct sockaddr_in address4; + }; + /** + * 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_default_route. + */ +static bool is_default_route (private_host_t *this) +{ + switch (this->family) + { + case AF_INET: + { + static u_int8_t default_route[4] = {0x00,0x00,0x00,0x00}; + + if (memcmp(default_route,&(this->address4.sin_addr.s_addr),4) == 0) + { + return TRUE; + } + return FALSE; + } + default: + { + /* empty chunk is returned */ + return FALSE; + } + } +} + +/** + * implements host_t.get_address + */ +static char *get_address(private_host_t *this) +{ + switch (this->family) + { + case AF_INET: + { + char *string; + /* we need to clone it, since inet_ntoa overwrites + * internal buffer on subsequent calls + */ + free(this->string); + string = inet_ntoa(this->address4.sin_addr); + this->string = malloc(strlen(string)+1); + strcpy(this->string, string); + return this->string; + } + default: + { + return "(family not supported)"; + } + } +} + +/** + * Implementation of host_t.get_address_as_chunk. + */ +static chunk_t get_address_as_chunk(private_host_t *this) +{ + chunk_t address = CHUNK_INITIALIZER; + + switch (this->family) + { + case AF_INET: + { + /* allocate 4 bytes for IPV4 address*/ + address.ptr = malloc(4); + address.len = 4; + memcpy(address.ptr,&(this->address4.sin_addr.s_addr),4); + } + default: + { + /* empty chunk is returned */ + return address; + } + } +} + +static xfrm_address_t get_xfrm_addr(private_host_t *this) +{ + switch (this->family) + { + case AF_INET: + { + return (xfrm_address_t)(this->address4.sin_addr.s_addr); + } + default: + { + /* todo */ + return (xfrm_address_t)(this->address4.sin_addr.s_addr); + } + } +} + +static int get_family(private_host_t *this) +{ + return this->family; +} + +/** + * implements host_t.get_port + */ +static u_int16_t get_port(private_host_t *this) +{ + switch (this->family) + { + case AF_INET: + { + return ntohs(this->address4.sin_port); + } + default: + { + return 0; + } + } +} + + +/** + * 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)); + if (this->string) + { + new->string = malloc(strlen(this->string)+1); + strcpy(new->string, this->string); + } + return new; +} + +/** + * Impelements host_t.ip_equals + */ +static bool ip_equals(private_host_t *this, private_host_t *other) +{ + switch (this->family) + { + /* IPv4 */ + case AF_INET: + { + if ((this->address4.sin_family == other->address4.sin_family) && + (this->address4.sin_addr.s_addr == other->address4.sin_addr.s_addr)) + { + return TRUE; + } + } + } + return FALSE; +} + +/** + * Impelements host_t.equals + */ +static bool equals(private_host_t *this, private_host_t *other) +{ + switch (this->family) + { + /* IPv4 */ + case AF_INET: + { + if ((this->address4.sin_family == other->address4.sin_family) && + (this->address4.sin_addr.s_addr == other->address4.sin_addr.s_addr) && + (this->address4.sin_port == other->address4.sin_port)) + { + return TRUE; + } + } + } + return FALSE; +} + +/** + * Implements host_t.destroy + */ +static void destroy(private_host_t *this) +{ + free(this->string); + free(this); +} + +/** + * Creates an empty host_t object + */ +static private_host_t *host_create_empty() +{ + 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_xfrm_addr = (xfrm_address_t (*) (host_t *))get_xfrm_addr; + this->public.get_address = (char* (*) (host_t *))get_address; + this->public.get_address_as_chunk = (chunk_t (*) (host_t *)) get_address_as_chunk; + this->public.get_port = (u_int16_t (*) (host_t *))get_port; + this->public.ip_equals = (bool (*) (host_t *,host_t *)) ip_equals; + this->public.equals = (bool (*) (host_t *,host_t *)) equals; + this->public.is_default_route = (bool (*) (host_t *)) is_default_route; + this->public.destroy = (void (*) (host_t*))destroy; + + this->string = NULL; + + return this; +} + +/* + * Described in header. + */ +host_t *host_create(int family, char *address, u_int16_t port) +{ + private_host_t *this = host_create_empty(); + + this->family = family; + + switch (family) + { + /* IPv4 */ + case AF_INET: + { + this->address4.sin_family = AF_INET; + this->address4.sin_addr.s_addr = inet_addr(address); + this->address4.sin_port = htons(port); + this->socklen = sizeof(struct sockaddr_in); + return &(this->public); + } + default: + { + 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->family = family; + switch (family) + { + /* IPv4 */ + case AF_INET: + { + if (address.len != 4) + { + break; + } + this->address4.sin_family = AF_INET; + 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); + } + } + free(this); + return NULL; +} + +/* + * Described in header. + */ +host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) +{ + chunk_t address; + + switch (sockaddr->sa_family) + { + /* IPv4 */ + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *)sockaddr; + address.ptr = (void*)&(sin->sin_addr.s_addr); + address.len = 4; + return host_create_from_chunk(AF_INET, address, ntohs(sin->sin_port)); + } + default: + return NULL; + } +} + diff --git a/src/charon/lib/utils/host.h b/src/charon/lib/utils/host.h new file mode 100644 index 000000000..d81efffa6 --- /dev/null +++ b/src/charon/lib/utils/host.h @@ -0,0 +1,225 @@ +/** + * @file host.h + * + * @brief Interface of host_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HOST_H_ +#define HOST_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + + +typedef struct host_t host_t; + +/** + * @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 network + */ +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. + * + * Sepending 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 address as xfrm_address_t. + * + * This function allows the conversion to an + * xfrm_address_t, used for netlink communication + * with the kernel. + * + * @see kernel_interface_t. + * + * @param this calling object + * @return address in xfrm_address_t format + */ + xfrm_address_t (*get_xfrm_addr) (host_t *this); + + /** + * @brief Gets the family of the address + * + * @param this calling object + * @return family + */ + int (*get_family) (host_t *this); + + /** + * @brief get the address of this host + * + * Mostly used for debugging purposes. + * @warning string must NOT be freed + * + * @param this object + * @return address string, + */ + char* (*get_address) (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_default_route) (host_t *this); + + /** + * @brief get the address of this host as chunk_t + * + * @warning returned chunk has to get destroyed by caller. + * + * @param this object + * @return address string, + */ + chunk_t (*get_address_as_chunk) (host_t *this); + + /** + * @brief get the port of this host + * + * Mostly used for debugging purposes. + * + * @param this object to clone + * @return port number + */ + u_int16_t (*get_port) (host_t *this); + + /** + * @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 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 + * + * Currently supports only IPv4! + * + * @param family Address family to use for this object, such as AF_INET or AF_INET6 + * @param address string of an address, such as "152.96.193.130" + * @param port port number + * @return + * - host_t object + * - NULL, if family not supported. + * + * @ingroup network + */ +host_t *host_create(int family, char *address, u_int16_t port); + +/** + * @brief Constructor to create a host_t object from an address chunk + * + * Currently supports only IPv4! + * + * @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 + * + * Currently supports only IPv4! + * + * @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); + + +#endif /*HOST_H_*/ diff --git a/src/charon/lib/utils/identification.c b/src/charon/lib/utils/identification.c new file mode 100644 index 000000000..33f3d92cd --- /dev/null +++ b/src/charon/lib/utils/identification.c @@ -0,0 +1,1167 @@ +/** + * @file identification.c + * + * @brief Implementation of identification_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "identification.h" + +#include + +/** + * String mappings for id_type_t. + */ +mapping_t id_type_m[] = { + {ID_IPV4_ADDR, "ID_IPV4_ADDR"}, + {ID_FQDN, "ID_FQDN"}, + {ID_RFC822_ADDR, "ID_RFC822_ADDR"}, + {ID_IPV6_ADDR, "ID_IPV6_ADDR"}, + {ID_DER_ASN1_DN, "ID_DER_ASN1_DN"}, + {ID_DER_ASN1_GN, "ID_DER_ASN1_GN"}, + {ID_KEY_ID, "ID_KEY_ID"}, + {ID_ANY, "ID_ANY"}, + {MAPPING_END, NULL} +}; + + +/** + * 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 + +/** + * Different kinds of generalNames + */ +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, +}; + +/** + * 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 + +typedef struct private_identification_t private_identification_t; + +/** + * Private data of an identification_t object. + */ +struct private_identification_t { + /** + * Public interface. + */ + identification_t public; + + /** + * String representation of this ID. + */ + char *string; + + /** + * Encoded representation of this ID. + */ + chunk_t encoded; + + /** + * Type of this ID. + */ + id_type_t type; +}; + +static private_identification_t *identification_create(); + + +/** + * 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++)); + } +} + +/** + * 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_INITIALIZER; + *attribute = CHUNK_INITIALIZER; + + /* 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_INITIALIZER; + *value = CHUNK_INITIALIZER; + + /* 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; + asn1_t type; + int oid_code; + bool next; + bool first = TRUE; + + status_t status = init_rdn(dn, &rdn, &attribute, &next); + + if (status != SUCCESS) + {/* a parsing error has occured */ + return status; + } + + while (next) + { + status = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next); + + if (status != SUCCESS) + {/* a parsing error has occured */ + 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 */ + update_chunk(str, snprintf(str->ptr,str->len,"=%.*s", (int)value.len,value.ptr)); + } + 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 (memcmp(a.ptr, b.ptr, b.len) == 0) + { + 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 */ + *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 == '*') + { + (*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! */ + return TRUE; +} + +/** + * get string representation of a general name + * TODO: Add support for gn types + */ +static char *gntoa(chunk_t blob) +{ + asn1_ctx_t ctx; + chunk_t object; + int objectID = 0; + u_int level; + char buf[128]; + + asn1_init(&ctx, blob, 0, FALSE); + + while (objectID < GN_OBJ_ROOF) + { + if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx)) + { + return NULL; + } + switch (objectID) + { + case GN_OBJ_RFC822_NAME: + case GN_OBJ_DNS_NAME: + case GN_OBJ_URI: + snprintf(buf, sizeof(buf), "%.*s", object.len, object.ptr); + return strdup(buf); + case GN_OBJ_IP_ADDRESS: + if (object.len == 4 && + inet_ntop(AF_INET, object.ptr, buf, sizeof(buf))) + { + return strdup(buf); + } + return NULL; + break; + case GN_OBJ_OTHER_NAME: + return strdup("(other name)"); + case GN_OBJ_X400_ADDRESS: + return strdup("(X400 Address)"); + case GN_OBJ_EDI_PARTY_NAME: + return strdup("(EDI party name)"); + case GN_OBJ_REGISTERED_ID: + return strdup("(registered ID)"); + case GN_OBJ_DIRECTORY_NAME: + return strdup("(directory name)"); + default: + break; + } + objectID++; + } + return NULL; +} + +/** + * 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; + + char *wrap_mode; + chunk_t oid = CHUNK_INITIALIZER; + chunk_t name = CHUNK_INITIALIZER; + chunk_t names[25]; /* max to 25 rdns */ + int name_count = 0; + int whitespace = 0; + int pos = 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 (pos = 0; pos < X501_RDN_ROOF; pos++) + { + if (strlen(x501rdns[pos].name) == oid.len && + strncasecmp(x501rdns[pos].name, oid.ptr, oid.len) == 0) + { + break; /* found a valid OID */ + } + } + if (pos == X501_RDN_ROOF) + { + status = NOT_SUPPORTED; + state = UNKNOWN_OID; + break; + } + /* reset oid and change state */ + oid = CHUNK_INITIALIZER; + 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[pos].type == ASN1_PRINTABLESTRING + && !is_printablestring(name))? ASN1_T61STRING : x501rdns[pos].type; + + if (name_count < 25) + { + names[name_count++] = + asn1_wrap(ASN1_SET, "m", + asn1_wrap(ASN1_SEQUENCE, "mm", + asn1_wrap(ASN1_OID, "c", x501rdns[pos].oid), + asn1_wrap(rdn_type, "c", name) + ) + ); + } + else + { + status = OUT_OF_RES; + } + /* reset name and change state */ + name = CHUNK_INITIALIZER; + state = SEARCH_OID; + } + break; + case UNKNOWN_OID: + break; + } + } while (*src++ != '\0'); + + + /* build the distinguished name sequence */ + wrap_mode = alloca(26); + memset(wrap_mode, 0, 26); + memset(wrap_mode, 'm', name_count); + *dn = asn1_wrap(ASN1_SEQUENCE, wrap_mode, + names[0], names[1], names[2], names[3], names[4], + names[5], names[6], names[7], names[8], names[9], + names[10], names[11], names[12], names[13], names[14], + names[15], names[16], names[17], names[18], names[19], + names[20], names[21], names[22], names[23], names[24]); + if (status != SUCCESS) + { + free(dn->ptr); + *dn = CHUNK_INITIALIZER; + } + 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.get_string. + */ +static char *get_string(private_identification_t *this) +{ + return this->string; +} + +/** + * Implementation of identification_t.contains_wildcards. + */ +static bool contains_wildcards(private_identification_t *this) +{ + if (this->type == ID_ANY || + memchr(this->encoded.ptr, '*', this->encoded.len) != NULL) + { + return TRUE; + } + return FALSE; +} + +/** + * Default implementation of identification_t.equals and identification_t.belongs_to. + * compares encoded chunk for equality. + */ +static bool equals_binary(private_identification_t *this,private_identification_t *other) +{ + if (this->type == other->type) + { + if (this->encoded.len == other->encoded.len && + memcmp(this->encoded.ptr, other->encoded.ptr, this->encoded.len) == 0) + { + return TRUE; + } + } + return FALSE; +} + +/** + * 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); +} + +/** + * Special implementation of identification_t.belongs_to for ID_RFC822_ADDR/ID_FQDN. + * checks for a wildcard in other-string, and compares it against this-string. + */ +static bool belongs_to_wc_string(private_identification_t *this, private_identification_t *other) +{ + char *this_str, *other_str, *pos; + + if (other->type == ID_ANY) + { + return TRUE; + } + + if (this->type == other->type) + { + /* try a binary comparison first */ + if (equals_binary(this, other)) + { + return TRUE; + } + } + if (other->encoded.len > 0 && + *(other->encoded.ptr) == '*') + { + if (other->encoded.len == 1) + { + /* other contains just a wildcard, and therefore matches anything */ + return TRUE; + } + /* We strdup chunks, since they are NOT null-terminated */ + this_str = strndupa(this->encoded.ptr, this->encoded.len); + other_str = strndupa(other->encoded.ptr + 1, other->encoded.len - 1); + pos = strstr(this_str, other_str); + if (pos != NULL) + { + /* ok, other is contained in this, but there may be more characters, so check it */ + if (strlen(pos) == strlen(other_str)) + { + return TRUE; + } + } + } + + return FALSE; +} + +/** + * Special implementation of identification_t.belongs_to for ID_ANY. + * ANY matches only another ANY, but nothing other + */ +static bool belongs_to_any(private_identification_t *this, private_identification_t *other) +{ + if (other->type == ID_ANY) + { + return TRUE; + } + return FALSE; +} + +/** + * Special implementation of identification_t.belongs_to for ID_DER_ASN1_DN. + * ANY matches any, even ANY, thats why its there... + */ +static bool belongs_to_dn(private_identification_t *this, private_identification_t *other) +{ + int wildcards; + + if (other->type == ID_ANY) + { + return TRUE; + } + + if (this->type == other->type) + { + return match_dn(this->encoded, other->encoded, &wildcards); + } + return FALSE; +} + +/** + * 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->string = malloc(strlen(this->string) + 1); + strcpy(clone->string, this->string); + + return &clone->public; +} + +/** + * Implementation of identification_t.destroy. + */ +static void destroy(private_identification_t *this) +{ + free(this->string); + free(this->encoded.ptr); + free(this); +} + +/** + * Generic constructor used for the other constructors. + */ +static private_identification_t *identification_create() +{ + 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.get_string = (char* (*) (identification_t*))get_string; + 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.belongs_to = (bool (*) (identification_t*,identification_t*))equals_binary; + + this->string = NULL; + this->encoded = CHUNK_INITIALIZER; + + return this; +} + +/* + * Described in header. + */ +identification_t *identification_create_from_string(char *string) +{ + private_identification_t *this = identification_create(); + + 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 + * discard optional @ character in front of DN + */ + if (atodn((*string == '@') ? string + 1 : string, &this->encoded) != SUCCESS) + { + free(this); + return NULL; + } + this->string = strdup(string); + this->type = ID_DER_ASN1_DN; + this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn; + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn; + return &this->public; + } + else if (strchr(string, '@') == NULL) + { + if (strcmp(string, "%any") == 0 || + strcmp(string, "0.0.0.0") == 0 || + strcmp(string, "*") == 0 || + strcmp(string, "::") == 0|| + strcmp(string, "0::0") == 0) + { + /* any ID will be accepted */ + this->type = ID_ANY; + this->string = strdup("%any"); + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_any; + return &this->public; + } + else + { + /* TODO: Pluto resolve domainnames without '@' to IPv4/6 address. Is this really needed? */ + + 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->string = strdup(string); + 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->string = strdup(string); + this->type = ID_IPV6_ADDR; + return &(this->public); + } + } + } + else + { + if (*string == '@') + { + if (*(string + 1) == '#') + { + /* TODO: Pluto handles '#' as hex encoded ASN1/KEY ID. Do we need this, too? */ + free(this); + return NULL; + } + else + { + this->type = ID_FQDN; + this->string = strdup(string + 1); /* discard @ */ + this->encoded.ptr = strdup(string + 1); + this->encoded.len = strlen(string + 1); + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; + return &(this->public); + } + } + else + { + this->type = ID_RFC822_ADDR; + this->string = strdup(string); + this->encoded.ptr = strdup(string); + this->encoded.len = strlen(string); + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_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(); + char buf[256]; + chunk_t buf_chunk = chunk_from_buf(buf); + char *pos; + + this->type = type; + switch (type) + { + case ID_ANY: + this->string = strdup("%any"); + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_any; + break; + case ID_IPV4_ADDR: + if (encoded.len < sizeof(struct in_addr) || + inet_ntop(AF_INET, encoded.ptr, buf, sizeof(buf)) == NULL) + { + this->string = strdup("(invalid ID_IPV4_ADDR)"); + } + else + { + this->string = strdup(buf); + } + break; + case ID_IPV6_ADDR: + if (encoded.len < sizeof(struct in6_addr) || + inet_ntop(AF_INET6, encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL) + { + this->string = strdup("(invalid ID_IPV6_ADDR)"); + } + else + { + this->string = strdup(buf); + } + break; + case ID_FQDN: + snprintf(buf, sizeof(buf), "@%.*s", encoded.len, encoded.ptr); + this->string = strdup(buf); + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; + break; + case ID_RFC822_ADDR: + snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr); + this->string = strdup(buf); + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_wc_string; + break; + case ID_DER_ASN1_DN: + snprintf(buf, sizeof(buf), "%.*s", encoded.len, encoded.ptr); + /* TODO: whats returned on failure */ + dntoa(encoded, &buf_chunk); + this->string = strdup(buf); + this->public.equals = (bool (*) (identification_t*,identification_t*))equals_dn; + this->public.belongs_to = (bool (*) (identification_t*,identification_t*))belongs_to_dn; + break; + case ID_DER_ASN1_GN: + this->string = gntoa(encoded); + break; + case ID_KEY_ID: + this->string = strdup("(unparsed KEY_ID)"); + break; + default: + snprintf(buf, sizeof(buf), "(invalid ID type: %d)", type); + this->string = strdup(buf); + break; + } + + /* apply encoded chunk */ + if (type != ID_ANY) + { + this->encoded = chunk_clone(encoded); + } + + /* remove unprintable chars in string */ + for (pos = this->string; *pos != '\0'; pos++) + { + if (!isprint(*pos)) + { + *pos = '?'; + } + } + return &(this->public); +} diff --git a/src/charon/lib/utils/identification.h b/src/charon/lib/utils/identification.h new file mode 100644 index 000000000..309b6858c --- /dev/null +++ b/src/charon/lib/utils/identification.h @@ -0,0 +1,245 @@ +/** + * @file identification.h + * + * @brief Interface of identification_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 IDENTIFICATION_H_ +#define IDENTIFICATION_H_ + + +#include "types.h" + +typedef enum id_type_t id_type_t; + +/** + * @brief ID Types in a ID payload. + * + * @ingroup utils + */ +enum id_type_t { + + /** + * 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 + * a ID_RFC822_ADDR is, "jsmith@example.com". The string MUST + * not contain any terminators. + */ + ID_RFC822_ADDR = 3, + + /** + * ID data is a single sixteen (16) octet IPv6 address. + */ + ID_IPV6_ADDR = 5, + + /** + * ID data is the binary DER encoding of an ASN.1 X.500 Distinguished Name + * [X.501]. + */ + ID_DER_ASN1_DN = 9, + + /** + * ID data is the binary DER encoding of an ASN.1 X.500 GeneralName + * [X.509]. + */ + 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, + + /** + * Special type of PRIVATE USE which matches to any other id. + */ + ID_ANY = 201, +}; + +/** + * String mappings for id_type_t. + */ +extern mapping_t id_type_m[]; + +typedef struct identification_t identification_t; + +/** + * @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 + * + * @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 Get a string representation of this id. + * + * @warning Result points to internal data, do NOT free! + * + * @param this the identification_t object + * @return string + */ + char *(*get_string) (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 belongs to 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 + * @return TRUE if other belongs to this + */ + bool (*belongs_to) (identification_t *this, identification_t *other); + + /** + * @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 sring representation fails. + * + * @ingroup utils + */ +identification_t * identification_create_from_encoding(id_type_t type, chunk_t encoded); + + +#endif /* IDENTIFICATION_H_ */ diff --git a/src/charon/lib/utils/iterator.h b/src/charon/lib/utils/iterator.h new file mode 100644 index 000000000..de81db8e9 --- /dev/null +++ b/src/charon/lib/utils/iterator.h @@ -0,0 +1,153 @@ +/** + * @file iterator.h + * + * @brief Interface iterator_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 ITERATOR_H_ +#define ITERATOR_H_ + +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 Iterate over all items. + * + * The easy way to iterate over items. + * + * @param this calling object + * @param[out] value item + * @return + * - TRUE, if more elements are avaiable, + * - FALSE otherwise + */ + bool (*iterate) (iterator_t *this, void** value); + + /** + * @brief Moves to the next element, if available. + * + * A newly created iterator_t object doesn't point to any item. + * Call iterator_t.has_next first to point it to the first item. + * + * @param this calling object + * @return + * - TRUE, if more elements are avaiable, + * - FALSE otherwise + */ + bool (*has_next) (iterator_t *this); + + /** + * @brief Returns the current value at the iterator position. + * + * @param this calling object + * @param[out] value value is set to the current value at iterator position + * @return + * - SUCCESS + * - FAILED if iterator on an invalid position + */ + status_t (*current) (iterator_t *this, void **value); + + /** + * @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 position of the iterator is set in the following order: + * - to the item before, if available + * - otherwise to the item after, if available + * - otherwise it gets reseted + * + * @param linked_list calling object + * @return + * - SUCCESS + * - FAILED if iterator is on an invalid position + */ + status_t (*remove) (iterator_t *iterator); + + /** + * @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/charon/lib/utils/leak_detective.c b/src/charon/lib/utils/leak_detective.c new file mode 100644 index 000000000..780ba4c05 --- /dev/null +++ b/src/charon/lib/utils/leak_detective.c @@ -0,0 +1,540 @@ +/** + * @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 "leak_detective.h" + +#include +#include + +#ifdef LEAK_DETECTIVE + +/** + * Magic value which helps to detect memory corruption + */ +#define MEMORY_HEADER_MAGIC 0xF1367ADF + +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 void load_excluded_functions(); + +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 +}; + +/** + * logger for the leak detective + */ +static logger_t *logger; + +/** + * 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!!! + */ +static void log_stack_frames(void **stack_frames, int stack_frame_count) +{ + char **strings; + size_t i; + + strings = backtrace_symbols (stack_frames, stack_frame_count); + + logger->log(logger, ERROR, " dumping %d stack frame addresses.", stack_frame_count); + + for (i = 0; i < stack_frame_count; i++) + { + logger->log(logger, ERROR, " %s", strings[i]); + } + free (strings); +} + +/** + * Report leaks at library destruction + */ +void report_leaks() +{ + memory_header_t *hdr; + int leaks = 0; + + /* reaquire a logger is necessary, this will force ((destructor)) + * order to work correctly */ + logger = logger_manager->get_logger(logger_manager, LEAK_DETECT); + for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) + { + logger->log(logger, ERROR, "Leak (%d bytes at %p)", hdr->bytes, hdr + 1); + log_stack_frames(hdr->stack_frames, hdr->stack_frame_count); + leaks++; + } + + switch (leaks) + { + case 0: + logger->log(logger, CONTROL, "No leaks detected"); + break; + case 1: + logger->log(logger, ERROR, "One leak detected"); + break; + default: + logger->log(logger, ERROR, "%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); + uninstall_hooks(); + hdr = malloc(bytes + sizeof(memory_header_t)); + + hdr->magic = MEMORY_HEADER_MAGIC; + hdr->bytes = bytes; + hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + + /* 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; + install_hooks(); + 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); + if (hdr->magic != MEMORY_HEADER_MAGIC) + { + pthread_mutex_unlock(&mutex); + /* TODO: since pthread_join cannot be excluded cleanly, we are not whining about bad frees */ + return; + logger->log(logger, ERROR, "freeing of invalid memory (%p)", ptr); + stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); + log_stack_frames(stack_frames, stack_frame_count); + return; + } + /* remove magic from hdr */ + hdr->magic = 0; + + /* remove item from list */ + if (hdr->next) + { + hdr->next->previous = hdr->previous; + } + hdr->previous->next = hdr->next; + + uninstall_hooks(); + free(hdr); + install_hooks(); + pthread_mutex_unlock(&mutex); +} + +/** + * Hook function for realloc() + */ +void *realloc_hook(void *old, size_t bytes, const void *caller) +{ + void *new; + memory_header_t *hdr = old - sizeof(memory_header_t); + void *stack_frames[STACK_FRAMES_COUNT]; + int stack_frame_count; + + /* allow reallocation of NULL */ + if (old == NULL) + { + return malloc_hook(bytes, caller); + } + if (hdr->magic != MEMORY_HEADER_MAGIC) + { + logger->log(logger, ERROR, "reallocation of invalid memory (%p)", old); + stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); + log_stack_frames(stack_frames, stack_frame_count); + kill(getpid(), SIGKILL); + return NULL; + } + + /* malloc and free is done with hooks */ + new = malloc_hook(bytes, caller); + memcpy(new, old, min(bytes, hdr->bytes)); + free_hook(old, caller); + + return new; +} + + +/** + * Setup leak detective + */ +void leak_detective_init() +{ + logger = logger_manager->get_logger(logger_manager, LEAK_DETECT); + load_excluded_functions(); + install_hooks(); +} + +/** + * Clean up leak detective + */ +void leak_detective_cleanup() +{ + uninstall_hooks(); + report_leaks(); +} + + +/** + * The following glibc functions are excluded from leak detection, since + * they use static allocated buffers or other ugly allocation hacks. + * For this to work, the linker must link libstrongswan preferred to + * the other (overriden) libs. + */ +struct excluded_function { + char *lib_name; + char *function_name; + void *handle; + void *lib_function; +} excluded_functions[] = { + {"libc.so.6", "inet_ntoa", NULL, NULL}, + {"libpthread.so.0", "pthread_create", NULL, NULL}, + {"libpthread.so.0", "pthread_cancel", NULL, NULL}, + {"libpthread.so.0", "pthread_join", NULL, NULL}, + {"libpthread.so.0", "_pthread_cleanup_push",NULL, NULL}, + {"libpthread.so.0", "_pthread_cleanup_pop", NULL, NULL}, + {"libc.so.6", "mktime", NULL, NULL}, + {"libc.so.6", "vsyslog", NULL, NULL}, + {"libc.so.6", "strerror", NULL, NULL}, +}; +#define INET_NTOA 0 +#define PTHREAD_CREATE 1 +#define PTHREAD_CANCEL 2 +#define PTHREAD_JOIN 3 +#define PTHREAD_CLEANUP_PUSH 4 +#define PTHREAD_CLEANUP_POP 5 +#define MKTIME 6 +#define VSYSLOG 7 +#define STRERROR 8 + + +/** + * Load libraries and function pointers for excluded functions + */ +static void load_excluded_functions() +{ + int i; + + for (i = 0; i < sizeof(excluded_functions)/sizeof(struct excluded_function); i++) + { + void *handle, *function; + handle = dlopen(excluded_functions[i].lib_name, RTLD_LAZY); + if (handle == NULL) + { + kill(getpid(), SIGSEGV); + } + + function = dlsym(handle, excluded_functions[i].function_name); + + if (function == NULL) + { + dlclose(handle); + kill(getpid(), SIGSEGV); + } + excluded_functions[i].handle = handle; + excluded_functions[i].lib_function = function; + } +} + +char *inet_ntoa(struct in_addr in) +{ + char *(*_inet_ntoa)(struct in_addr) = excluded_functions[INET_NTOA].lib_function; + char *result; + + pthread_mutex_lock(&mutex); + uninstall_hooks(); + + result = _inet_ntoa(in); + + install_hooks(); + pthread_mutex_unlock(&mutex); + return result; +} + +int pthread_create(pthread_t *__restrict __threadp, __const pthread_attr_t *__restrict __attr, + void *(*__start_routine) (void *), void *__restrict __arg) +{ + int (*_pthread_create) (pthread_t *__restrict __threadp, + __const pthread_attr_t *__restrict __attr, + void *(*__start_routine) (void *), + void *__restrict __arg) = excluded_functions[PTHREAD_CREATE].lib_function; + int result; + + pthread_mutex_lock(&mutex); + uninstall_hooks(); + + result = _pthread_create(__threadp, __attr, __start_routine, __arg); + + install_hooks(); + pthread_mutex_unlock(&mutex); + return result; +} + + +int pthread_cancel(pthread_t __th) +{ + int (*_pthread_cancel) (pthread_t) = excluded_functions[PTHREAD_CANCEL].lib_function; + int result; + + pthread_mutex_lock(&mutex); + uninstall_hooks(); + + result = _pthread_cancel(__th); + + install_hooks(); + pthread_mutex_unlock(&mutex); + return result; +} + +// /* TODO: join has probs, since it dellocates memory +// * allocated (somewhere) with leak_detective :-(. +// * We should exclude all pthread_ functions to fix it !? */ +// int pthread_join(pthread_t __th, void **__thread_return) +// { +// int (*_pthread_join) (pthread_t, void **) = excluded_functions[PTHREAD_JOIN].lib_function; +// int result; +// +// pthread_mutex_lock(&mutex); +// uninstall_hooks(); +// +// result = _pthread_join(__th, __thread_return); +// +// install_hooks(); +// pthread_mutex_unlock(&mutex); +// return result; +// } +// +// void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer, +// void (*__routine) (void *), +// void *__arg) +// { +// int (*__pthread_cleanup_push) (struct _pthread_cleanup_buffer *__buffer, +// void (*__routine) (void *), +// void *__arg) = +// excluded_functions[PTHREAD_CLEANUP_PUSH].lib_function; +// +// pthread_mutex_lock(&mutex); +// uninstall_hooks(); +// +// __pthread_cleanup_push(__buffer, __routine, __arg); +// +// install_hooks(); +// pthread_mutex_unlock(&mutex); +// return; +// } +// +// void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, int __execute) +// { +// int (*__pthread_cleanup_pop) (struct _pthread_cleanup_buffer *__buffer, int __execute) = +// excluded_functions[PTHREAD_CLEANUP_POP].lib_function; +// +// pthread_mutex_lock(&mutex); +// uninstall_hooks(); +// +// __pthread_cleanup_pop(__buffer, __execute); +// +// install_hooks(); +// pthread_mutex_unlock(&mutex); +// return; +// } + +time_t mktime(struct tm *tm) +{ + time_t (*_mktime)(struct tm *tm) = excluded_functions[MKTIME].lib_function; + time_t result; + + pthread_mutex_lock(&mutex); + uninstall_hooks(); + + result = _mktime(tm); + + install_hooks(); + pthread_mutex_unlock(&mutex); + return result; +} + +void vsyslog (int __pri, __const char *__fmt, __gnuc_va_list __ap) +{ + void (*_vsyslog) (int __pri, __const char *__fmt, __gnuc_va_list __ap) = excluded_functions[VSYSLOG].lib_function; + + pthread_mutex_lock(&mutex); + uninstall_hooks(); + + _vsyslog(__pri, __fmt, __ap); + + install_hooks(); + pthread_mutex_unlock(&mutex); + return; +} + + + +char *strerror(int errnum) +{ + char* (*_strerror) (int) = excluded_functions[STRERROR].lib_function; + char *result; + + pthread_mutex_lock(&mutex); + uninstall_hooks(); + + result = _strerror(errnum); + + install_hooks(); + pthread_mutex_unlock(&mutex); + return result; +} + +#endif /* LEAK_DETECTION */ diff --git a/src/charon/lib/utils/leak_detective.h b/src/charon/lib/utils/leak_detective.h new file mode 100644 index 000000000..13c0d01ab --- /dev/null +++ b/src/charon/lib/utils/leak_detective.h @@ -0,0 +1,50 @@ +/** + * @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_ + + +#ifdef LEAK_DETECTIVE + +/** + * Max number of stack frames to include in a backtrace. + */ +#define STACK_FRAMES_COUNT 30 + +/** + * Initialize leak detective, activates it + */ +void leak_detective_init(); + +/** + * Cleanup leak detective, deactivates it + */ +void leak_detective_cleanup(); + +#else /* !LEAK_DETECTIVE */ + +#define leak_detective_init() {} +#define leak_detective_cleanup() {} + +#endif /* LEAK_DETECTIVE */ + +#endif /* LEAK_DETECTIVE_H_ */ diff --git a/src/charon/lib/utils/linked_list.c b/src/charon/lib/utils/linked_list.c new file mode 100644 index 000000000..64443434b --- /dev/null +++ b/src/charon/lib/utils/linked_list.c @@ -0,0 +1,727 @@ +/** + * @file linked_list.c + * + * @brief Implementation of linked_list_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "linked_list.h" + + + +typedef struct linked_list_element_t linked_list_element_t; + +/** + * @brief Element in a linked list. + * + * This element holds a pointer to the value it represents. + */ +struct linked_list_element_t { + + /** + * Value of a list item. + */ + void *value; + + /** + * Previous list element. + * + * NULL if first element in list. + */ + linked_list_element_t *previous; + + /** + * Next list element. + * + * NULL if last element in list. + */ + linked_list_element_t *next; + + /** + * Destroys a linked_list_element object. + * + * @param linked_list_element_t calling object + */ + void (*destroy) (linked_list_element_t *this); +}; + +/** + * Implementation of linked_list_element_t.destroy. + */ +static void linked_list_element_destroy(linked_list_element_t *this) +{ + free(this); +} + +/** + * @brief Creates an empty linked list object. + * + * @warning Only the pointer to the value is stored. + * + * @param[in] value value of item to be set + * @return linked_list_element_t object + */ + +linked_list_element_t *linked_list_element_create(void *value) +{ + linked_list_element_t *this = malloc_thing(linked_list_element_t); + + this->destroy = linked_list_element_destroy; + + 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. + */ + linked_list_element_t *first; + + /** + * Last element in list. + * NULL if no elements in list. + */ + linked_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. + */ + linked_list_element_t *current; + + /** + * Direction of iterator. + */ + bool forward; +}; + +/** + * Implementation of iterator_t.has_next. + */ +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; + *value = this->current->value; + return TRUE; + } + if (this->forward) + { + if (this->current->next == NULL) + { + return FALSE; + } + this->current = this->current->next; + *value = this->current->value; + return TRUE; + } + /* backward */ + if (this->current->previous == NULL) + { + return FALSE; + } + this->current = this->current->previous; + *value = this->current->value; + return TRUE; +} + +/** + * Implementation of iterator_t.has_next. + */ +static bool iterator_has_next(private_iterator_t *this) +{ + if (this->list->count == 0) + { + return FALSE; + } + if (this->current == NULL) + { + this->current = (this->forward) ? this->list->first : this->list->last; + return TRUE; + } + if (this->forward) + { + if (this->current->next == NULL) + { + return FALSE; + } + this->current = this->current->next; + return TRUE; + } + /* backward */ + if (this->current->previous == NULL) + { + return FALSE; + } + this->current = this->current->previous; + return TRUE; +} + +/** + * Implementation of iterator_t.current. + */ +static status_t iterator_current(private_iterator_t *this, void **value) +{ + if (this->current == NULL) + { + return NOT_FOUND; + } + *value = this->current->value; + return SUCCESS; +} + +/** + * 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) +{ + linked_list_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 */ + if (this->current->previous != NULL) + { + new_current = this->current->previous; + } + else if (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--; + this->current->destroy(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); + } + + linked_list_element_t *element =(linked_list_element_t *) linked_list_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; + } + + linked_list_element_t *element =(linked_list_element_t *) linked_list_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) +{ + 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.call_on_items. + */ +static void call_on_items(private_linked_list_t *this, void(*func)(void*)) +{ + iterator_t *iterator; + void *item; + + iterator = this->public.create_iterator(&(this->public),TRUE); + + while (iterator->has_next(iterator)) + { + iterator->current(iterator, &item); + (*func)(item); + } + iterator->destroy(iterator); +} + +/** + * Implementation of linked_list_t.insert_first. + */ +static void insert_first(private_linked_list_t *this, void *item) +{ + linked_list_element_t *element; + + element =(linked_list_element_t *) linked_list_element_create(item); + + if (this->count == 0) + { + /* first entry in list */ + this->first = element; + this->last = element; + element->previous = NULL; + element->next = NULL; + } + else + { + linked_list_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) +{ + if (this->count == 0) + { + return NOT_FOUND; + } + + linked_list_element_t *element = this->first; + + if (element->next != NULL) + { + element->next->previous = NULL; + } + this->first = element->next; + + if (item != NULL) + { + *item = element->value; + } + + this->count--; + + element->destroy(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) +{ + linked_list_element_t *element = (linked_list_element_t *) linked_list_element_create(item); + + if (this->count == 0) + { + /* first entry in list */ + this->first = element; + this->last = element; + element->previous = NULL; + element->next = NULL; + } + else + { + + linked_list_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) +{ + if (this->count == 0) + { + return NOT_FOUND; + } + + linked_list_element_t *element = this->last; + + if (element->previous != NULL) + { + element->previous->next = NULL; + } + this->last = element->previous; + + if (item != NULL) + { + *item = element->value; + } + + this->count--; + + element->destroy(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) +{ + linked_list_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; + } + + linked_list_element_t *element =(linked_list_element_t *) linked_list_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->has_next(iterator); + for (i = 0; i < position;i++) + { + iterator->has_next(iterator); + } + iterator->current(iterator,item); + 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; + status_t status; + if (this->count <= position) + { + return INVALID_ARG; + } + + iterator = this->public.create_iterator(&(this->public),TRUE); + + iterator->has_next(iterator); + for (i = 0; i < position;i++) + { + iterator->has_next(iterator); + } + status = iterator->current(iterator,item); + iterator->destroy(iterator); + return status; +} + +/** + * 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.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.iterate = (bool (*) (iterator_t *this, void **value)) iterate; + this->public.has_next = (bool (*) (iterator_t *this)) iterator_has_next; + this->public.current = (status_t (*) (iterator_t *this, void **value)) iterator_current; + this->public.insert_before = (void (*) (iterator_t *this, void *item)) insert_before; + this->public.insert_after = (void (*) (iterator_t *this, void *item)) insert_after; + this->public.replace = (status_t (*) (iterator_t *, void **, void *)) replace; + this->public.remove = (status_t (*) (iterator_t *this)) remove; + this->public.reset = (void (*) (iterator_t *this)) iterator_reset; + this->public.destroy = (void (*) (iterator_t *this)) iterator_destroy; + + this->forward = forward; + this->current = NULL; + this->list = linked_list; + + return &(this->public); +} + +/** + * Implementation of linked_list_t.destroy. + */ +static void linked_list_destroy(private_linked_list_t *this) +{ + void * value; + /* Remove all list items before destroying list */ + while (this->public.remove_first(&(this->public),&value) != NOT_FOUND) + { + /* values are not destroyed so memory leaks are possible + * if list is not empty when deleting */ + } + free(this); +} + +/* + * 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.call_on_items = (void (*) (linked_list_t *, void(*func)(void*)))call_on_items; + 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.destroy = (void (*) (linked_list_t *)) linked_list_destroy; + + this->count = 0; + this->first = NULL; + this->last = NULL; + + return (&(this->public)); +} diff --git a/src/charon/lib/utils/linked_list.h b/src/charon/lib/utils/linked_list.h new file mode 100644 index 000000000..8647f064d --- /dev/null +++ b/src/charon/lib/utils/linked_list.h @@ -0,0 +1,203 @@ +/** + * @file linked_list.h + * + * @brief Interface of linked_list_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 LINKED_LIST_H_ +#define LINKED_LIST_H_ + +#include +#include + + +typedef struct linked_list_t linked_list_t; + +/** + * @brief Class implementing a double linked list (named only as linked list). + * + * @warning Access to an object of this type is not thread-save. + * + * @b Costructors: + * - linked_list_create() + * + * @see + * - job_queue_t + * - event_queue_t + * - send_queue_t + * + * @ingroup utils + */ +struct linked_list_t { + + /** + * @brief Gets the count of items in the list. + * + * @param linked_list calling object + * @return number of items in list + */ + int (*get_count) (linked_list_t *linked_list); + + /** + * @brief Creates a iterator for the given list. + * + * @warning Created iterator_t object has to get destroyed by the caller. + * + * @param linked_list calling object + * @param forward iterator direction (TRUE: front to end) + * @return new iterator_t object + */ + iterator_t * (*create_iterator) (linked_list_t *linked_list, bool forward); + + /** + * @brief Call a function with list element as argument. + * + * This method accepts a function, which will be called for + * each list element once. The function must accept the list + * element as the first argument. Handy for destruction of + * list elements. + * + * @todo Additional vararg which are passed to the + * function would be nice... + * + * @param linked_list calling object + * @param func function to call + */ + void (*call_on_items) (linked_list_t *linked_list, void(*func)(void*)); + + /** + * @brief Inserts a new item at the beginning of the list. + * + * @param linked_list calling object + * @param[in] item item value to insert in list + */ + void (*insert_first) (linked_list_t *linked_list, void *item); + + /** + * @brief Removes the first item in the list and returns its value. + * + * @param linked_list 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 *linked_list, void **item); + + /** + * @brief Returns the value of the first list item without removing it. + * + * @param linked_list calling object + * @param[out] item item returned value of first item + * @return + * - SUCCESS + * - NOT_FOUND, if list is empty + */ + status_t (*get_first) (linked_list_t *linked_list, void **item); + + /** + * @brief Inserts a new item at the end of the list. + * + * @param linked_list calling object + * @param[in] item item value to insert into list + */ + void (*insert_last) (linked_list_t *linked_list, void *item); + + /** + * @brief Inserts a new item at a given position in the list. + * + * @param linked_list calling object + * @param position position starting at 0 to insert new entry + * @param[in] item item value to insert into list + * @return + * - SUCCESS + * - INVALID_ARG if position not existing + */ + status_t (*insert_at_position) (linked_list_t *linked_list,size_t position, void *item); + + /** + * @brief Removes an item from a given position in the list. + * + * @param linked_list 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 *linked_list,size_t position, void **item); + + /** + * @brief Get an item from a given position in the list. + * + * @param linked_list 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 *linked_list,size_t position, void **item); + + /** + * @brief Removes the last item in the list and returns its value. + * + * @param linked_list 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 *linked_list, void **item); + + /** + * @brief Returns the value of the last list item without removing it. + * + * @param linked_list 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 *linked_list, void **item); + + /** + * @brief Destroys a linked_list object. + * + * @warning All items are removed before deleting the list. The + * associated values are NOT destroyed. + * Destroying an list which is not empty may cause + * memory leaks! + * + * @param linked_list calling object + */ + void (*destroy) (linked_list_t *linked_list); +}; + +/** + * @brief Creates an empty linked list object. + * + * @return linked_list_t object. + * + * @ingroup utils + */ +linked_list_t *linked_list_create(); + + +#endif /*LINKED_LIST_H_*/ diff --git a/src/charon/lib/utils/logger.c b/src/charon/lib/utils/logger.c new file mode 100644 index 000000000..fdaeddff0 --- /dev/null +++ b/src/charon/lib/utils/logger.c @@ -0,0 +1,342 @@ +/** + * @file logger.c + * + * @brief Implementation of logger_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "logger.h" + + +/** + * Maximum length of a log entry (only used for logger_s.log). + */ +#define MAX_LOG 8192 + +/** + * Maximum number of logged bytes per line + */ +#define MAX_BYTES 16 + +typedef struct private_logger_t private_logger_t; + +/** + * @brief Private data of a logger_t object. + */ +struct private_logger_t { + /** + * Public data. + */ + logger_t public; + /** + * Detail-level of logger. + */ + log_level_t level; + /** + * Name of logger. + */ + char *name; + /** + * File to write log output to. + * NULL for syslog. + */ + FILE *output; + + /** + * Should a thread_id be included in the log? + */ + bool log_thread_id; +}; + +/** + * prepend the logging prefix to string and store it in buffer + */ +static void prepend_prefix(private_logger_t *this, log_level_t loglevel, const char *string, char *buffer) +{ + char log_type, log_details; + char thread_id[10] = ""; + + if (loglevel & CONTROL) + { + log_type = 'C'; + } + else if (loglevel & ERROR) + { + log_type = 'E'; + } + else if (loglevel & RAW) + { + log_type = 'R'; + } + else if (loglevel & PRIVATE) + { + log_type = 'P'; + } + else if (loglevel & AUDIT) + { + log_type = 'A'; + } + else + { + log_type = '-'; + } + + if (loglevel & (LEVEL3 - LEVEL2)) + { + log_details = '3'; + } + else if (loglevel & (LEVEL2 - LEVEL1)) + { + log_details = '2'; + } + else if (loglevel & LEVEL1) + { + log_details = '1'; + } + else + { + log_details = '0'; + } + + if (this->log_thread_id) + { + snprintf(thread_id, sizeof(thread_id), " @%d", (int)pthread_self()); + } + snprintf(buffer, MAX_LOG, "[%c%c:%s]%s %s", log_type, log_details, this->name, thread_id, string); +} + +/** + * Convert a charon-loglevel to a syslog priority + */ +static int get_priority(log_level_t loglevel) +{ + if (loglevel & AUDIT) + { + return LOG_AUTHPRIV|LOG_INFO; + } + return LOG_DAEMON|LOG_DEBUG; +} + +/** + * Implementation of logger_t.log. + * + * Yes, logg is written wrong :-). + */ +static void logg(private_logger_t *this, log_level_t loglevel, const char *format, ...) +{ + if ((this->level & loglevel) == loglevel) + { + char buffer[MAX_LOG]; + va_list args; + + + if (this->output == NULL) + { + /* syslog */ + prepend_prefix(this, loglevel, format, buffer); + va_start(args, format); + vsyslog(get_priority(loglevel), buffer, args); + va_end(args); + } + else + { + /* File output */ + prepend_prefix(this, loglevel, format, buffer); + va_start(args, format); + vfprintf(this->output, buffer, args); + va_end(args); + fprintf(this->output, "\n"); + } + + } +} + +/** + * Implementation of logger_t.log_bytes. + */ +static void log_bytes(private_logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len) +{ + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + if ((this->level & loglevel) == loglevel) + { + char thread_id[10] = ""; + char buffer[MAX_LOG]; + char ascii_buffer[MAX_BYTES+1]; + + char *buffer_pos = buffer; + const char format[] = "%s %d bytes @ %p"; + const char *bytes_pos = bytes; + const char *bytes_roof = bytes + len; + + int line_start = 0; + int i = 0; + + if (this->log_thread_id) + { + snprintf(thread_id, sizeof(thread_id), " @%d", (int)pthread_self()); + } + + /* since me can't do multi-line output to syslog, + * we must do multiple syslogs. To avoid + * problems in output order, lock this by a mutex. + */ + pthread_mutex_lock(&mutex); + + prepend_prefix(this, loglevel, format, buffer); + + if (this->output == NULL) + { + syslog(get_priority(loglevel), buffer, label, len, bytes); + } + else + { + fprintf(this->output, buffer, label, len, bytes); + fprintf(this->output, "\n"); + } + + 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 == MAX_BYTES) + { + int padding = 3 * (MAX_BYTES - i); + + while (padding--) + { + *buffer_pos++ = ' '; + } + *buffer_pos++ = '\0'; + ascii_buffer[i] = '\0'; + + if (this->output == NULL) + { + syslog(get_priority(loglevel), "[ :%5d]%s %s %s", line_start, thread_id, buffer, ascii_buffer); + } + else + { + fprintf(this->output, "[ :%5d]%s %s %s\n", line_start, thread_id, buffer, ascii_buffer); + } + buffer_pos = buffer; + line_start += MAX_BYTES; + i = 0; + } + else + { + *buffer_pos++ = ' '; + } + } + pthread_mutex_unlock(&mutex); + } +} + +/** + * Implementation of logger_t.log_chunk. + */ +static void log_chunk(logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk) +{ + this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len); +} + +/** + * Implementation of logger_t.enable_level. + */ +static void enable_level(private_logger_t *this, log_level_t log_level) +{ + this->level |= log_level; +} + +/** + * Implementation of logger_t.disable_level. + */ +static void disable_level(private_logger_t *this, log_level_t log_level) +{ + this->level &= ~log_level; +} + +/** + * Implementation of logger_t.set_output. + */ +static void set_output(private_logger_t *this, FILE * output) +{ + this->output = output; +} + +/** + * Implementation of logger_t.get_level. + */ +static log_level_t get_level(private_logger_t *this) +{ + return this->level; +} + +/** + * Implementation of logger_t.destroy. + */ +static void destroy(private_logger_t *this) +{ + free(this->name); + free(this); +} + +/* + * Described in header. + */ +logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output) +{ + private_logger_t *this = malloc_thing(private_logger_t); + + /* public functions */ + this->public.log = (void(*)(logger_t*,log_level_t,const char*,...))logg; + this->public.log_bytes = (void(*)(logger_t*, log_level_t, const char*, const char*,size_t))log_bytes; + this->public.log_chunk = log_chunk; + this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level; + this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level; + this->public.get_level = (log_level_t(*)(logger_t*))get_level; + this->public.set_output = (void(*)(logger_t*,FILE*))set_output; + this->public.destroy = (void(*)(logger_t*))destroy; + + if (logger_name == NULL) + { + logger_name = ""; + } + + /* private variables */ + this->level = log_level; + this->log_thread_id = log_thread_id; + this->name = malloc(strlen(logger_name) + 1); + + strcpy(this->name,logger_name); + this->output = output; + + return (logger_t*)this; +} diff --git a/src/charon/lib/utils/logger.h b/src/charon/lib/utils/logger.h new file mode 100644 index 000000000..dec73078e --- /dev/null +++ b/src/charon/lib/utils/logger.h @@ -0,0 +1,198 @@ +/** + * @file logger.h + * + * @brief Interface of logger_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 LOGGER_H_ +#define LOGGER_H_ + +#include + +#include + +typedef enum log_level_t log_level_t; + +/** + * @brief Log Levels supported by the logger object. + * + * Logleves are devided in two different kinds: + * - levels to specify the type of the log + * - levels to specify the detail-level of the log + * + * Use combinations of these to build detailed loglevels, such + * as CONTROL|LEVEL2 fore a detailed cotrol level, or + * use RAW to see all raw data dumps (except private). + * + * @ingroup utils + */ +enum log_level_t { + /** + * Control flow. + */ + CONTROL = 1, + /** + * Error reporting. + */ + ERROR = 2, + /** + * Logs important for the sysadmin. + */ + AUDIT = 4, + /** + * Raw data dumps. + */ + RAW = 8, + /** + * Private data dumps. + */ + PRIVATE = 16, + + /** + * Log most important output, can be omitted. + */ + LEVEL0 = 0, + /** + * Log more detailed output. + */ + LEVEL1 = 32, + /** + * Log even more detailed output. + */ + LEVEL2 = LEVEL1 + 64, + /** + * Use maximum detailed output. + */ + LEVEL3 = LEVEL2 + 128, + + /** + * Summary for all types with all detail-levels. + */ + FULL = LEVEL3 + CONTROL + ERROR + RAW + PRIVATE + AUDIT +}; + +typedef struct logger_t logger_t; + +/** + * @brief Class to simplify logging. + * + * @b Constructors: + * - logger_create() + * + * @ingroup utils + */ +struct logger_t { + + /** + * @brief Log an entry, using printf()-like params. + * + * All specified loglevels must be activated that + * the log is done. + * + * @param this logger_t object + * @param loglevel or'ed set of log_level_t's + * @param format printf like format string + * @param ... printf like parameters + */ + void (*log) (logger_t *this, log_level_t log_level, const char *format, ...); + + /** + * @brief Log some bytes, useful for debugging. + * + * All specified loglevels must be activated that + * the log is done. + * + * @param this logger_t object + * @param loglevel or'ed set of log_level_t's + * @param label a labeling name, logged with the bytes + * @param bytes pointer to the bytes to dump + * @param len number of bytes to dump + */ + void (*log_bytes) (logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len); + + /** + * @brief Log a chunk, useful for debugging. + * + * All specified loglevels must be activated that + * the log is done. + * + * @param this logger_t object + * @param loglevel or'ed set of log_level_t's + * @param label a labeling name, logged with the bytes + * @param chunk chunk to log + */ + void (*log_chunk) (logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk); + + /** + * @brief Enables a loglevel for the current logger_t object. + * + * @param this logger_t object + * @param log_level loglevel to enable + */ + void (*enable_level) (logger_t *this, log_level_t log_level); + + /** + * @brief Disables a loglevel for the current logger_t object. + * + * @param this logger_t object + * @param log_level loglevel to enable + */ + void (*disable_level) (logger_t *this, log_level_t log_level); + + /** + * @brief Set the output of the logger. + * + * Use NULL for syslog. + * + * @param this logger_t object + * @param output file, where log output should be written + */ + void (*set_output) (logger_t *this, FILE *output); + + /** + * @brief Get the currently used loglevel. + * + * @param this logger_t object + * @return currently used loglevel + */ + log_level_t (*get_level) (logger_t *this); + + /** + * @brief Destroys a logger_t object. + * + * @param this logger_t object + */ + void (*destroy) (logger_t *this); +}; + +/** + * @brief Constructor to create a logger_t object. + * + * @param logger_name name for the logger_t object + * @param log_level or'ed set of log_levels to assign to the new logger_t object + * @param log_thread_id TRUE if thread id should also be logged + * @param output FILE * if log has to go on a file output, NULL for syslog + * @return logger_t object + * + * @ingroup utils + */ +logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output); + + +#endif /*LOGGER_H_*/ diff --git a/src/charon/lib/utils/logger_manager.c b/src/charon/lib/utils/logger_manager.c new file mode 100644 index 000000000..ecbe1a6c1 --- /dev/null +++ b/src/charon/lib/utils/logger_manager.c @@ -0,0 +1,220 @@ +/** + * @file logger_manager.c + * + * @brief Implementation of logger_manager_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "logger_manager.h" + +#include +#include +#include + +/** + * String mappings for logger_context_t + */ +mapping_t logger_context_t_mappings[] = { + {PARSER, "PARSER"}, + {GENERATOR, "GENERATOR"}, + {IKE_SA, "IKE_SA"}, + {IKE_SA_MANAGER, "IKE_SA_MANAGER"}, + {CHILD_SA, "CHILD_SA"}, + {MESSAGE, "MESSAGE"}, + {THREAD_POOL, "THREAD_POOL"}, + {WORKER, "WORKER"}, + {SCHEDULER, "SCHEDULER"}, + {SENDER, "SENDER"}, + {RECEIVER, "RECEIVER"}, + {SOCKET, "SOCKET"}, + {TESTER, "TESTER"}, + {DAEMON, "DAEMON"}, + {CONFIG, "CONFIG"}, + {ENCRYPTION_PAYLOAD, "ENCRYPTION_PAYLOAD"}, + {PAYLOAD, "PAYLOAD"}, + {DER_DECODER, "DER_DECODER"}, + {DER_ENCODER, "DER_ENCODER"}, + {ASN1, "ASN1"}, + {XFRM, "XFRM"}, + {LEAK_DETECT, "LEAK_DETECT"}, + {MAPPING_END, NULL}, +}; + +struct { + char *name; + log_level_t level; + bool log_thread_ids; +} logger_defaults[] = { + { "PARSR", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* PARSER */ + { "GNRAT", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* GENERATOR */ + { "IKESA", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* IKE_SA */ + { "SAMGR", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* IKE_SA_MANAGER */ + { "CHDSA", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* CHILD_SA */ + { "MESSG", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* MESSAGE */ + { "TPOOL", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* THREAD_POOL */ + { "WORKR", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* WORKER */ + { "SCHED", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* SCHEDULER */ + { "SENDR", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* SENDER */ + { "RECVR", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* RECEIVER */ + { "SOCKT", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* SOCKET */ + { "TESTR", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* TESTER */ + { "DAEMN", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* DAEMON */ + { "CONFG", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* CONFIG */ + { "ENCPL", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* ENCRYPTION_PAYLOAD */ + { "PAYLD", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* PAYLOAD */ + { "DERDC", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* DER_DECODER */ + { "DEREC", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* DER_ENCODER */ + { "ASN_1", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* ASN1 */ + { "XFRM ", ERROR|CONTROL|AUDIT|LEVEL0, TRUE }, /* XFRM */ + { "LEAKD", ERROR|CONTROL|AUDIT|LEVEL0, FALSE}, /* LEAK_DETECT */ +}; + + +typedef struct private_logger_manager_t private_logger_manager_t; + +/** + * Private data of logger_manager_t object. + */ +struct private_logger_manager_t { + /** + * Public data. + */ + logger_manager_t public; + + /** + * Array of loggers, one for each context + */ + logger_t *loggers[LOGGER_CONTEXT_ROOF]; +}; + +/** + * The one and only instance of the logger manager + */ +static private_logger_manager_t private_logger_manager; + +/** + * Exported pointer for the logger manager + */ +logger_manager_t *logger_manager = (logger_manager_t *)&private_logger_manager; + +/** + * Implementation of logger_manager_t.get_logger. + */ +static logger_t *get_logger(private_logger_manager_t *this, logger_context_t context) +{ + return this->loggers[context]; +} + +/** + * Implementation of logger_manager_t.get_log_level. + */ +static log_level_t get_log_level (private_logger_manager_t *this, logger_context_t context) +{ + return this->loggers[context]->get_level(this->loggers[context]); +} + +/** + * Implementation of private_logger_manager_t.enable_log_level. + */ +static void enable_log_level(private_logger_manager_t *this, logger_context_t context, log_level_t level) +{ + if (context == ALL_LOGGERS) + { + for (context = 0; context < LOGGER_CONTEXT_ROOF; context++) + { + this->loggers[context]->enable_level(this->loggers[context], level); + } + } + else + { + this->loggers[context]->enable_level(this->loggers[context], level); + } +} + +/** + * Implementation of private_logger_manager_t.disable_log_level. + */ +static void disable_log_level(private_logger_manager_t *this, logger_context_t context, log_level_t level) +{ + if (context == ALL_LOGGERS) + { + for (context = 0; context < LOGGER_CONTEXT_ROOF; context++) + { + this->loggers[context]->disable_level(this->loggers[context], level); + } + } + else + { + this->loggers[context]->disable_level(this->loggers[context], level); + } +} + +/** + * Implementation of private_logger_manager_t.set_output. + */ +static void set_output(private_logger_manager_t *this, logger_context_t context, FILE *output) +{ + if (context == ALL_LOGGERS) + { + for (context = 0; context < LOGGER_CONTEXT_ROOF; context++) + { + this->loggers[context]->set_output(this->loggers[context], output); + } + } + else + { + this->loggers[context]->set_output(this->loggers[context], output); + } +} + + +/** + * Creates the instance of the logger manager at library startup + */ +void logger_manager_init() +{ + int i; + + logger_manager->get_logger = (logger_t *(*)(logger_manager_t*,logger_context_t context))get_logger; + logger_manager->get_log_level = (log_level_t (*)(logger_manager_t *, logger_context_t)) get_log_level; + logger_manager->enable_log_level = (void (*)(logger_manager_t *, logger_context_t, log_level_t)) enable_log_level; + logger_manager->disable_log_level = (void (*)(logger_manager_t *, logger_context_t, log_level_t)) disable_log_level; + logger_manager->set_output = (void (*)(logger_manager_t *, logger_context_t, FILE*)) set_output; + + for (i = 0; i < LOGGER_CONTEXT_ROOF; i++) + { + private_logger_manager.loggers[i] = logger_create(logger_defaults[i].name, + logger_defaults[i].level, + logger_defaults[i].log_thread_ids, + INITIAL_LOG_OUTPUT); + } + +} + +/** + * Destroy the logger manager at library exit + */ +void logger_manager_cleanup() +{ + int i; + for (i = 0; i < LOGGER_CONTEXT_ROOF; i++) + { + private_logger_manager.loggers[i]->destroy(private_logger_manager.loggers[i]); + } +} diff --git a/src/charon/lib/utils/logger_manager.h b/src/charon/lib/utils/logger_manager.h new file mode 100644 index 000000000..a3ff5a37e --- /dev/null +++ b/src/charon/lib/utils/logger_manager.h @@ -0,0 +1,160 @@ +/** + * @file logger_manager.h + * + * @brief Interface of logger_manager_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 LOGGER_MANAGER_H_ +#define LOGGER_MANAGER_H_ + +#include + +#include + +#define INITIAL_LOG_OUTPUT stdout + +typedef enum logger_context_t logger_context_t; + +/** + * @brief Context of a specific logger. + * + * @ingroup utils + */ +enum logger_context_t { + ALL_LOGGERS = -1, + PARSER = 0, + GENERATOR, + IKE_SA, + IKE_SA_MANAGER, + CHILD_SA, + MESSAGE, + THREAD_POOL, + WORKER, + SCHEDULER, + SENDER, + RECEIVER, + SOCKET, + TESTER, + DAEMON, + CONFIG, + ENCRYPTION_PAYLOAD, + PAYLOAD, + DER_DECODER, + DER_ENCODER, + ASN1, + XFRM, + LEAK_DETECT, + LOGGER_CONTEXT_ROOF, +}; + + +typedef struct logger_manager_t logger_manager_t; + +/** + * @brief Class to manage logger_t objects. + * + * The logger manager manages all logger_t object in a list and + * allows their manipulation. Via a logger_context_t, the loglevel + * of a specific logging type can be adjusted at runtime. + * This class differs from others, as it has no constructor or destroy + * function. The one and only instance "logger_manager" is created at + * library start and destroyed at exit. + * + * @b Constructors: + * - none, logger_manager is the single instance + * use logger_manager_init/logger_manager_cleanup + * + * @see logger_t + * + * @ingroup utils + */ +struct logger_manager_t { + + /** + * @brief Gets a logger_t object for a specific logger context. + * + * @param this logger_manager_t object + * @param context logger_context to use the logger for + * @param name name for the new logger. Context name is already included + * and has not to be specified (so NULL is allowed) + * @return logger_t object + */ + logger_t *(*get_logger) (logger_manager_t *this, logger_context_t context); + + /** + * @brief Returns the set log_level of a specific context. + * + * @param this calling object + * @param context context to check level + * @return log_level for the given logger_context + */ + log_level_t (*get_log_level) (logger_manager_t *this, logger_context_t context); + + /** + * @brief Enables a logger level of a specific context. + * + * Use context ALL_LOGGERS to manipulate all loggers. + * + * @param this calling object + * @param context context to set level + * @param log_level logger level to eanble + */ + void (*enable_log_level) (logger_manager_t *this, logger_context_t context,log_level_t log_level); + + /** + * @brief Disables a logger level of a specific context. + * + * Use context ALL_LOGGERS to manipulate all loggers. + * + * @param this calling object + * @param context context to set level + * @param log_level logger level to disable + */ + void (*disable_log_level) (logger_manager_t *this, logger_context_t context,log_level_t log_level); + + /** + * @brief Sets the output of a logger. + * + * Use context ALL_LOGGERS to redirect all loggers. + * + * @param this calling object + * @param context context to set output + * @param log_level logger level to disable + */ + void (*set_output) (logger_manager_t *this, logger_context_t context, FILE *output); +}; + +/** + * The single and global instance of the logger_manager + */ +extern logger_manager_t *logger_manager; + +/** + * Initialize the logger manager with all its logger. + * Has to be called before logger_manager is accessed. + */ +void logger_manager_init(); + +/** + * Free any resources hold by the logger manager. Do + * not access logger_manager after this call. + */ +void logger_manager_cleanup(); + +#endif /*LOGGER_MANAGER_H_*/ diff --git a/src/charon/lib/utils/randomizer.c b/src/charon/lib/utils/randomizer.c new file mode 100644 index 000000000..09e81894e --- /dev/null +++ b/src/charon/lib/utils/randomizer.c @@ -0,0 +1,164 @@ +/** + * @file randomizer.c + * + * @brief Implementation of randomizer_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "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 ? PSEUDO_RANDOM_DEVICE : RANDOM_DEVICE; + + 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/charon/lib/utils/randomizer.h b/src/charon/lib/utils/randomizer.h new file mode 100644 index 000000000..55519550e --- /dev/null +++ b/src/charon/lib/utils/randomizer.h @@ -0,0 +1,110 @@ +/** + * @file randomizer.h + * + * @brief Interface of randomizer_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 RANDOMIZER_H_ +#define RANDOMIZER_H_ + +#include + + +/** + * Device to read real random bytes + */ +#define RANDOM_DEVICE "/dev/random" + +/** + * Device to read pseudo random bytes + */ +#define PSEUDO_RANDOM_DEVICE "/dev/urandom" + +typedef struct randomizer_t randomizer_t; + +/** + * @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(); + +#endif /*RANDOMIZER_H_*/ diff --git a/src/charon/lib/utils/tester.c b/src/charon/lib/utils/tester.c new file mode 100644 index 000000000..a7599dd82 --- /dev/null +++ b/src/charon/lib/utils/tester.c @@ -0,0 +1,256 @@ +/** + * @file tester.c + * + * @brief Implementation of tester_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "tester.h" + +#include +#include + + +typedef struct private_tester_t private_tester_t; + +/** + * @brief Private Data of tester_t class. + * + */ +struct private_tester_t { + + /** + * Protected interface of tester_t. + */ + protected_tester_t protected; + + /** + * Runs a specific test. + * + * @param tester associated tester object + * @param test_function test function to perform + * @param test_name name for the given test + */ + void (*run_test) (private_tester_t *tester, void (*test_function) (protected_tester_t * tester), char * test_name); + + /** + * Returns the difference of to timeval structs in microseconds. + * + * @warning this function is also defined in the event queue + * in later improvements, this function can be added to a general + * class type! + * + * @param end_time end time + * @param start_time start time + * + * @TODO make object function or move to utils! + * + * @return difference in microseconds + */ + long (*time_difference) (private_tester_t *tester,struct timeval *end_time, struct timeval *start_time); + + /** + * Output is written into this file. + */ + FILE* output; + + /** + * Number of already performed tests. + */ + int tests_count; + + /** + * Number of failed tests. + */ + int failed_tests_count; + + /** + * Number of failed asserts in current test. + */ + int failed_asserts_count; + + /** + * TRUE if also succeeded asserts should be written to output. + */ + bool display_succeeded_asserts; + + /** + * Mutex to make this class thread-save. + */ + pthread_mutex_t mutex; +}; + +/** + * Implementation of tester_t.perform_tests. + */ +static void perform_tests(private_tester_t *this,test_t **tests) +{ + int current_test = 0; + fprintf(this->output,"\nStart testing...\n\n"); + fprintf(this->output,"_____________________________________________________________________\n"); + fprintf(this->output,"Testname | running time\n"); + fprintf(this->output,"_______________________________________________________|_____________\n"); + + while (tests[current_test] != NULL) + { + this->run_test(this,tests[current_test]->test_function,tests[current_test]->test_name); + current_test++; + } + fprintf(this->output,"=====================================================================\n"); + fprintf(this->output,"End testing. %d of %d tests succeeded\n",this->tests_count - this->failed_tests_count,this->tests_count); + fprintf(this->output,"=====================================================================\n"); +} + +/** + * Implementation of tester_t.perform_test. + */ +static void perform_test(private_tester_t *this, test_t *test) +{ + test_t *tests[] = {test, NULL}; + return (perform_tests(this,tests)); +} + +/** + * Returns the difference of to timeval structs in microseconds. + * + * @warning this function is also defined in the event queue + * in later improvements, this function can be added to a general + * class type! + * + * @param end_time end time + * @param start_time start time + * + * @TODO make object function or move to utils! + * + * @return difference in microseconds + */ +static long time_difference(private_tester_t *this,struct timeval *end_time, struct timeval *start_time) +{ + long seconds, microseconds; + + seconds = (end_time->tv_sec - start_time->tv_sec); + microseconds = (end_time->tv_usec - start_time->tv_usec); + return ((seconds * 1000000) + microseconds); +} + + +/** + * Implementation of private_tester_t.run_test. + */ +static void run_test(private_tester_t *this, void (*test_function) (protected_tester_t * tester), char * test_name) +{ + struct timeval start_time, end_time; + long timediff; + this->tests_count++; + this->failed_asserts_count = 0; + fprintf(this->output,"%-55s\n", test_name); + gettimeofday(&start_time,NULL); + test_function(&(this->protected)); + gettimeofday(&end_time,NULL); + timediff = this->time_difference(this,&end_time, &start_time); + + if (this->failed_asserts_count > 0) + { + fprintf(this->output," => Test failed: %-37s|%10ld us\n",test_name,timediff); + }else + { + fprintf(this->output,"\033[1A\033[55C|%10ld us\033[1B\033[80D",timediff); + } + if (this->failed_asserts_count > 0) + { + this->failed_tests_count++; + } +} + + +/** + * Implementation of tester_t.assert_true. + */ +static void assert_true(private_tester_t *this, bool to_be_true,char * assert_name) +{ + if (assert_name == NULL) + { + assert_name = "unknown"; + } + + pthread_mutex_lock(&(this->mutex)); + if (!to_be_true) + { + this->failed_asserts_count++; + fprintf(this->output," check '%s' failed!\n", assert_name); + }else + { + if (this->display_succeeded_asserts) + { + fprintf(this->output," check '%s' succeeded\n", assert_name); + } + } + pthread_mutex_unlock(&(this->mutex)); +} + +/** + * Implementation of tester_t.assert_false. + */ +static void assert_false(private_tester_t *this, bool to_be_false,char * assert_name) +{ + this->protected.assert_true(&(this->protected),(!to_be_false),assert_name); +} + +/** + * Implementation of tester_t.destroy. + */ +static void destroy(private_tester_t *tester) +{ + private_tester_t *this = (private_tester_t*) tester; + pthread_mutex_destroy(&(this->mutex)); + free(this); +} + +/* + * Described in header. + */ +tester_t *tester_create(FILE *output, bool display_succeeded_asserts) +{ + private_tester_t *this = malloc_thing(private_tester_t); + + /* public functions */ + this->protected.public.destroy = (void (*) (tester_t *))destroy; + this->protected.public.perform_tests = (void (*) (tester_t *, test_t**)) perform_tests; + this->protected.public.perform_test = (void (*) (tester_t *, test_t*))perform_test; + this->protected.assert_true = (void (*) (protected_tester_t *, bool, char*)) assert_true; + this->protected.assert_false = (void (*) (protected_tester_t *, bool, char*)) assert_false; + + /* private functions */ + this->run_test = run_test; + this->time_difference = time_difference; + + /* private data */ + this->display_succeeded_asserts = display_succeeded_asserts; + this->failed_tests_count = 0; + this->tests_count = 0; + this->output = output; + pthread_mutex_init(&(this->mutex),NULL); + + return &(this->protected.public); +} diff --git a/src/charon/lib/utils/tester.h b/src/charon/lib/utils/tester.h new file mode 100644 index 000000000..3decb2039 --- /dev/null +++ b/src/charon/lib/utils/tester.h @@ -0,0 +1,148 @@ +/** + * @file tester.h + * + * @brief Interface of tester_t. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 TESTER_H_ +#define TESTER_H_ + +#include + +#include + + +/* must be defined here cause it is used in test_t */ +typedef struct protected_tester_t protected_tester_t; + +typedef struct test_t test_t; + +/** + * @brief Representing a specified test. + * + * @ingroup utils + */ +struct test_t { + /** + * Testfunction called for this test. + * + * @param tester associated tester_t object + */ + void (*test_function) (protected_tester_t * tester); + + /** + * Name of the test. + */ + char * test_name; +}; + + +typedef struct tester_t tester_t; + +/** + * @brief A class to perform tests. + * + * @b Constructors: + * - tester_create() + * + * @ingroup utils + */ +struct tester_t { + /** + * @brief Test all testcases in array tests with specific tester_t object. + * + * @param tester tester_t object + * @param tests pointer to an array of test_t-pointers. + * The last item has to be NULL to mark end of array. + */ + void (*perform_tests) (tester_t *tester,test_t **tests); + + /** + * @brief Run a specific test case. + * + * @param this tester_t object + * @param test pointer to a test_t object which will be performed + */ + void (*perform_test) (tester_t *tester, test_t *test); + + /** + * @brief Destroys a tester_t object. + * + * @param tester tester_t object + */ + void (*destroy) (tester_t *tester); +}; + + +/** + * @brief A class used in a specific testcase. + * + * For each testcase an object of this type is passed to the testfunction. The testfunction uses this + * object to check specific asserts with protected_tester_t.assert_true and protected_tester_t.assert_false. + * + * @b Constructors: + * - tester_create() + * + * @ingroup utils + */ +struct protected_tester_t { + + /** + * Public functions of a tester_t object + */ + tester_t public; + + /** + * @brief Is called in a testcase to check a specific situation for TRUE. + * + * Log-Values to the tester output are protected from multiple access. + * + * @param this tester_t object + * @param to_be_true assert which has to be TRUE + * @param assert_name name of the assertion + */ + void (*assert_true) (protected_tester_t *tester, bool to_be_true, char *assert_name); + + /** + * @brief Is called in a testcase to check a specific situation for FALSE. + * + * Log-Values to the tester output are protected from multiple access. + * + * @param this tester_t object + * @param to_be_false assert which has to be FALSE + * @param assert_name name of the assertion + */ + void (*assert_false) (protected_tester_t *tester, bool to_be_false, char *assert_name); +}; + + +/** + * @brief Creates a tester_t object used to perform tests with. + * + * @param output test output is written to this output. + * @param display_succeeded_asserts has to be TRUE, if all asserts should be displayed, + * FALSE otherwise + * + * @return tester_t object + * + * @ingroup utils + */ +tester_t *tester_create(FILE *output, bool display_succeeded_asserts); + +#endif /*TESTER_H_*/ diff --git a/src/charon/patches/strongswan-2.7.0.patch b/src/charon/patches/strongswan-2.7.0.patch new file mode 100644 index 000000000..b21e1013b --- /dev/null +++ b/src/charon/patches/strongswan-2.7.0.patch @@ -0,0 +1,874 @@ +diff -Naur strongswan-2.7.0/Makefile.inc strongswan-2.7.0-patched/Makefile.inc +--- strongswan-2.7.0/Makefile.inc 2006-01-25 18:23:15.000000000 +0100 ++++ strongswan-2.7.0-patched/Makefile.inc 2006-04-28 08:56:38.000000000 +0200 +@@ -84,6 +84,8 @@ + FINALLIBDIR=$(INC_USRLOCAL)/lib/ipsec + LIBDIR=$(DESTDIR)$(FINALLIBDIR) + ++# sharedlibdir is where shared libraries go ++SHAREDLIBDIR=$(DESTDIR)$(INC_USRLOCAL)/lib + + # where the appropriate manpage tree is located + # location within INC_USRLOCAL +@@ -284,6 +286,9 @@ + # include PKCS11-based smartcard support + USE_SMARTCARD?=false + ++# support IKEv2 via charon ++USE_IKEV2?=true ++ + # Default PKCS11 library + # Uncomment this line if using OpenSC <= 0.9.6 + PKCS11_DEFAULT_LIB=\"/usr/lib/pkcs11/opensc-pkcs11.so\" +diff -Naur strongswan-2.7.0/programs/Makefile strongswan-2.7.0-patched/programs/Makefile +--- strongswan-2.7.0/programs/Makefile 2006-04-17 13:04:45.000000000 +0200 ++++ strongswan-2.7.0-patched/programs/Makefile 2006-04-28 08:56:38.000000000 +0200 +@@ -32,6 +32,10 @@ + SUBDIRS+=showpolicy + endif + ++ifeq ($(USE_IKEV2),true) ++SUBDIRS+=charon ++endif ++ + def: + @echo "Please read doc/intro.html or INSTALL before running make" + @false +diff -Naur strongswan-2.7.0/programs/ipsec/ipsec.in strongswan-2.7.0-patched/programs/ipsec/ipsec.in +--- strongswan-2.7.0/programs/ipsec/ipsec.in 2006-03-09 21:09:33.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/ipsec/ipsec.in 2006-04-28 08:56:38.000000000 +0200 +@@ -26,6 +26,7 @@ + export IPSEC_DIR IPSEC_CONFS IPSEC_LIBDIR IPSEC_EXECDIR + + IPSEC_STARTER_PID="/var/run/starter.pid" ++IPSEC_CHARON_PID="/var/run/charon.pid" + + # standardize PATH, and export it for everything else's benefit + PATH="${IPSEC_SBINDIR}":/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin +@@ -123,6 +124,10 @@ + down) + shift + $IPSEC_EXECDIR/whack --name "$1" --terminate ++ if test -e $IPSEC_CHARON_PID ++ then ++ $IPSEC_EXECDIR/stroke down "$1" ++ fi + exit 0 + ;; + listalgs|listpubkeys|listcerts|listcacerts|\ +@@ -134,6 +139,10 @@ + op="$1" + shift + $IPSEC_EXECDIR/whack "$@" "--$op" ++ if test -e $IPSEC_CHARON_PID ++ then ++ $IPSEC_EXECDIR/stroke "$op" ++ fi + exit 0 + ;; + ready) +@@ -180,8 +189,16 @@ + if test $# -eq 0 + then + $IPSEC_EXECDIR/whack "--$op" ++ if test -e $IPSEC_CHARON_PID ++ then ++ $IPSEC_EXECDIR/stroke "$op" ++ fi + else + $IPSEC_EXECDIR/whack --name "$1" "--$op" ++ if test -e $IPSEC_CHARON_PID ++ then ++ $IPSEC_EXECDIR/stroke "$op" "$1" ++ fi + fi + exit 0 + ;; +@@ -198,6 +215,10 @@ + up) + shift + $IPSEC_EXECDIR/whack --name "$1" --initiate ++ if test -e $IPSEC_CHARON_PID ++ then ++ $IPSEC_EXECDIR/stroke up "$1" ++ fi + exit 0 + ;; + update) +diff -Naur strongswan-2.7.0/programs/pluto/Makefile strongswan-2.7.0-patched/programs/pluto/Makefile +--- strongswan-2.7.0/programs/pluto/Makefile 2006-01-25 18:22:19.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/pluto/Makefile 2006-04-28 08:56:38.000000000 +0200 +@@ -170,6 +170,11 @@ + LIBSPLUTO+= -ldl + endif + ++# enable IKEv2 support ++ifeq ($(USE_IKEV2),true) ++ DEFINES+= -DIKEV2 ++endif ++ + # This compile option activates the leak detective + ifeq ($(USE_LEAK_DETECTIVE),true) + DEFINES+= -DLEAK_DETECTIVE +diff -Naur strongswan-2.7.0/programs/pluto/demux.c strongswan-2.7.0-patched/programs/pluto/demux.c +--- strongswan-2.7.0/programs/pluto/demux.c 2005-02-18 22:08:59.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/pluto/demux.c 2006-04-28 08:56:13.000000000 +0200 +@@ -1196,6 +1196,21 @@ + } + #endif + ++#ifdef IKEV2 ++#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; ++ } ++#endif /* IKEV2 */ ++ + return TRUE; + } + +@@ -1229,6 +1244,7 @@ + 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); +diff -Naur strongswan-2.7.0/programs/starter/Makefile strongswan-2.7.0-patched/programs/starter/Makefile +--- strongswan-2.7.0/programs/starter/Makefile 2006-02-17 20:34:02.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/Makefile 2006-04-28 08:56:38.000000000 +0200 +@@ -34,6 +34,11 @@ + DEFINES+= -DLEAK_DETECTIVE + endif + ++# Enable charon support ++ifeq ($(USE_IKEV2),true) ++ DEFINES+= -DIKEV2 ++endif ++ + INCLUDES=-I${FREESWANDIR}/linux/include + CFLAGS=$(DEFINES) $(INCLUDES) -Wall + CFLAGS+=-DIPSEC_EXECDIR=\"${FINALLIBEXECDIR}\" -DIPSEC_CONFDDIR=\"${FINALCONFDDIR}\" +@@ -46,6 +51,11 @@ + starterwhack.o klips.o netkey.o interfaces.o exec.o cmp.o confread.o \ + loglite.o ${PLUTO_OBJS} + ++# Build charon-only objs ++ifeq ($(USE_IKEV2),true) ++ OBJS+= invokecharon.o starterstroke.o ++endif ++ + DISTSRC=$(OBJS:.o=.c) + DISTSRC+=cmp.h confread.h confwrite.h exec.h files.h interfaces.h klips.h netkey.h + DISTSRC+=parser.h args.h invokepluto.h starterwhack.h keywords.h keywords.txt +diff -Naur strongswan-2.7.0/programs/starter/args.c strongswan-2.7.0-patched/programs/starter/args.c +--- strongswan-2.7.0/programs/starter/args.c 2006-04-17 12:32:36.000000000 +0200 ++++ strongswan-2.7.0-patched/programs/starter/args.c 2006-04-28 08:56:38.000000000 +0200 +@@ -86,6 +86,10 @@ + + static const char *LST_keyexchange[] = { + "ike", ++#ifdef IKEV2 ++ "ikev1", ++ "ikev2", ++#endif /* IKEV2 */ + NULL + }; + +diff -Naur strongswan-2.7.0/programs/starter/files.h strongswan-2.7.0-patched/programs/starter/files.h +--- strongswan-2.7.0/programs/starter/files.h 2006-02-04 19:52:58.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/files.h 2006-04-28 08:56:38.000000000 +0200 +@@ -37,8 +37,15 @@ + #define SECRETS_FILE IPSEC_CONFDIR"/ipsec.secrets" + + #define PLUTO_CMD IPSEC_EXECDIR"/pluto" +-#define CTL_FILE DEFAULT_CTLBASE CTL_SUFFIX +-#define PID_FILE DEFAULT_CTLBASE PID_SUFFIX ++#define PLUTO_CTL_FILE DEFAULT_CTLBASE CTL_SUFFIX ++#define PLUTO_PID_FILE DEFAULT_CTLBASE PID_SUFFIX ++ ++#ifdef IKEV2 ++#define CHARON_CMD IPSEC_EXECDIR"/charon" ++#define CHARON_BASE "/var/run/charon" ++#define CHARON_CTL_FILE CHARON_BASE CTL_SUFFIX ++#define CHARON_PID_FILE CHARON_BASE PID_SUFFIX ++#endif /* IKEV2 */ + + #define DYNIP_DIR "/var/run/dynip" + #define INFO_FILE "/var/run/ipsec.info" +diff -Naur strongswan-2.7.0/programs/starter/invokecharon.c strongswan-2.7.0-patched/programs/starter/invokecharon.c +--- strongswan-2.7.0/programs/starter/invokecharon.c 1970-01-01 01:00:00.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/invokecharon.c 2006-04-28 08:56:38.000000000 +0200 +@@ -0,0 +1,174 @@ ++/* strongSwan charon launcher ++ * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security ++ * Copyright (C) 2006 Martin Willi - Hochschule fuer Technik Rapperswil ++ * ++ * Ported from invokepluto.c to fit charons needs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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: invokecharon.c $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "../pluto/constants.h" ++#include "../pluto/defs.h" ++#include "../pluto/log.h" ++ ++#include "confread.h" ++#include "invokecharon.h" ++#include "files.h" ++ ++static int _charon_pid = 0; ++static int _stop_requested; ++ ++pid_t ++starter_charon_pid(void) ++{ ++ return _charon_pid; ++} ++ ++void ++starter_charon_sigchild(pid_t pid) ++{ ++ if (pid == _charon_pid) ++ { ++ _charon_pid = 0; ++ if (!_stop_requested) ++ { ++ plog("charon has died -- restart scheduled (%dsec)" ++ , CHARON_RESTART_DELAY); ++ alarm(CHARON_RESTART_DELAY); // restart in 5 sec ++ } ++ unlink(CHARON_PID_FILE); ++ } ++} ++ ++int ++starter_stop_charon (void) ++{ ++ pid_t pid; ++ int i; ++ ++ pid = _charon_pid; ++ if (pid) ++ { ++ _stop_requested = 1; ++ ++ /* be more and more aggressive */ ++ for (i = 0; i < 20 && (pid = _charon_pid) != 0; i++) ++ { ++ if (i == 0) ++ kill(pid, SIGINT); ++ else if (i < 10) ++ kill(pid, SIGTERM); ++ else ++ kill(pid, SIGKILL); ++ usleep(20000); ++ } ++ if (_charon_pid == 0) ++ return 0; ++ plog("starter_stop_charon(): can't stop charon !!!"); ++ return -1; ++ } ++ else ++ { ++ plog("stater_stop_charon(): charon is not started..."); ++ } ++ return -1; ++} ++ ++ ++int ++starter_start_charon (starter_config_t *cfg, bool debug) ++{ ++ int pid, i; ++ struct stat stb; ++ int argc = 1; ++ char *arg[] = { ++ CHARON_CMD, NULL, NULL, ++ }; ++ ++ if (!debug) ++ { ++ arg[argc++] = "--use-syslog"; ++ } ++ ++ if (_charon_pid) ++ { ++ plog("starter_start_charon(): charon already started..."); ++ return -1; ++ } ++ else ++ { ++ unlink(CHARON_CTL_FILE); ++ _stop_requested = 0; ++ ++ pid = fork(); ++ switch (pid) ++ { ++ case -1: ++ plog("can't fork(): %s", strerror(errno)); ++ return -1; ++ case 0: ++ /* child */ ++ setsid(); ++ sigprocmask(SIG_SETMASK, 0, NULL); ++ execv(arg[0], arg); ++ plog("can't execv(%s,...): %s", arg[0], strerror(errno)); ++ exit(1); ++ default: ++ /* father */ ++ _charon_pid = pid; ++ for (i = 0; i < 50 && _charon_pid; i++) ++ { ++ /* wait for charon */ ++ usleep(20000); ++ if (stat(CHARON_PID_FILE, &stb) == 0) ++ { ++ DBG(DBG_CONTROL, ++ DBG_log("charon (%d) started", _charon_pid) ++ ) ++ return 0; ++ } ++ } ++ if (_charon_pid) ++ { ++ /* If charon is started but with no ctl file, stop it */ ++ plog("charon too long to start... - kill kill"); ++ for (i = 0; i < 20 && (pid = _charon_pid) != 0; i++) ++ { ++ if (i == 0) ++ kill(pid, SIGINT); ++ else if (i < 10) ++ kill(pid, SIGTERM); ++ else ++ kill(pid, SIGKILL); ++ usleep(20000); ++ } ++ } ++ else ++ { ++ plog("charon refused to be started"); ++ } ++ return -1; ++ } ++ } ++ return -1; ++} +diff -Naur strongswan-2.7.0/programs/starter/invokecharon.h strongswan-2.7.0-patched/programs/starter/invokecharon.h +--- strongswan-2.7.0/programs/starter/invokecharon.h 1970-01-01 01:00:00.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/invokecharon.h 2006-04-28 08:56:38.000000000 +0200 +@@ -0,0 +1,31 @@ ++/* strongSwan charon launcher ++ * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security ++ * Copyright (C) 2006 Martin Willi - Hochschule fuer Technik Rapperswil ++ * ++ * Ported from invokepluto.h to fit charons needs. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. See . ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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: invokecharon.h $ ++ */ ++ ++#ifndef _STARTER_CHARON_H_ ++#define _STARTER_CHARON_H_ ++ ++#define CHARON_RESTART_DELAY 5 ++ ++extern void starter_charon_sigchild (pid_t pid); ++extern pid_t starter_charon_pid (void); ++extern int starter_stop_charon (void); ++extern int starter_start_charon(struct starter_config *cfg, bool debug); ++ ++#endif /* _STARTER_CHARON_H_ */ ++ +diff -Naur strongswan-2.7.0/programs/starter/invokepluto.c strongswan-2.7.0-patched/programs/starter/invokepluto.c +--- strongswan-2.7.0/programs/starter/invokepluto.c 2006-02-17 22:41:50.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/invokepluto.c 2006-04-28 08:56:38.000000000 +0200 +@@ -54,7 +54,7 @@ + , PLUTO_RESTART_DELAY); + alarm(PLUTO_RESTART_DELAY); // restart in 5 sec + } +- unlink(PID_FILE); ++ unlink(PLUTO_PID_FILE); + } + } + +@@ -203,7 +203,7 @@ + } + else + { +- unlink(CTL_FILE); ++ unlink(PLUTO_CTL_FILE); + _stop_requested = 0; + + if (cfg->setup.prepluto) +@@ -252,7 +252,7 @@ + { + /* wait for pluto */ + usleep(20000); +- if (stat(CTL_FILE, &stb) == 0) ++ if (stat(PLUTO_CTL_FILE, &stb) == 0) + { + DBG(DBG_CONTROL, + DBG_log("pluto (%d) started", _pluto_pid) +diff -Naur strongswan-2.7.0/programs/starter/starter.c strongswan-2.7.0-patched/programs/starter/starter.c +--- strongswan-2.7.0/programs/starter/starter.c 2006-02-15 19:37:46.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/starter.c 2006-04-28 08:56:38.000000000 +0200 +@@ -37,6 +37,7 @@ + #include "files.h" + #include "starterwhack.h" + #include "invokepluto.h" ++#include "invokecharon.h" + #include "klips.h" + #include "netkey.h" + #include "cmp.h" +@@ -47,6 +48,9 @@ + #define FLAG_ACTION_RELOAD 0x04 + #define FLAG_ACTION_QUIT 0x08 + #define FLAG_ACTION_LISTEN 0x10 ++#ifdef IKEV2 ++#define FLAG_ACTION_START_CHARON 0x20 ++#endif /* IKEV2 */ + + static unsigned int _action_ = 0; + +@@ -65,6 +69,10 @@ + { + if (pid == starter_pluto_pid()) + name = " (Pluto)"; ++#ifdef IKEV2 ++ if (pid == starter_charon_pid()) ++ name = " (Charon)"; ++#endif /* IKEV2 */ + if (WIFSIGNALED(status)) + DBG(DBG_CONTROL, + DBG_log("child %d%s has been killed by sig %d\n", +@@ -87,6 +95,10 @@ + + if (pid == starter_pluto_pid()) + starter_pluto_sigchild(pid); ++#ifdef IKEV2 ++ if (pid == starter_charon_pid()) ++ starter_charon_sigchild(pid); ++#endif /* IKEV2 */ + } + } + break; +@@ -97,6 +109,9 @@ + + case SIGALRM: + _action_ |= FLAG_ACTION_START_PLUTO; ++#ifdef IKEV2 ++ _action_ |= FLAG_ACTION_START_CHARON; ++#endif /* IKEV2 */ + break; + + case SIGHUP: +@@ -193,6 +208,9 @@ + signal(SIGQUIT, fsig); + signal(SIGALRM, fsig); + signal(SIGUSR1, fsig); ++ ++ ++ plog("Starting strongSwan IPsec %s [starter]...", ipsec_version_code()); + + /* verify that we can start */ + if (getuid() != 0) +@@ -201,12 +219,24 @@ + exit(1); + } + +- if (stat(PID_FILE, &stb) == 0) ++ if (stat(PLUTO_PID_FILE, &stb) == 0) + { +- plog("pluto is already running (%s exists) -- aborting", PID_FILE); +- exit(1); ++ plog("pluto is already running (%s exists) -- skipping pluto start", PLUTO_PID_FILE); + } +- ++ else ++ { ++ _action_ |= FLAG_ACTION_START_PLUTO; ++ } ++#ifdef IKEV2 ++ if (stat(CHARON_PID_FILE, &stb) == 0) ++ { ++ plog("charon is already running (%s exists) -- skipping charon start", CHARON_PID_FILE); ++ } ++ else ++ { ++ _action_ |= FLAG_ACTION_START_CHARON; ++ } ++#endif /* IKEV2 */ + if (stat(DEV_RANDOM, &stb) != 0) + { + plog("unable to start strongSwan IPsec -- no %s!", DEV_RANDOM); +@@ -247,7 +277,11 @@ + + last_reload = time(NULL); + +- plog("Starting strongSwan IPsec %s [starter]...", ipsec_version_code()); ++ if (stat(MY_PID_FILE, &stb) == 0) ++ { ++ plog("starter is already running (%s exists) -- no fork done", MY_PID_FILE); ++ exit(0); ++ } + + /* fork if we're not debugging stuff */ + if (!no_fork) +@@ -296,17 +330,19 @@ + , &cfg->defaultroute); + } + +- _action_ = FLAG_ACTION_START_PLUTO; +- + for (;;) + { + /* +- * Stop pluto (if started) and exit +- */ ++ * Stop pluto/charon (if started) and exit ++ */ + if (_action_ & FLAG_ACTION_QUIT) + { + if (starter_pluto_pid()) + starter_stop_pluto(); ++#ifdef IKEV2 ++ if (starter_charon_pid()) ++ starter_stop_charon(); ++#endif IKEV2 + if (has_netkey) + starter_netkey_cleanup(); + else +@@ -337,6 +373,9 @@ + if (conn->state == STATE_ADDED) + { + starter_whack_del_conn(conn); ++#ifdef IKEV2 ++ starter_stroke_del_conn(conn); ++#endif /* IKEV2 */ + conn->state = STATE_TO_ADD; + } + } +@@ -427,6 +466,9 @@ + { + if (conn->state == STATE_ADDED) + starter_whack_del_conn(conn); ++#ifdef IKEV2 ++ starter_stroke_del_conn(conn); ++#endif /* IKEV2 */ + } + + /* Look for new ca sections that are already loaded */ +@@ -502,6 +544,27 @@ + conn->state = STATE_TO_ADD; + } + } ++ ++#ifdef IKEV2 ++ /* ++ * Start charon ++ */ ++ if (_action_ & FLAG_ACTION_START_CHARON) ++ { ++ if (starter_charon_pid() == 0) ++ { ++ DBG(DBG_CONTROL, ++ DBG_log("Attempting to start charon...") ++ ) ++ if (starter_start_charon(cfg, no_fork) != 0) ++ { ++ /* schedule next try */ ++ alarm(PLUTO_RESTART_DELAY); ++ } ++ } ++ _action_ &= ~FLAG_ACTION_START_CHARON; ++ } ++#endif /* IKEV2 */ + + /* + * Tell pluto to reread its interfaces +@@ -536,11 +599,36 @@ + conn->id = id++; + } + starter_whack_add_conn(conn); ++#ifdef IKEV2 ++ starter_stroke_add_conn(conn); ++#endif /* IKEV2 */ + conn->state = STATE_ADDED; + if (conn->startup == STARTUP_START) +- starter_whack_initiate_conn(conn); ++ { ++#ifdef IKEV2 ++ if (conn->keyexchange == 2) ++ { ++ starter_stroke_initiate_conn(conn); ++ } ++ else ++#endif /* IKEV2 */ ++ { ++ starter_whack_initiate_conn(conn); ++ } ++ } + else if (conn->startup == STARTUP_ROUTE) +- starter_whack_route_conn(conn); ++ { ++#ifdef IKEV2 ++ if (conn->keyexchange == 2) ++ { ++ starter_stroke_route_conn(conn); ++ } ++ else ++#endif /* IKEV2 */ ++ { ++ starter_whack_route_conn(conn); ++ } ++ } + } + } + } +diff -Naur strongswan-2.7.0/programs/starter/starterstroke.c strongswan-2.7.0-patched/programs/starter/starterstroke.c +--- strongswan-2.7.0/programs/starter/starterstroke.c 1970-01-01 01:00:00.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/starterstroke.c 2006-04-28 08:56:38.000000000 +0200 +@@ -0,0 +1,161 @@ ++/* Stroke for charon is the counterpart to whack from pluto ++ * 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. ++ * ++ * RCSID $Id: starterstroke.c $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "../pluto/constants.h" ++#include "../pluto/defs.h" ++#include "../pluto/log.h" ++ ++#include "../charon/stroke/stroke.h" ++ ++#include "starterstroke.h" ++#include "confread.h" ++#include "files.h" ++ ++static char* push_string(stroke_msg_t **strm, char *string) ++{ ++ stroke_msg_t *stroke_msg; ++ size_t string_length; ++ ++ if (string == NULL) ++ { ++ return NULL; ++ } ++ stroke_msg = *strm; ++ string_length = strlen(string) + 1; ++ stroke_msg->length += string_length; ++ ++ stroke_msg = realloc(stroke_msg, stroke_msg->length); ++ strcpy((char*)stroke_msg + stroke_msg->length - string_length, string); ++ ++ *strm = stroke_msg; ++ return (char*)(u_int)stroke_msg->length - string_length; ++} ++ ++static int ++send_stroke_msg (stroke_msg_t *msg) ++{ ++ struct sockaddr_un ctl_addr = { AF_UNIX, CHARON_CTL_FILE }; ++ int sock; ++ ++ sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (sock < 0) ++ { ++ plog("socket() failed: %s", strerror(errno)); ++ return -1; ++ } ++ if (connect(sock, (struct sockaddr *)&ctl_addr, ++ offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0) ++ { ++ plog("connect(charon_ctl) failed: %s", strerror(errno)); ++ close(sock); ++ return -1; ++ } ++ ++ /* send message */ ++ if (write(sock, msg, msg->length) != msg->length) ++ { ++ plog("write(charon_ctl) failed: %s", strerror(errno)); ++ close(sock); ++ return -1; ++ } ++ ++ close(sock); ++ return 0; ++} ++ ++static char * ++connection_name(starter_conn_t *conn) ++{ ++ /* if connection name is '%auto', create a new name like conn_xxxxx */ ++ static char buf[32]; ++ ++ if (streq(conn->name, "%auto")) ++ { ++ sprintf(buf, "conn_%ld", conn->id); ++ return buf; ++ } ++ return conn->name; ++} ++ ++ ++int starter_stroke_add_conn(starter_conn_t *conn) ++{ ++ stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); ++ int res; ++ ++ msg->length = sizeof(stroke_msg_t); ++ msg->type = STR_ADD_CONN; ++ ++ msg->add_conn.name = push_string(&msg, connection_name(conn)); ++ ++ msg->add_conn.me.id = push_string(&msg, conn->left.id); ++ msg->add_conn.me.cert = push_string(&msg, conn->left.cert); ++ msg->add_conn.me.address = push_string(&msg, inet_ntoa(conn->left.addr.u.v4.sin_addr)); ++ msg->add_conn.me.subnet = push_string(&msg, inet_ntoa(conn->left.subnet.addr.u.v4.sin_addr)); ++ msg->add_conn.me.subnet_mask = conn->left.subnet.maskbits; ++ ++ msg->add_conn.other.id = push_string(&msg, conn->right.id); ++ msg->add_conn.other.cert = push_string(&msg, conn->right.cert); ++ msg->add_conn.other.address = push_string(&msg, inet_ntoa(conn->right.addr.u.v4.sin_addr)); ++ msg->add_conn.other.subnet = push_string(&msg, inet_ntoa(conn->right.subnet.addr.u.v4.sin_addr)); ++ msg->add_conn.other.subnet_mask = conn->right.subnet.maskbits; ++ ++ res = send_stroke_msg(msg); ++ free(msg); ++ return res; ++} ++ ++int starter_stroke_del_conn(starter_conn_t *conn) ++{ ++ return 0; ++} ++int starter_stroke_route_conn(starter_conn_t *conn) ++{ ++ stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); ++ int res; ++ ++ msg->length = sizeof(stroke_msg_t); ++ msg->type = STR_INSTALL; ++ msg->install.name = push_string(&msg, connection_name(conn)); ++ res = send_stroke_msg(msg); ++ free(msg); ++ return res; ++} ++ ++int starter_stroke_initiate_conn(starter_conn_t *conn) ++{ ++ stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); ++ int res; ++ ++ msg->length = sizeof(stroke_msg_t); ++ msg->type = STR_INITIATE; ++ msg->initiate.name = push_string(&msg, connection_name(conn)); ++ res = send_stroke_msg(msg); ++ free(msg); ++ return res; ++} +diff -Naur strongswan-2.7.0/programs/starter/starterstroke.h strongswan-2.7.0-patched/programs/starter/starterstroke.h +--- strongswan-2.7.0/programs/starter/starterstroke.h 1970-01-01 01:00:00.000000000 +0100 ++++ strongswan-2.7.0-patched/programs/starter/starterstroke.h 2006-04-28 08:56:38.000000000 +0200 +@@ -0,0 +1,27 @@ ++/* Stroke for charon is the counterpart to whack from pluto ++ * 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. ++ * ++ * RCSID $Id: starterstroke.h $ ++ */ ++ ++#ifndef _STARTER_STROKE_H_ ++#define _STARTER_STROKE_H_ ++ ++#include "confread.h" ++ ++extern int starter_stroke_add_conn(starter_conn_t *conn); ++extern int starter_stroke_del_conn(starter_conn_t *conn); ++extern int starter_stroke_route_conn(starter_conn_t *conn); ++extern int starter_stroke_initiate_conn(starter_conn_t *conn); ++ ++#endif /* _STARTER_STROKE_H_ */ +diff -Naur strongswan-2.7.0/programs/starter/starterwhack.c strongswan-2.7.0-patched/programs/starter/starterwhack.c +--- strongswan-2.7.0/programs/starter/starterwhack.c 2006-04-17 12:32:36.000000000 +0200 ++++ strongswan-2.7.0-patched/programs/starter/starterwhack.c 2006-04-28 08:56:38.000000000 +0200 +@@ -54,7 +54,7 @@ + static int + send_whack_msg (whack_message_t *msg) + { +- struct sockaddr_un ctl_addr = { AF_UNIX, CTL_FILE }; ++ struct sockaddr_un ctl_addr = { AF_UNIX, PLUTO_CTL_FILE }; + int sock; + ssize_t len; + char *str_next, *str_roof; diff --git a/src/charon/scripts/alice-key.der b/src/charon/scripts/alice-key.der new file mode 100644 index 000000000..5a8aef6cb Binary files /dev/null and b/src/charon/scripts/alice-key.der differ diff --git a/src/charon/scripts/alice.der b/src/charon/scripts/alice.der new file mode 100644 index 000000000..8154defd9 Binary files /dev/null and b/src/charon/scripts/alice.der differ diff --git a/src/charon/scripts/bob-key.der b/src/charon/scripts/bob-key.der new file mode 100644 index 000000000..f944dec9f Binary files /dev/null and b/src/charon/scripts/bob-key.der differ diff --git a/src/charon/scripts/bob.der b/src/charon/scripts/bob.der new file mode 100644 index 000000000..401611888 Binary files /dev/null and b/src/charon/scripts/bob.der differ diff --git a/src/charon/scripts/complex1.der b/src/charon/scripts/complex1.der new file mode 100644 index 000000000..ba460cbee Binary files /dev/null and b/src/charon/scripts/complex1.der differ diff --git a/src/charon/scripts/complex2.der b/src/charon/scripts/complex2.der new file mode 100644 index 000000000..160b21f47 Binary files /dev/null and b/src/charon/scripts/complex2.der differ diff --git a/src/charon/scripts/daemon-loop.sh b/src/charon/scripts/daemon-loop.sh new file mode 100755 index 000000000..9a361e012 --- /dev/null +++ b/src/charon/scripts/daemon-loop.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +while [ 1 ] +do + ip x p f + ip x s f + rm /var/run/charon.* + make + bin/charon + echo "" + echo "----------------------------" + echo "" +done diff --git a/src/charon/scripts/deleteline b/src/charon/scripts/deleteline new file mode 100755 index 000000000..9f529dccc --- /dev/null +++ b/src/charon/scripts/deleteline @@ -0,0 +1,9 @@ +#!/bin/bash + +FILES=`find . -name '*.[ch]'` +for FILE in $FILES +do + TMP=${FILE}_tmp + sed "/$1/d" < $FILE > $TMP + mv $TMP $FILE +done diff --git a/src/charon/scripts/replace b/src/charon/scripts/replace new file mode 100755 index 000000000..adfc8e09a --- /dev/null +++ b/src/charon/scripts/replace @@ -0,0 +1,9 @@ +#!/bin/bash + +FILES=`find . -name '*.[ch]'` +for FILE in $FILES +do + TMP=${FILE}_tmp + sed "s/$1/$2/g" < $FILE > $TMP + mv $TMP $FILE +done diff --git a/src/charon/scripts/to-alice.sh b/src/charon/scripts/to-alice.sh new file mode 100755 index 000000000..01ba27f5b --- /dev/null +++ b/src/charon/scripts/to-alice.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# enable ip forwarding for gateway +echo 1 > /proc/sys/net/ipv4/ip_forward + +# add connection to alice +MY_ADDR=192.168.0.2 # Address of local peer +OTHER_ADDR=192.168.0.1 # Address of remote peer +MY_ID="C=CH, O=Linux strongSwan, CN=bob" # ID of local peer +OTHER_ID="C=CH, O=Linux strongSwan, CN=alice" # ID of remote peer +MY_NET=10.2.0.0 # protected local subnet +OTHER_NET=10.1.0.0 # protected remote subnet +MY_BITS=16 # size of subnet +OTHER_BITS=16 # size of subnet +CONN_NAME=to-alice # connection name + +bin/stroke add $CONN_NAME "$MY_ID" "$OTHER_ID" $MY_ADDR $OTHER_ADDR $MY_NET $OTHER_NET $MY_BITS $OTHER_BITS + +# initiate +i=0 +LIMIT=1 + +while [ "$i" -lt "$LIMIT" ] +do + bin/stroke up $CONN_NAME + let "i += 1" +done diff --git a/src/charon/scripts/to-bob.sh b/src/charon/scripts/to-bob.sh new file mode 100755 index 000000000..df30bd893 --- /dev/null +++ b/src/charon/scripts/to-bob.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# enable ip forwarding for gateway +echo 1 > /proc/sys/net/ipv4/ip_forward + +# add connection to bob +MY_ADDR=192.168.0.1 # Address of local peer +OTHER_ADDR=192.168.0.2 # Address of remote peer +MY_ID="C=CH, O=Linux strongSwan, CN=alice" # ID of local peer +OTHER_ID="C=CH, O=Linux strongSwan, CN=bob" # ID of remote peer +MY_NET=10.1.0.0 # protected local subnet +OTHER_NET=10.2.0.0 # protected remote subnet +MY_BITS=16 # size of subnet +OTHER_BITS=16 # size of subnet +CONN_NAME=to-bob # connection name + +bin/stroke add $CONN_NAME "$MY_ID" "$OTHER_ID" $MY_ADDR $OTHER_ADDR $MY_NET $OTHER_NET $MY_BITS $OTHER_BITS + +# initiate +i=0 +LIMIT=0 + +while [ "$i" -lt "$LIMIT" ] +do + bin/stroke up $CONN_NAME + let "i += 1" +done diff --git a/src/charon/stroke/Makefile.stroke b/src/charon/stroke/Makefile.stroke new file mode 100644 index 000000000..c87445095 --- /dev/null +++ b/src/charon/stroke/Makefile.stroke @@ -0,0 +1,17 @@ +# 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. +# +STROKE_DIR= $(MAIN_DIR)stroke/ + +$(BUILD_DIR)stroke.o : $(STROKE_DIR)stroke.c $(STROKE_DIR)stroke.h + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/charon/stroke/stroke.c b/src/charon/stroke/stroke.c new file mode 100644 index 000000000..9ecda0413 --- /dev/null +++ b/src/charon/stroke/stroke.c @@ -0,0 +1,304 @@ +/* Stroke for charon is the counterpart to whack from pluto + * 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.h" + +static char* push_string(stroke_msg_t **strm, char *string) +{ + stroke_msg_t *stroke_msg; + size_t string_length; + + if (string == NULL) + { + return NULL; + } + stroke_msg = *strm; + string_length = strlen(string) + 1; + stroke_msg->length += string_length; + + stroke_msg = realloc(stroke_msg, stroke_msg->length); + strcpy((char*)stroke_msg + stroke_msg->length - string_length, string); + + *strm = stroke_msg; + return (char*)(u_int)stroke_msg->length - string_length; +} + +static int send_stroke_msg (stroke_msg_t *msg) +{ + struct sockaddr_un ctl_addr = { AF_UNIX, STROKE_SOCKET }; + int sock; + char buffer[64]; + int byte_count; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + { + fprintf(stderr, "Opening unix socket %s: %s\n", STROKE_SOCKET, strerror(errno)); + return -1; + } + if (connect(sock, (struct sockaddr *)&ctl_addr, + offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0) + { + fprintf(stderr, "Connect to socket failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + /* send message */ + if (write(sock, msg, msg->length) != msg->length) + { + fprintf(stderr, "writing to socket failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + while ((byte_count = read(sock, buffer, sizeof(buffer)-1)) > 0) + { + buffer[byte_count] = '\0'; + printf("%s", buffer); + } + if (byte_count < 0) + { + fprintf(stderr, "reading from socket failed: %s\n", strerror(errno)); + } + + close(sock); + return 0; +} + +static int add_connection(char *name, + char *my_id, char *other_id, + char *my_addr, char *other_addr, + char *my_net, char *other_net, + u_int my_netmask, u_int other_netmask) +{ + stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); + int res; + + msg->length = sizeof(stroke_msg_t); + msg->type = STR_ADD_CONN; + + msg->add_conn.name = push_string(&msg, name); + + msg->add_conn.me.id = push_string(&msg, my_id); + msg->add_conn.me.address = push_string(&msg, my_addr); + msg->add_conn.me.subnet = push_string(&msg, my_net); + msg->add_conn.me.subnet_mask = my_netmask; + msg->add_conn.me.cert = NULL; + + msg->add_conn.other.id = push_string(&msg, other_id); + msg->add_conn.other.address = push_string(&msg, other_addr); + msg->add_conn.other.subnet = push_string(&msg, other_net); + msg->add_conn.other.subnet_mask = other_netmask; + msg->add_conn.other.cert = NULL; + + res = send_stroke_msg(msg); + free(msg); + return res; +} + +static int initiate_connection(char *name) +{ + stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); + int res; + + msg->length = sizeof(stroke_msg_t); + msg->type = STR_INITIATE; + msg->initiate.name = push_string(&msg, name); + res = send_stroke_msg(msg); + free(msg); + return res; +} + +static int terminate_connection(char *name) +{ + stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); + int res; + + msg->length = sizeof(stroke_msg_t); + msg->type = STR_TERMINATE; + msg->initiate.name = push_string(&msg, name); + res = send_stroke_msg(msg); + free(msg); + return res; +} + +static int show_status(char *mode, char *connection) +{ + stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); + int res; + + msg->length = sizeof(stroke_msg_t); + if (strcmp(mode, "statusall") == 0) + { + msg->type = STR_STATUS_ALL; + } + else + { + msg->type = STR_STATUS; + } + msg->status.name = push_string(&msg, connection); + res = send_stroke_msg(msg); + free(msg); + return res; +} + +static int set_logtype(char *context, char *type, int enable) +{ + stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); + int res; + + msg->length = sizeof(stroke_msg_t); + msg->type = STR_LOGTYPE; + msg->logtype.context = push_string(&msg, context); + msg->logtype.type = push_string(&msg, type); + msg->logtype.enable = enable; + res = send_stroke_msg(msg); + free(msg); + return res; +} + +static int set_loglevel(char *context, u_int level) +{ + stroke_msg_t *msg = malloc(sizeof(stroke_msg_t)); + int res; + + msg->length = sizeof(stroke_msg_t); + msg->type = STR_LOGLEVEL; + msg->loglevel.context = push_string(&msg, context); + msg->loglevel.level = level; + res = send_stroke_msg(msg); + free(msg); + return res; +} + +static void exit_error(char *error) +{ + if (error) + { + fprintf(stderr, "%s\n", error); + } + exit(-1); +} + +static void exit_usage(char *error) +{ + printf("Usage:\n"); + printf(" Add a connection:\n"); + printf(" stroke add NAME MY_ID OTHER_ID MY_ADDR OTHER_ADDR\\\n"); + printf(" MY_NET OTHER_NET MY_NETBITS OTHER_NETBITS\n"); + printf(" where: ID is any IKEv2 ID \n"); + printf(" ADDR is a IPv4 address\n"); + printf(" NET is a IPv4 address of the subnet to tunnel\n"); + printf(" NETBITS is the size of the subnet, as the \"24\" in 192.168.0.0/24\n"); + printf(" Initiate a connection:\n"); + printf(" stroke up NAME\n"); + printf(" where: NAME is a connection name added with \"stroke add\"\n"); + printf(" Terminate a connection:\n"); + printf(" stroke down NAME\n"); + printf(" where: NAME is a connection name added with \"stroke add\"\n"); + printf(" Set logtype for a logging context:\n"); + printf(" stroke logtype CONTEXT TYPE ENABLE\n"); + printf(" where: CONTEXT is PARSR|GNRAT|IKESA|SAMGR|CHDSA|MESSG|TPOOL|WORKR|SCHED|\n"); + printf(" SENDR|RECVR|SOCKT|TESTR|DAEMN|CONFG|ENCPL|PAYLD\n"); + printf(" TYPE is CONTROL|ERROR|AUDIT|RAW|PRIVATE\n"); + printf(" ENABLE is 0|1\n"); + printf(" Set loglevel for a logging context:\n"); + printf(" stroke loglevel CONTEXT LEVEL\n"); + printf(" where: CONTEXT is PARSR|GNRAT|IKESA|SAMGR|CHDSA|MESSG|TPOOL|WORKR|SCHED|\n"); + printf(" SENDR|RECVR|SOCKT|TESTR|DAEMN|CONFG|ENCPL|PAYLD\n"); + printf(" LEVEL is 0|1|2|3\n"); + printf(" Show connection status:\n"); + printf(" stroke status\n"); + exit_error(error); +} + +int main(int argc, char *argv[]) +{ + int res; + + if (argc < 2) + { + exit_usage(NULL); + } + + if (strcmp(argv[1], "status") == 0 || + strcmp(argv[1], "statusall") == 0) + { + res = show_status(argv[1], argc > 2 ? argv[2] : NULL); + } + + else if (strcmp(argv[1], "up") == 0) + { + if (argc < 3) + { + exit_usage("\"up\" needs a connection name"); + } + res = initiate_connection(argv[2]); + } + else if (strcmp(argv[1], "down") == 0) + { + if (argc < 3) + { + exit_usage("\"down\" needs a connection name"); + } + res = terminate_connection(argv[2]); + } + else if (strcmp(argv[1], "add") == 0) + { + if (argc < 11) + { + exit_usage("\"add\" needs more parameters..."); + } + res = add_connection(argv[2], + argv[3], argv[4], + argv[5], argv[6], + argv[7], argv[8], + atoi(argv[9]), atoi(argv[10])); + } + else if (strcmp(argv[1], "logtype") == 0) + { + if (argc < 5) + { + exit_usage("\"logtype\" needs more parameters..."); + } + res = set_logtype(argv[2], argv[3], atoi(argv[4])); + } + else if (strcmp(argv[1], "loglevel") == 0) + { + if (argc < 4) + { + exit_usage("\"logtype\" needs more parameters..."); + } + res = set_loglevel(argv[2], atoi(argv[3])); + } + else + { + exit_usage(NULL); + } + + return res; +} diff --git a/src/charon/stroke/stroke.h b/src/charon/stroke/stroke.h new file mode 100644 index 000000000..cb40cf843 --- /dev/null +++ b/src/charon/stroke/stroke.h @@ -0,0 +1,91 @@ +/** + * @file stroke.h + * + * @brief Definition of stroke_msg_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_H_ +#define STROKE_H_ + +/** + * Socket which is used to communicate between charon and stroke + */ +#define STROKE_SOCKET "/var/run/charon.ctl" + + +typedef struct stroke_msg_t stroke_msg_t; + +/** + * @brief A stroke message sent over the unix socket. + */ +struct stroke_msg_t { + /* length of this message with all strings */ + u_int16_t length; + /* type of the message */ + enum { + /* initiate a connection */ + STR_INITIATE, + /* install SPD entries for a connection */ + STR_INSTALL, + /* add a connection */ + STR_ADD_CONN, + /* delete a connection */ + STR_DEL_CONN, + /* terminate connection */ + STR_TERMINATE, + /* show connection status */ + STR_STATUS, + /* show verbose connection status */ + STR_STATUS_ALL, + /* set a log type to log/not log */ + STR_LOGTYPE, + /* set the verbosity of a logging context */ + STR_LOGLEVEL, + /* more to come */ + } type; + union { + /* data for STR_INITIATE, STR_INSTALL, STR_UP, STR_DOWN */ + struct { + char *name; + } initiate, install, terminate, status; + /* data for STR_ADD_CONN */ + struct { + char *name; + struct { + char *id; + char *cert; + char *address; + char *subnet; + u_int8_t subnet_mask; + } me, other; + } add_conn; + struct { + char *context; + char *type; + int enable; + } logtype; + struct { + char *context; + u_int level; + } loglevel; + }; + u_int8_t buffer[]; +}; + +#endif /* STROKE_H_ */ diff --git a/src/charon/testing/Makefile.testcases b/src/charon/testing/Makefile.testcases new file mode 100644 index 000000000..5a261a799 --- /dev/null +++ b/src/charon/testing/Makefile.testcases @@ -0,0 +1,139 @@ +# Copyright (C) 2005 Jan Hutter, 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. +# + +TESTCASES_DIR= $(MAIN_DIR)testing/ + + +$(BUILD_DIR)testcases.o : $(TESTCASES_DIR)testcases.c + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)aes_cbc_crypter_test.o +$(BUILD_DIR)aes_cbc_crypter_test.o : $(TESTCASES_DIR)aes_cbc_crypter_test.c $(TESTCASES_DIR)aes_cbc_crypter_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)diffie_hellman_test.o +$(BUILD_DIR)diffie_hellman_test.o : $(TESTCASES_DIR)diffie_hellman_test.c $(TESTCASES_DIR)diffie_hellman_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)event_queue_test.o +$(BUILD_DIR)event_queue_test.o : $(TESTCASES_DIR)event_queue_test.c $(TESTCASES_DIR)event_queue_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)generator_test.o +$(BUILD_DIR)generator_test.o : $(TESTCASES_DIR)generator_test.c $(TESTCASES_DIR)generator_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)ike_sa_id_test.o +$(BUILD_DIR)ike_sa_id_test.o : $(TESTCASES_DIR)ike_sa_id_test.c $(TESTCASES_DIR)ike_sa_id_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)job_queue_test.o +$(BUILD_DIR)job_queue_test.o : $(TESTCASES_DIR)job_queue_test.c $(TESTCASES_DIR)job_queue_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)parser_test.o +$(BUILD_DIR)parser_test.o : $(TESTCASES_DIR)parser_test.c $(TESTCASES_DIR)parser_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)hasher_test.o +$(BUILD_DIR)hasher_test.o : $(TESTCASES_DIR)hasher_test.c $(TESTCASES_DIR)hasher_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)ike_sa_manager_test.o +$(BUILD_DIR)ike_sa_manager_test.o : $(TESTCASES_DIR)ike_sa_manager_test.c $(TESTCASES_DIR)ike_sa_manager_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)linked_list_test.o +$(BUILD_DIR)linked_list_test.o : $(TESTCASES_DIR)linked_list_test.c $(TESTCASES_DIR)linked_list_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)hmac_test.o +$(BUILD_DIR)hmac_test.o : $(TESTCASES_DIR)hmac_test.c $(TESTCASES_DIR)hmac_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)hmac_signer_test.o +$(BUILD_DIR)hmac_signer_test.o : $(TESTCASES_DIR)hmac_signer_test.c $(TESTCASES_DIR)hmac_signer_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)scheduler_test.o +$(BUILD_DIR)scheduler_test.o : $(TESTCASES_DIR)scheduler_test.c $(TESTCASES_DIR)scheduler_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)prf_plus_test.o +$(BUILD_DIR)prf_plus_test.o : $(TESTCASES_DIR)prf_plus_test.c $(TESTCASES_DIR)prf_plus_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)send_queue_test.o +$(BUILD_DIR)send_queue_test.o : $(TESTCASES_DIR)send_queue_test.c $(TESTCASES_DIR)send_queue_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)socket_test.o +$(BUILD_DIR)socket_test.o : $(TESTCASES_DIR)socket_test.c $(TESTCASES_DIR)socket_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)packet_test.o +$(BUILD_DIR)packet_test.o : $(TESTCASES_DIR)packet_test.c $(TESTCASES_DIR)packet_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)ike_sa_test.o +$(BUILD_DIR)ike_sa_test.o : $(TESTCASES_DIR)ike_sa_test.c $(TESTCASES_DIR)ike_sa_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)sender_test.o +$(BUILD_DIR)sender_test.o : $(TESTCASES_DIR)sender_test.c $(TESTCASES_DIR)sender_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)thread_pool_test.o +$(BUILD_DIR)thread_pool_test.o : $(TESTCASES_DIR)thread_pool_test.c $(TESTCASES_DIR)thread_pool_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)encryption_payload_test.o +$(BUILD_DIR)encryption_payload_test.o : $(TESTCASES_DIR)encryption_payload_test.c $(TESTCASES_DIR)encryption_payload_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)connection_test.o +$(BUILD_DIR)connection_test.o : $(TESTCASES_DIR)connection_test.c $(TESTCASES_DIR)connection_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)policy_test.o +$(BUILD_DIR)policy_test.o : $(TESTCASES_DIR)policy_test.c $(TESTCASES_DIR)policy_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)proposal_test.o +$(BUILD_DIR)proposal_test.o : $(TESTCASES_DIR)proposal_test.c $(TESTCASES_DIR)proposal_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)rsa_test.o +$(BUILD_DIR)rsa_test.o : $(TESTCASES_DIR)rsa_test.c $(TESTCASES_DIR)rsa_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)kernel_interface_test.o +$(BUILD_DIR)kernel_interface_test.o : $(TESTCASES_DIR)kernel_interface_test.c $(TESTCASES_DIR)kernel_interface_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)child_sa_test.o +$(BUILD_DIR)child_sa_test.o : $(TESTCASES_DIR)child_sa_test.c $(TESTCASES_DIR)child_sa_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)certificate_test.o +$(BUILD_DIR)certificate_test.o : $(TESTCASES_DIR)certificate_test.c $(TESTCASES_DIR)certificate_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)leak_detective_test.o +$(BUILD_DIR)leak_detective_test.o : $(TESTCASES_DIR)leak_detective_test.c $(TESTCASES_DIR)leak_detective_test.h + $(CC) $(CFLAGS) -c -o $@ $< + +TEST_OBJS+= $(BUILD_DIR)identification_test.o +$(BUILD_DIR)identification_test.o : $(TESTCASES_DIR)identification_test.c $(TESTCASES_DIR)identification_test.h + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/src/charon/testing/aes_cbc_crypter_test.c b/src/charon/testing/aes_cbc_crypter_test.c new file mode 100644 index 000000000..30dae3926 --- /dev/null +++ b/src/charon/testing/aes_cbc_crypter_test.c @@ -0,0 +1,201 @@ +/** + * @file aes_cbc_crypter_test.c + * + * @brief Tests for the aes_cbc_crypter_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "aes_cbc_crypter_test.h" + +#include + +void test_aes_cbc_crypter(protected_tester_t *tester) +{ + /* + * Test 1 of RFC3602 + * Key : 0x06a9214036b8a15b512e03d534120006 + * IV : 0x3dafba429d9eb430b422da802c9fac41 + * Plaintext : "Single block msg" + * Ciphertext: 0xe353779c1079aeb82708942dbe77181a + */ + crypter_t *crypter; + u_int8_t key1[] = {0x06,0xa9,0x21,0x40,0x36,0xb8,0xa1,0x5b, + 0x51,0x2e,0x03,0xd5,0x34,0x12,0x00,0x06}; + chunk_t key1_chunk = {ptr: key1, len : 16}; + u_int8_t iv1[] = {0x3d,0xaf,0xba,0x42,0x9d,0x9e,0xb4,0x30, + 0xb4,0x22,0xda,0x80,0x2c,0x9f,0xac,0x41}; + chunk_t iv1_chunk = {ptr: iv1, len : 16}; + u_int8_t ciphertext1[] = { 0xe3,0x53,0x77,0x9c,0x10,0x79,0xae,0xb8, + 0x27,0x08,0x94,0x2d,0xbe,0x77,0x18,0x1a}; + + chunk_t expected_encrypted1 = {ptr: ciphertext1, len : 16}; + char * plaintext1 = "Single block msg"; + chunk_t data1 = {ptr: plaintext1, len : 16}; + chunk_t encrypted1; + chunk_t decrypted1; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + crypter = (crypter_t *) aes_cbc_crypter_create(16); + tester->assert_true(tester, (crypter != NULL), "create call test"); + + tester->assert_true(tester, (crypter->set_key(crypter,key1_chunk) == SUCCESS), "set_key call test"); + + tester->assert_true(tester, (crypter->encrypt(crypter,data1,iv1_chunk,&encrypted1) == SUCCESS), "encrypt call test"); + + tester->assert_true(tester, (memcmp(encrypted1.ptr, expected_encrypted1.ptr, 16) == 0), "Encrypted value"); + + logger->log_chunk(logger,RAW,"exptected encrypted :", expected_encrypted1); + logger->log_chunk(logger,RAW,"encrypted :", encrypted1); + + tester->assert_true(tester, (crypter->decrypt(crypter,encrypted1,iv1_chunk,&decrypted1) == SUCCESS), "decrypt call test"); + chunk_free(&encrypted1); + + tester->assert_true(tester, (memcmp(decrypted1.ptr, plaintext1, 16) == 0), "decrypted value"); + + logger->log_chunk(logger,RAW,"expected decrypted :", data1); + logger->log_chunk(logger,RAW,"decrypted :", decrypted1); + + chunk_free(&decrypted1); + + crypter->destroy(crypter); + + + /* + * Test 2 of RFC3602 + * Key : 0xc286696d887c9aa0611bbb3e2025a45a + * IV : 0x562e17996d093d28ddb3ba695a2e6f58 + * Plaintext : 0x000102030405060708090a0b0c0d0e0f + * 101112131415161718191a1b1c1d1e1f + * Ciphertext: 0xd296cd94c2cccf8a3a863028b5e1dc0a + * 7586602d253cfff91b8266bea6d61ab1 + */ + u_int8_t key2[] = {0xc2,0x86,0x69,0x6d,0x88,0x7c,0x9a,0xa0, + 0x61,0x1b,0xbb,0x3e,0x20,0x25,0xa4,0x5a}; + chunk_t key2_chunk = {ptr: key2, len : 16}; + u_int8_t iv2[] = {0x56,0x2e,0x17,0x99,0x6d,0x09,0x3d,0x28, + 0xdd,0xb3,0xba,0x69,0x5a,0x2e,0x6f,0x58}; + chunk_t iv2_chunk = {ptr: iv2, len : 16}; + u_int8_t ciphertext2[] = { 0xd2,0x96,0xcd,0x94,0xc2,0xcc,0xcf,0x8a, + 0x3a,0x86,0x30,0x28,0xb5,0xe1,0xdc,0x0a, + 0x75,0x86,0x60,0x2d,0x25,0x3c,0xff,0xf9, + 0x1b,0x82,0x66,0xbe,0xa6,0xd6,0x1a,0xb1}; + + chunk_t expected_encrypted2 = {ptr: ciphertext2, len : 32}; + u_int8_t plaintext2[] = {0x00,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,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f}; + chunk_t data2 = {ptr: plaintext2, len : 32}; + chunk_t encrypted2; + chunk_t decrypted2; + + + crypter = (crypter_t *) aes_cbc_crypter_create(16); + tester->assert_true(tester, (crypter != NULL), "create call test"); + + tester->assert_true(tester, (crypter->set_key(crypter,key2_chunk) == SUCCESS), "set_key call test"); + + tester->assert_true(tester, (crypter->encrypt(crypter,data2,iv2_chunk,&encrypted2) == SUCCESS), "encrypt call test"); + + tester->assert_true(tester, (memcmp(encrypted2.ptr, expected_encrypted2.ptr, 32) == 0), "Encrypted value"); + + logger->log_chunk(logger,RAW,"exptected encrypted :", expected_encrypted2); + logger->log_chunk(logger,RAW,"encrypted :", encrypted2); + + tester->assert_true(tester, (crypter->decrypt(crypter,encrypted2,iv2_chunk,&decrypted2) == SUCCESS), "decrypt call test"); + chunk_free(&encrypted2); + + tester->assert_true(tester, (memcmp(decrypted2.ptr, plaintext2, 32) == 0), "decrypted value"); + + logger->log_chunk(logger,RAW,"expected decrypted :", data2); + logger->log_chunk(logger,RAW,"decrypted :", decrypted2); + + chunk_free(&decrypted2); + + crypter->destroy(crypter); + + /* + * Test 3 of RFC3603 + * Key : 0x56e47a38c5598974bc46903dba290349 + * IV : 0x8ce82eefbea0da3c44699ed7db51b7d9 + * Plaintext : 0xa0a1a2a3a4a5a6a7a8a9aaabacadaeaf + * b0b1b2b3b4b5b6b7b8b9babbbcbdbebf + * c0c1c2c3c4c5c6c7c8c9cacbcccdcecf + * d0d1d2d3d4d5d6d7d8d9dadbdcdddedf + * Ciphertext: 0xc30e32ffedc0774e6aff6af0869f71aa + * 0f3af07a9a31a9c684db207eb0ef8e4e + * 35907aa632c3ffdf868bb7b29d3d46ad + * 83ce9f9a102ee99d49a53e87f4c3da55 + */ + u_int8_t key3[] = {0x56,0xe4,0x7a,0x38,0xc5,0x59,0x89,0x74, + 0xbc,0x46,0x90,0x3d,0xba,0x29,0x03,0x49}; + chunk_t key3_chunk = {ptr: key3, len : 16}; + u_int8_t iv3[] = {0x8c,0xe8,0x2e,0xef,0xbe,0xa0,0xda,0x3c, + 0x44,0x69,0x9e,0xd7,0xdb,0x51,0xb7,0xd9}; + chunk_t iv3_chunk = {ptr: iv3, len : 16}; + u_int8_t ciphertext3[] = { 0xc3,0x0e,0x32,0xff,0xed,0xc0,0x77,0x4e, + 0x6a,0xff,0x6a,0xf0,0x86,0x9f,0x71,0xaa, + 0x0f,0x3a,0xf0,0x7a,0x9a,0x31,0xa9,0xc6, + 0x84,0xdb,0x20,0x7e,0xb0,0xef,0x8e,0x4e, + 0x35,0x90,0x7a,0xa6,0x32,0xc3,0xff,0xdf, + 0x86,0x8b,0xb7,0xb2,0x9d,0x3d,0x46,0xad, + 0x83,0xce,0x9f,0x9a,0x10,0x2e,0xe9,0x9d, + 0x49,0xa5,0x3e,0x87,0xf4,0xc3,0xda,0x55}; + + chunk_t expected_encrypted3 = {ptr: ciphertext3, len : 64}; + u_int8_t plaintext3[] = {0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, + 0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, + 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7, + 0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf, + 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, + 0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, + 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7, + 0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf}; + chunk_t data3 = {ptr: plaintext3, len : 64}; + chunk_t encrypted3; + chunk_t decrypted3; + + crypter = (crypter_t *) aes_cbc_crypter_create(16); + tester->assert_true(tester, (crypter != NULL), "create call test"); + + tester->assert_true(tester, (crypter->set_key(crypter,key3_chunk) == SUCCESS), "set_key call test"); + + tester->assert_true(tester, (crypter->encrypt(crypter,data3,iv3_chunk,&encrypted3) == SUCCESS), "encrypt call test"); + + tester->assert_true(tester, (memcmp(encrypted3.ptr, expected_encrypted3.ptr, 64) == 0), "Encrypted value"); + + logger->log_chunk(logger,RAW,"exptected encrypted :", expected_encrypted3); + logger->log_chunk(logger,RAW,"encrypted :", encrypted3); + + tester->assert_true(tester, (crypter->decrypt(crypter,encrypted3,iv3_chunk,&decrypted3) == SUCCESS), "decrypt call test"); + chunk_free(&encrypted3); + + tester->assert_true(tester, (memcmp(decrypted3.ptr, plaintext3, 64) == 0), "decrypted value"); + + logger->log_chunk(logger,RAW,"expected decrypted :", data3); + logger->log_chunk(logger,RAW,"decrypted :", decrypted3); + + chunk_free(&decrypted3); + + crypter->destroy(crypter); +} + diff --git a/src/charon/testing/aes_cbc_crypter_test.h b/src/charon/testing/aes_cbc_crypter_test.h new file mode 100644 index 000000000..c3897a4d6 --- /dev/null +++ b/src/charon/testing/aes_cbc_crypter_test.h @@ -0,0 +1,38 @@ +/** + * @file aes_cbc_crypter_test.h + * + * @brief Tests for the aes_cbc_crypter_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 AES_CBC_CRYPTER_TEST_H_ +#define AES_CBC_CRYPTER_TEST_H_ + +#include +#include + +/** + * @brief Test function used to test the aes_cbc_crypter_t class. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_aes_cbc_crypter(protected_tester_t *tester); + +#endif /* AES_CBC_CRYPTER_TEST_H_ */ diff --git a/src/charon/testing/certificate_test.c b/src/charon/testing/certificate_test.c new file mode 100644 index 000000000..be8a8f7cf --- /dev/null +++ b/src/charon/testing/certificate_test.c @@ -0,0 +1,112 @@ +/** + * @file certificate_test.c + * + * @brief Tests for the certificate_t class. + * + */ + +/* + * 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 "certificate_test.h" + +#include +#include +#include + + +static char certificate_buffer[] = { + 0x30,0x82,0x02,0xf9,0x30,0x82,0x01,0xe1,0xa0,0x03,0x02,0x01,0x02,0x02,0x11,0x00, + 0xfe,0xae,0xe3,0xcf,0x00,0x27,0x8d,0xa0,0xe1,0xfa,0xb2,0x07,0xd4,0x15,0x40,0x93, + 0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x05,0x05,0x00,0x30, + 0x38,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x19, + 0x30,0x17,0x06,0x03,0x55,0x04,0x0a,0x13,0x10,0x4c,0x69,0x6e,0x75,0x78,0x20,0x73, + 0x74,0x72,0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,0x0e,0x30,0x0c,0x06,0x03,0x55, + 0x04,0x03,0x13,0x05,0x6d,0x61,0x65,0x6e,0x6f,0x30,0x1e,0x17,0x0d,0x30,0x36,0x30, + 0x33,0x32,0x37,0x30,0x36,0x35,0x32,0x33,0x38,0x5a,0x17,0x0d,0x31,0x31,0x30,0x33, + 0x32,0x36,0x30,0x36,0x35,0x32,0x33,0x38,0x5a,0x30,0x38,0x31,0x0b,0x30,0x09,0x06, + 0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04, + 0x0a,0x13,0x10,0x4c,0x69,0x6e,0x75,0x78,0x20,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53, + 0x77,0x61,0x6e,0x31,0x0e,0x30,0x0c,0x06,0x03,0x55,0x04,0x03,0x13,0x05,0x6d,0x61, + 0x65,0x6e,0x6f,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7, + 0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02, + 0x82,0x01,0x01,0x00,0xe3,0x75,0x56,0xb9,0x68,0x46,0xa6,0x3e,0x6c,0x19,0x36,0xfb, + 0x9a,0xb4,0xbc,0xc1,0x22,0x47,0xc0,0x00,0x8a,0x44,0x1c,0xa7,0x44,0x2e,0x73,0x50, + 0xfc,0xd2,0x91,0x9c,0xaa,0xc3,0xa3,0x88,0x8c,0x4b,0x33,0xef,0x9a,0x52,0x89,0x9c, + 0x8e,0x01,0x62,0x21,0x7a,0x75,0x5e,0xa3,0x3b,0xc0,0xb0,0x58,0xc0,0xc0,0xce,0x77, + 0xe0,0x84,0x9a,0x9e,0xc1,0x51,0x71,0xc7,0xc4,0xa0,0x1e,0xf0,0x8e,0xb3,0x90,0x3e, + 0xcd,0xe3,0x7d,0x8e,0x11,0x7b,0x92,0x5d,0x4a,0x37,0x3b,0x4b,0xb3,0x3d,0x58,0x9a, + 0x8b,0x51,0x39,0x15,0xcd,0x27,0xd4,0x5b,0xad,0x5e,0xa5,0x07,0x94,0x29,0x0f,0x02, + 0x0c,0x61,0x85,0x97,0x3b,0xc4,0xcf,0x5d,0x17,0x86,0x4d,0x96,0x5e,0x42,0xe9,0xf2, + 0x72,0x2f,0xd4,0x58,0x4d,0x02,0xf8,0x0f,0xbd,0xe7,0x37,0xc8,0xa9,0x87,0xfe,0xab, + 0x26,0x37,0x13,0x90,0x65,0x2d,0x51,0x41,0x18,0x18,0xdf,0x48,0x21,0x87,0x70,0x61, + 0xcb,0x1b,0x62,0xad,0xaf,0x65,0xd2,0x29,0x27,0x93,0x58,0x7b,0xea,0x89,0xdd,0x58, + 0x01,0x6d,0xeb,0x60,0xd8,0xc3,0x82,0x07,0x2c,0x67,0x39,0xc3,0x68,0xfc,0xcd,0xeb, + 0xe9,0x7c,0x67,0xe3,0x1b,0x7a,0x50,0xf9,0x36,0x68,0xea,0xe2,0x15,0x01,0xee,0x99, + 0xf2,0x52,0xe0,0x0a,0x8e,0x5f,0x63,0xb1,0x61,0x7a,0x38,0x88,0x07,0xae,0xb0,0x8d, + 0x44,0x26,0xe8,0xce,0x1b,0x6f,0xcd,0x05,0x4b,0x94,0x9d,0xee,0xb5,0xeb,0x28,0xc4, + 0x93,0x47,0xfd,0x47,0x40,0x45,0x58,0xc0,0x3e,0x44,0x74,0x7b,0x78,0x8d,0xc8,0x25, + 0xc1,0xe1,0x0a,0x43,0x02,0x03,0x01,0x00,0x01,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48, + 0x86,0xf7,0x0d,0x01,0x01,0x05,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x77,0xfd,0xd2, + 0x68,0x7e,0xb9,0xc2,0x40,0xb4,0xa3,0xea,0xe8,0x15,0x55,0x18,0xfe,0xe3,0x80,0xe0, + 0x73,0xf9,0xe1,0xe5,0xe2,0x91,0xf5,0xa7,0xcb,0xdf,0xfb,0xc1,0x36,0xa6,0x55,0x6a, + 0xd9,0x27,0xcd,0xef,0x64,0x30,0x70,0xd8,0x4b,0x72,0x7c,0xd1,0x9c,0x32,0xf8,0xb4, + 0x15,0x7f,0xd7,0x79,0x0c,0x9f,0x24,0xf8,0x50,0xea,0xc7,0xd9,0xef,0x1f,0xf1,0x76, + 0x3c,0x19,0xdb,0x61,0xb7,0x35,0x97,0xf9,0x03,0x87,0x42,0x77,0x23,0xd8,0xfe,0xd1, + 0x74,0xf2,0x1e,0x95,0x87,0x5f,0x42,0x80,0x8e,0xee,0x6c,0x19,0x7b,0x2c,0x25,0xe6, + 0xf9,0xdb,0x24,0x35,0x94,0x65,0x44,0xa0,0x56,0x6f,0x7f,0x57,0x2e,0x1a,0xcd,0xa6, + 0xed,0x7f,0x42,0xf2,0x64,0xd4,0xf9,0x3f,0xc1,0x46,0xf6,0xc8,0xb1,0xb2,0x80,0x75, + 0x3e,0xd1,0xa8,0x5e,0x07,0xd0,0x3b,0x35,0x81,0x49,0x93,0x77,0xd2,0xcf,0xf7,0xb6, + 0xd0,0xeb,0xe5,0xf3,0x2c,0x03,0x52,0xc7,0x6d,0x02,0x26,0xa6,0xdc,0x39,0xcd,0x4d, + 0x9e,0xca,0x99,0x01,0x01,0x73,0xd6,0x55,0x89,0x93,0x12,0xa0,0xc5,0xe6,0xa7,0x9a, + 0xdc,0x5f,0x9f,0x5c,0x2c,0x2b,0xdb,0x23,0xa5,0xee,0x69,0x15,0x1f,0x3a,0xf1,0x76, + 0x36,0xb5,0x77,0x18,0x57,0xff,0xff,0xf7,0x45,0x59,0xce,0x1b,0x0b,0x56,0xcb,0x09, + 0x00,0x12,0x17,0xb8,0xa2,0x81,0x86,0x70,0x29,0x63,0x99,0x76,0xff,0x18,0x80,0x2b, + 0x9b,0x5e,0x04,0xb1,0xcc,0xe4,0x15,0x90,0x29,0xa6,0x40,0xdd,0x85,0x38,0xd7,0xfe, + 0x10,0xb5,0x97,0x6e,0x62,0x60,0xb9,0x02,0x67,0xef,0xf1,0xab,0xb3, +}; + +/** + * Described in header. + */ +void test_certificate(protected_tester_t *tester) +{ + chunk_t certificate = {certificate_buffer, sizeof(certificate_buffer)}; + identification_t *id; + x509_t *cert; + + cert = x509_create_from_chunk(certificate); + id = cert->get_subject(cert); + tester->assert_true(tester, strcmp(id->get_string(id), "C=CH, O=Linux strongSwan, CN=maeno") == 0, "subject"); + id = cert->get_issuer(cert); + tester->assert_true(tester, strcmp(id->get_string(id), "C=CH, O=Linux strongSwan, CN=maeno") == 0, "issuer"); + cert->destroy(cert); + /* + cert = x509_create_from_file("scripts/complex1.der"); + id = cert->get_subject(cert); + printf("Subject: %s\n", id->get_string(id)); + id = cert->get_issuer(cert); + printf("Issuer: %s\n", id->get_string(id)); + cert->destroy(cert); + + cert = x509_create_from_file("scripts/complex2.der"); + id = cert->get_subject(cert); + printf("Subject: %s\n", id->get_string(id)); + id = cert->get_issuer(cert); + printf("Issuer: %s\n", id->get_string(id)); + cert->destroy(cert);*/ +} diff --git a/src/charon/testing/certificate_test.h b/src/charon/testing/certificate_test.h new file mode 100644 index 000000000..8dcbd0f93 --- /dev/null +++ b/src/charon/testing/certificate_test.h @@ -0,0 +1,42 @@ +/** + * @file certificate_test.h + * + * @brief Tests for the certificate_t class. + * + */ + +/* + * 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 CERTIFICATE_TEST_H_ +#define CERTIFICATE_TEST_H_ + +#include + +/** + * @brief Test function used to test the certificate_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_certificate(protected_tester_t *tester); + +#endif /* CERTIFICATE_TEST_H_ */ + + + + diff --git a/src/charon/testing/child_sa_test.c b/src/charon/testing/child_sa_test.c new file mode 100644 index 000000000..0cf354c26 --- /dev/null +++ b/src/charon/testing/child_sa_test.c @@ -0,0 +1,101 @@ +/** + * @file child_sa_test.c + * + * @brief Tests for the child_sa_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_sa_test.h" + +#include +#include +#include + + +/** + * Described in header. + */ +void test_child_sa(protected_tester_t *tester) +{ + proposal_t *proposal1, *proposal2; + linked_list_t *list; + host_t *local_me, *remote_me; + host_t *local_other, *remote_other; + child_sa_t *local_sa, *remote_sa; + prf_plus_t *local_prf_plus, *remote_prf_plus; + prf_t *local_prf, *remote_prf; + u_int8_t key_buffer[] = {0x01,0x02,0x03,0x04}; + chunk_t key = {key_buffer, sizeof(key_buffer)}; + status_t status; + + /* setup test data */ + local_me = host_create(AF_INET, "192.168.0.1", 0); + local_other = host_create(AF_INET, "192.168.0.2", 0); + remote_me = host_create(AF_INET, "192.168.0.3", 0); + remote_other = host_create(AF_INET, "192.168.0.4", 0); + + local_sa = child_sa_create(local_me, local_other); + remote_sa = child_sa_create(remote_me, remote_other); + + proposal1 = proposal_create(1); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + + proposal2 = proposal_create(2); + proposal2->add_algorithm(proposal2, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0); + + list = linked_list_create(); + list->insert_last(list, proposal1); + list->insert_last(list, proposal2); + + local_prf = prf_create(PRF_HMAC_SHA1); + remote_prf = prf_create(PRF_HMAC_SHA1); + local_prf->set_key(local_prf, key); + remote_prf->set_key(remote_prf, key); + local_prf_plus = prf_plus_create(local_prf, key); + remote_prf_plus = prf_plus_create(remote_prf, key); + + /* + * local plays initiator + *********************** + */ + status = local_sa->alloc(local_sa, list); + tester->assert_true(tester, status == SUCCESS, "spi allocation"); + + status = remote_sa->add(remote_sa, proposal1, remote_prf_plus); + tester->assert_true(tester, status == SUCCESS, "sa add"); + + status = local_sa->update(local_sa, proposal1, local_prf_plus); + tester->assert_true(tester, status == SUCCESS, "sa update"); + + /* cleanup */ + proposal1->destroy(proposal1); + proposal2->destroy(proposal2); + list->destroy(list); + local_prf->destroy(local_prf); + local_prf_plus->destroy(local_prf_plus); + remote_prf->destroy(remote_prf); + remote_prf_plus->destroy(remote_prf_plus); + local_sa->destroy(local_sa); + remote_sa->destroy(remote_sa); + local_me->destroy(local_me); + local_other->destroy(local_other); + remote_me->destroy(remote_me); + remote_other->destroy(remote_other); + + +} diff --git a/src/charon/testing/child_sa_test.h b/src/charon/testing/child_sa_test.h new file mode 100644 index 000000000..ef92499fe --- /dev/null +++ b/src/charon/testing/child_sa_test.h @@ -0,0 +1,42 @@ +/** + * @file child_sa_test.h + * + * @brief Tests for the child_sa_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_TEST_H_ +#define CHILD_SA_TEST_H_ + +#include + +/** + * @brief Test function used to test the child_sa_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_child_sa(protected_tester_t *tester); + +#endif /* CHILD_SA_TEST_H_ */ + + + + diff --git a/src/charon/testing/connection_test.c b/src/charon/testing/connection_test.c new file mode 100644 index 000000000..6b12afc1d --- /dev/null +++ b/src/charon/testing/connection_test.c @@ -0,0 +1,82 @@ +/** + * @file connection_test.c + * + * @brief Tests for the connection_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "connection_test.h" + +#include +#include + + +/** + * Described in header. + */ +void test_connection(protected_tester_t *tester) +{ + host_t *alice = host_create(AF_INET, "192.168.0.1", 500); + host_t *bob = host_create(AF_INET, "192.168.0.2", 500); + identification_t *alice_id = identification_create_from_string("192.168.0.1"); + identification_t *bob_id = identification_create_from_string("192.168.0.2"); + connection_t *connection = connection_create(alice, bob, alice_id, bob_id, RSA_DIGITAL_SIGNATURE); + proposal_t *prop1, *prop2, *prop3, *prop4; + linked_list_t *list; + + prop1 = proposal_create(1); + prop1->add_algorithm(prop1, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 20); + prop1->add_algorithm(prop1, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + prop1->add_algorithm(prop1, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 20); + prop1->add_algorithm(prop1, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + + prop2 = proposal_create(2); + prop2->add_algorithm(prop2, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 20); + prop2->add_algorithm(prop2, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + prop2->add_algorithm(prop2, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 20); + prop2->add_algorithm(prop2, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + + prop3 = proposal_create(3); + prop3->add_algorithm(prop3, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_DES, 20); + prop3->add_algorithm(prop3, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + prop3->add_algorithm(prop3, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 20); + prop3->add_algorithm(prop3, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_768_BIT, 0); + + prop4 = proposal_create(4); + prop4->add_algorithm(prop4, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_3DES, 20); + prop4->add_algorithm(prop4, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + prop4->add_algorithm(prop4, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_TIGER, 20); + prop4->add_algorithm(prop4, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_768_BIT, 0); + + connection->add_proposal(connection, prop1); + connection->add_proposal(connection, prop2); + connection->add_proposal(connection, prop3); + connection->add_proposal(connection, prop4); + + list = connection->get_proposals(connection); + + tester->assert_true(tester,(list->get_count(list) == 4), "proposal count check "); + + + /* going to check proposals */ + /* TODO test?*/ + + list->destroy(list); + + connection->destroy(connection); +} diff --git a/src/charon/testing/connection_test.h b/src/charon/testing/connection_test.h new file mode 100644 index 000000000..4d2a1d89e --- /dev/null +++ b/src/charon/testing/connection_test.h @@ -0,0 +1,38 @@ +/** + * @file connection_test.h + * + * @brief Tests for the connection_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 INIT_CONFIG_TEST_H_ +#define INIT_CONFIG_TEST_H_ + +#include + +/** + * @brief Test function used to test the connection_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_connection(protected_tester_t *tester); + +#endif /* INIT_CONFIG_TEST_H_ */ diff --git a/src/charon/testing/diffie_hellman_test.c b/src/charon/testing/diffie_hellman_test.c new file mode 100644 index 000000000..0a44a022a --- /dev/null +++ b/src/charon/testing/diffie_hellman_test.c @@ -0,0 +1,76 @@ +/** + * @file diffie_hellman_test.c + * + * @brief Tests for the diffie_hellman_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "diffie_hellman_test.h" + +#include +#include +#include +#include + +/* + * described in Header-File + */ +void test_diffie_hellman(protected_tester_t *tester) +{ + diffie_hellman_t *my_diffie_hellman, *other_diffie_hellman; + logger_t *logger; + chunk_t my_public_value, other_public_value; + chunk_t my_secret, other_secret; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + + my_diffie_hellman = diffie_hellman_create(MODP_1024_BIT); + tester->assert_true(tester,(my_diffie_hellman != NULL), "create call check"); + + other_diffie_hellman = diffie_hellman_create(MODP_1024_BIT); + tester->assert_true(tester,(other_diffie_hellman != NULL), "create call check"); + + my_diffie_hellman->get_my_public_value(my_diffie_hellman,&my_public_value); + logger->log_chunk(logger,RAW,"My public value",my_public_value); + + other_diffie_hellman->get_my_public_value(other_diffie_hellman,&other_public_value); + logger->log_chunk(logger,RAW,"Other public value",other_public_value); + + my_diffie_hellman->set_other_public_value(my_diffie_hellman,other_public_value); + other_diffie_hellman->set_other_public_value(other_diffie_hellman,my_public_value); + + free(my_public_value.ptr); + free(other_public_value.ptr); + + tester->assert_true(tester,(my_diffie_hellman->get_shared_secret(my_diffie_hellman,&my_secret) == SUCCESS), "get_shared_secret call check"); + logger->log_chunk(logger,RAW,"My shared secret",my_secret); + + tester->assert_true(tester,(other_diffie_hellman->get_shared_secret(other_diffie_hellman,&other_secret) == SUCCESS), "get_shared_secret call check"); + logger->log_chunk(logger,RAW,"Other shared secret",other_secret); + + tester->assert_true(tester,(memcmp(my_secret.ptr,other_secret.ptr,other_secret.len) == 0), "shared secret same value check"); + + free(my_secret.ptr); + free(other_secret.ptr); + + my_diffie_hellman->destroy(my_diffie_hellman); + other_diffie_hellman->destroy(other_diffie_hellman); +} diff --git a/src/charon/testing/diffie_hellman_test.h b/src/charon/testing/diffie_hellman_test.h new file mode 100644 index 000000000..e6e3ff608 --- /dev/null +++ b/src/charon/testing/diffie_hellman_test.h @@ -0,0 +1,37 @@ +/** + * @file diffie_hellman_test.h + * + * @brief Tests for the diffie_hellman_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 DIFFIE_HELLMAN_TEST_H_ +#define DIFFIE_HELLMAN_TEST_H_ + +#include + +/** + * @brief Test function used to test the diffie_hellman_t functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_diffie_hellman(protected_tester_t *tester); + +#endif /*DIFFIE_HELLMAN_TEST_H_*/ diff --git a/src/charon/testing/encryption_payload_test.c b/src/charon/testing/encryption_payload_test.c new file mode 100644 index 000000000..9e4108b9e --- /dev/null +++ b/src/charon/testing/encryption_payload_test.c @@ -0,0 +1,139 @@ +/** + * @file encryption_payload_test.c + * + * @brief Tests for the encryption_payload_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "encryption_payload_test.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * described in Header-File + */ +void test_encryption_payload(protected_tester_t *tester) +{ + encryption_payload_t *encryption_payload; + nonce_payload_t *nonce_payload; + crypter_t *crypter; + signer_t *signer; + chunk_t nonce, got_nonce; + chunk_t data; + chunk_t key; + generator_t *generator; + parser_t *parser; + status_t status; + logger_t *logger; + iterator_t *iterator; + + + u_int8_t key_bytes[] = { + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01 + }; + key.ptr = key_bytes; + key.len = sizeof(key_bytes); + + logger = logger_manager->get_logger(logger_manager, TESTER); + + nonce.ptr = "test text und so..."; + nonce.len = strlen(nonce.ptr) + 1; + + logger->log_chunk(logger, RAW, "nonce", nonce); + + encryption_payload = encryption_payload_create(); + nonce_payload = nonce_payload_create(); + nonce_payload->set_nonce(nonce_payload, nonce); + + encryption_payload->add_payload(encryption_payload, (payload_t*)nonce_payload); + signer = signer_create(AUTH_HMAC_SHA1_96); + crypter = crypter_create(ENCR_AES_CBC, 16); + + signer->set_key(signer, key); + crypter->set_key(crypter, key); + + + + /* generating */ + + encryption_payload->set_transforms(encryption_payload, crypter, signer); + + logger->log(logger, RAW, "encrypt"); + status = encryption_payload->encrypt(encryption_payload); + tester->assert_true(tester, (status == SUCCESS), "encryption"); + + generator = generator_create(); + generator->generate_payload(generator, (payload_t*)encryption_payload); + + generator->write_to_chunk(generator, &data); + logger->log_chunk(logger, RAW, "generated data", data); + + encryption_payload->build_signature(encryption_payload, data); + logger->log_chunk(logger, RAW, "generated data", data); + + encryption_payload->destroy(encryption_payload); + + + /* parsing */ + + parser = parser_create(data); + status = parser->parse_payload(parser, ENCRYPTED, (payload_t**)&encryption_payload); + tester->assert_true(tester, (status == SUCCESS), "parsing"); + + encryption_payload->set_transforms(encryption_payload, crypter, signer); + status = encryption_payload->verify_signature(encryption_payload, data); + tester->assert_true(tester, (status == SUCCESS), "signature verification"); + + status = encryption_payload->decrypt(encryption_payload); + tester->assert_true(tester, (status == SUCCESS), "decryption"); + + + iterator = encryption_payload->create_payload_iterator(encryption_payload, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&nonce_payload); + got_nonce = nonce_payload->get_nonce(nonce_payload); + } + iterator->destroy(iterator); + + + tester->assert_true(tester, (got_nonce.len == nonce.len), "decrypted nonce"); + tester->assert_false(tester, memcmp(nonce.ptr, got_nonce.ptr, nonce.len), "decrypted nonce"); + + logger->log_chunk(logger, RAW, "nonce", got_nonce); + + free(data.ptr); + free(got_nonce.ptr); + encryption_payload->destroy(encryption_payload); + crypter->destroy(crypter); + signer->destroy(signer); + generator->destroy(generator); + parser->destroy(parser); +} diff --git a/src/charon/testing/encryption_payload_test.h b/src/charon/testing/encryption_payload_test.h new file mode 100644 index 000000000..5e6353bfd --- /dev/null +++ b/src/charon/testing/encryption_payload_test.h @@ -0,0 +1,37 @@ +/** + * @file encryption_payload_test.h + * + * @brief Tests for the encryption_payload_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 ENCRYPTION_PAYLOAD_TEST_H_ +#define ENCRYPTION_PAYLOAD_TEST_H_ + +#include + +/** + * @brief Test function used to test the encryption_payload_t functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_encryption_payload(protected_tester_t *tester); + +#endif /*ENCRYPTION_PAYLOAD_TEST_H_*/ diff --git a/src/charon/testing/event_queue_test.c b/src/charon/testing/event_queue_test.c new file mode 100644 index 000000000..58a214051 --- /dev/null +++ b/src/charon/testing/event_queue_test.c @@ -0,0 +1,143 @@ +/** + * @file event_queue_test.h + * + * @brief Tests for the event_queue_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "event_queue_test.h" + +#include +#include + +/** + * Number of different times to insert per thread + */ +#define EVENT_QUEUE_TIMES 5 +/** + * Number of entries per time per thread + */ +#define EVENT_QUEUE_ENTRY_PER_TIME 20 + +/** + * Number of test-thread + */ +#define EVENT_QUEUE_INSERT_THREADS 1 + +/** + * @brief Informations for the involved test-thread used in this test + * + */ +typedef struct event_queue_test_s event_queue_test_t; + +struct event_queue_test_s{ + protected_tester_t *tester; + event_queue_t *event_queue; + + /** + * number of different event times to be inserted in the event-queue by each thread + */ + int insert_times_count; + + /** + * number of event to insert at one time + */ + int entries_per_time; +}; + + +static void event_queue_insert_thread(event_queue_test_t * testinfos) +{ + timeval_t current_time; + timeval_t time; + job_t * job; + int i,j; + connection_t *connection; + + gettimeofday(¤t_time,NULL); + for (i = 0; i < testinfos->insert_times_count;i++) + { + + for (j = 0; j < testinfos->entries_per_time;j++) + { + job = (job_t *) initiate_ike_sa_job_create(connection); + time.tv_usec = 0; + time.tv_sec = current_time.tv_sec + i; + + testinfos->event_queue->add_absolute(testinfos->event_queue,job,time); + } + } +} + + +void test_event_queue(protected_tester_t *tester) +{ + event_queue_t * event_queue = event_queue_create(); + event_queue_test_t testinfos; + pthread_t threads[EVENT_QUEUE_INSERT_THREADS]; + int i,j, number_of_total_events; + timeval_t current_time, start_time; + + testinfos.tester = tester; + testinfos.event_queue = event_queue; + testinfos.insert_times_count = EVENT_QUEUE_TIMES; + testinfos.entries_per_time = EVENT_QUEUE_ENTRY_PER_TIME; + + number_of_total_events = EVENT_QUEUE_ENTRY_PER_TIME * EVENT_QUEUE_TIMES * EVENT_QUEUE_INSERT_THREADS; + + gettimeofday(&start_time,NULL); + + for (i = 0; i < EVENT_QUEUE_INSERT_THREADS; i++) + { + int retval; + retval = pthread_create( &(threads[i]), NULL,(void*(*)(void*)) &event_queue_insert_thread, (void*) &testinfos); + tester->assert_true(tester,(retval== 0), "thread creation call check"); + } + + + /* wait for all threads */ + for (i = 0; i < EVENT_QUEUE_INSERT_THREADS; i++) + { + int retval; + retval = pthread_join(threads[i], NULL); + tester->assert_true(tester,(retval== 0), "thread creation call check"); + + } + + tester->assert_true(tester,(event_queue->get_count(event_queue) == number_of_total_events), "event count check"); + + for (i = 0; i < EVENT_QUEUE_TIMES;i++) + { + for (j = 0; j < (EVENT_QUEUE_ENTRY_PER_TIME * EVENT_QUEUE_INSERT_THREADS);j++) + { + job_t *job; + + job = event_queue->get(event_queue); + gettimeofday(¤t_time,NULL); + tester->assert_true(tester,((current_time.tv_sec - start_time.tv_sec) == i), "value of entry check"); + job->destroy(job); + } + } + + + event_queue->destroy(event_queue); + return; +} diff --git a/src/charon/testing/event_queue_test.h b/src/charon/testing/event_queue_test.h new file mode 100644 index 000000000..5f8c47fad --- /dev/null +++ b/src/charon/testing/event_queue_test.h @@ -0,0 +1,39 @@ +/** + * @file event_queue_test.h + * + * @brief Tests to test the Event-Queue type event_queue_t + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 EVENT_QUEUE_TEST_H_ +#define EVENT_QUEUE_TEST_H_ + +#include + +/** + * @brief Test function used to test the event_queue functionality. + * + * Tests are performed using one thread. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_event_queue(protected_tester_t *tester); + +#endif /*EVENT_QUEUE_TEST_H_*/ diff --git a/src/charon/testing/generator_test.c b/src/charon/testing/generator_test.c new file mode 100644 index 000000000..004c700e6 --- /dev/null +++ b/src/charon/testing/generator_test.c @@ -0,0 +1,1410 @@ +/** + * @file generator_test.c + * + * @brief Tests for the generator_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "generator_test.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Described in Header + */ +void test_generator_with_header_payload(protected_tester_t *tester) +{ + generator_t *generator; + ike_header_t *header_data; + chunk_t generated_data; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager, TESTER); + + header_data = ike_header_create(); + header_data->set_initiator_spi(header_data,1); + header_data->set_responder_spi(header_data,2); + ((payload_t *) header_data)->set_next_type((payload_t *) header_data, 3); + header_data->set_exchange_type(header_data, 6); + header_data->set_initiator_flag(header_data, TRUE); + header_data->set_response_flag(header_data, TRUE); + header_data->set_message_id(header_data,7); + + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + generator->generate_payload(generator,(payload_t *) header_data); + + generator->write_to_chunk(generator,&generated_data); + + u_int8_t expected_generation[] = { + 0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x03,0x20,0x06,0x28, + 0x00,0x00,0x00,0x07, + 0x00,0x00,0x00,0x1C, + }; + + logger->log_bytes(logger,RAW,"expected header",expected_generation,sizeof(expected_generation)); + tester->assert_true(tester,(generated_data.len == sizeof(expected_generation)), "compare generated data length"); + logger->log_chunk(logger,RAW,"generated header",generated_data); + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data 1"); + chunk_free(&generated_data); + + generator->destroy(generator); + + header_data->set_initiator_spi(header_data,0x22000054231234LL); + header_data->set_responder_spi(header_data,0x122398); + ((payload_t *) header_data)->set_next_type((payload_t *) header_data,0xF3); + header_data->set_exchange_type(header_data, 0x12); + header_data->set_initiator_flag(header_data, TRUE); + header_data->set_response_flag(header_data, TRUE); + header_data->set_message_id(header_data,0x33AFF3); + + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + generator->generate_payload(generator,(payload_t *)header_data); + + generator->write_to_chunk(generator,&generated_data); + + u_int8_t expected_generation2[] = { + 0x34,0x12,0x23,0x54, + 0x00,0x00,0x22,0x00, + 0x98,0x23,0x12,0x00, + 0x00,0x00,0x00,0x00, + 0xF3,0x20,0x12,0x28, + 0x00,0x33,0xAF,0xF3, + 0x00,0x00,0x00,0x1C, + }; + + + logger->log_bytes(logger,RAW,"expected header",expected_generation2,sizeof(expected_generation2)); + + logger->log_chunk(logger,RAW,"generated header",generated_data); + + tester->assert_true(tester,(memcmp(expected_generation2,generated_data.ptr,sizeof(expected_generation2)) == 0), "compare generated data 2"); + chunk_free(&generated_data); + + header_data->destroy(header_data); + + generator->destroy(generator); +} + +/* + * Described in header + */ +void test_generator_with_transform_attribute(protected_tester_t *tester) +{ + generator_t *generator; + transform_attribute_t *attribute; + chunk_t generated_data; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager, TESTER); + + + /* test empty attribute */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + attribute = transform_attribute_create(); + generator->generate_payload(generator,(payload_t *)attribute); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated attribute",generated_data); + + u_int8_t expected_generation[] = { + 0x80,0x00,0x00,0x00, + }; + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + chunk_free(&generated_data); + attribute->destroy(attribute); + generator->destroy(generator); + + /* test attribute with 2 byte data */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + attribute = transform_attribute_create(); + u_int16_t dataval = 5768; + chunk_t data; + data.ptr = (void *) &dataval; + data.len = 2; + + attribute->set_value_chunk(attribute,data); + + generator->generate_payload(generator,(payload_t *)attribute); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated attribute",generated_data); + + u_int8_t expected_generation2[] = { + 0x80,0x00,0x16,0x88, + }; + tester->assert_true(tester,(memcmp(expected_generation2,generated_data.ptr,sizeof(expected_generation2)) == 0), "compare generated data"); + + chunk_free(&generated_data); + attribute->destroy(attribute); + generator->destroy(generator); + + + + /* test attribute with 25 byte data */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + attribute = transform_attribute_create(); + char *stringval = "ddddddddddeeeeeeeeeefffff"; + data.ptr = (void *) stringval; + data.len = 25; + + attribute->set_value_chunk(attribute,data); + + attribute->set_attribute_type(attribute,456); + + + generator->generate_payload(generator,(payload_t *)attribute); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated attribute",generated_data); + + u_int8_t expected_generation3[] = { + 0x01,0xC8,0x00,0x19, + 0x64,0x64,0x64,0x64, + 0x64,0x64,0x64,0x64, + 0x64,0x64,0x65,0x65, + 0x65,0x65,0x65,0x65, + 0x65,0x65,0x65,0x65, + 0x66,0x66,0x66,0x66, + 0x66 + }; + tester->assert_true(tester,(memcmp(expected_generation3,generated_data.ptr,sizeof(expected_generation3)) == 0), "compare generated data"); + + chunk_free(&generated_data); + attribute->destroy(attribute); + generator->destroy(generator); +} + + + +/* + * Described in header + */ +void test_generator_with_transform_substructure(protected_tester_t *tester) +{ + generator_t *generator; + transform_attribute_t *attribute1, *attribute2; + transform_substructure_t *transform; + chunk_t data; + chunk_t generated_data; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + /* create attribute 1 */ + attribute1 = transform_attribute_create(); + char *stringval = "abcd"; + data.ptr = (void *) stringval; + data.len = 4; + attribute1->set_value_chunk(attribute1,data); + attribute1->set_attribute_type(attribute1,0); + logger->log(logger,CONTROL,"attribute1 created"); + + /* create attribute 2 */ + attribute2 = transform_attribute_create(); + stringval = "efgh"; + data.ptr = (void *) stringval; + data.len = 4; + attribute2->set_value_chunk(attribute2,data); + attribute2->set_attribute_type(attribute2,0); + logger->log(logger,CONTROL,"attribute2 created"); + + /* create transform */ + transform = transform_substructure_create(); + tester->assert_true(tester,(transform != NULL), "transform create check"); + transform->add_transform_attribute(transform,attribute1); + transform->add_transform_attribute(transform,attribute2); + transform->set_transform_type(transform,5); /* hex 5 */ + transform->set_transform_id(transform,65000); /* hex FDE8 */ + + + logger->log(logger,CONTROL,"transform created"); + + generator->generate_payload(generator,(payload_t *)transform); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated transform",generated_data); + + u_int8_t expected_generation3[] = { + 0x00,0x00,0x00,0x18, + 0x05,0x00,0xFD,0xE8, + 0x00,0x00,0x00,0x04, + 0x61,0x62,0x63,0x64, + 0x00,0x00,0x00,0x04, + 0x65,0x66,0x67,0x68, + }; + tester->assert_true(tester,(memcmp(expected_generation3,generated_data.ptr,sizeof(expected_generation3)) == 0), "compare generated data"); + + chunk_free(&generated_data); + transform->destroy(transform); + generator->destroy(generator); +} + + +/* + * Described in header + */ +void test_generator_with_proposal_substructure(protected_tester_t *tester) +{ + generator_t *generator; + transform_attribute_t *attribute1, *attribute2, *attribute3; + transform_substructure_t *transform1, *transform2; + proposal_substructure_t *proposal; + chunk_t data; + chunk_t generated_data; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + /* create attribute 1 */ + attribute1 = transform_attribute_create(); + char *stringval = "abcd"; + data.ptr = (void *) stringval; + data.len = 4; + attribute1->set_value_chunk(attribute1,data); + attribute1->set_attribute_type(attribute1,0); + + logger->log(logger,CONTROL,"attribute1 created"); + + /* create attribute 2 */ + attribute2 = transform_attribute_create(); + stringval = "efgh"; + data.ptr = (void *) stringval; + data.len = 4; + attribute2->set_value_chunk(attribute2,data); + attribute2->set_attribute_type(attribute2,0); + logger->log(logger,CONTROL,"attribute2 created"); + + /* create attribute 3 */ + attribute3 = transform_attribute_create(); + stringval = "ijkl"; + data.ptr = (void *) stringval; + data.len = 4; + attribute3->set_value_chunk(attribute3,data); + attribute3->set_attribute_type(attribute3,0); + logger->log(logger,CONTROL,"attribute3 created"); + + /* create transform 1*/ + transform1 = transform_substructure_create(); + tester->assert_true(tester,(transform1 != NULL), "transform create check"); + transform1->add_transform_attribute(transform1,attribute1); + transform1->add_transform_attribute(transform1,attribute2); + transform1->set_transform_type(transform1,5); /* hex 5 */ + transform1->set_transform_id(transform1,65000); /* hex FDE8 */ + + /* create transform 2*/ + transform2 = transform_substructure_create(); + tester->assert_true(tester,(transform2 != NULL), "transform create check"); + transform2->add_transform_attribute(transform2,attribute3); + transform2->set_transform_type(transform2,3); /* hex 3 */ + transform2->set_transform_id(transform2,4); /* hex 4 */ + + logger->log(logger,CONTROL,"transforms created"); + + proposal = proposal_substructure_create(); + tester->assert_true(tester,(proposal != NULL), "proposal create check"); + + stringval = "ABCDEFGH"; + data.ptr = (void *) stringval; + data.len = 8; + + proposal->add_transform_substructure(proposal,transform1); + proposal->add_transform_substructure(proposal,transform2); + proposal->set_spi(proposal,data); + proposal->set_proposal_number(proposal,7); + proposal->set_protocol_id(proposal,4); + + generator->generate_payload(generator,(payload_t *)proposal); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated transform",generated_data); + + u_int8_t expected_generation[] = { + /* proposal header */ + 0x00,0x00,0x00,0x38, + 0x07,0x04,0x08,0x02, + /* SPI */ + 0x41,0x42,0x43,0x44, + 0x45,0x46,0x47,0x48, + /* first transform */ + 0x03,0x00,0x00,0x18, + 0x05,0x00,0xFD,0xE8, + /* first transform attributes */ + 0x00,0x00,0x00,0x04, + 0x61,0x62,0x63,0x64, + 0x00,0x00,0x00,0x04, + 0x65,0x66,0x67,0x68, + /* second transform */ + 0x00,0x00,0x00,0x10, + 0x03,0x00,0x00,0x04, + /* second transform attributes */ + 0x00,0x00,0x00,0x04, + 0x69,0x6A,0x6B,0x6C + }; + logger->log_bytes(logger,RAW,"expected transform",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + proposal->destroy(proposal); + generator->destroy(generator); +} + +/* + * Described in header + */ +void test_generator_with_sa_payload(protected_tester_t *tester) +{ + generator_t *generator; + transform_attribute_t *attribute1, *attribute2, *attribute3; + transform_substructure_t *transform1, *transform2; + proposal_substructure_t *proposal_str1, *proposal_str2; + linked_list_t *list; + proposal_t *proposal1, *proposal2; + sa_payload_t *sa_payload; + ike_header_t *ike_header; + + chunk_t data; + chunk_t generated_data; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + /* --------------------------- */ + /* test first with self created proposals */ + + /* create attribute 1 */ + attribute1 = transform_attribute_create(); + char *stringval = "abcd"; + data.ptr = (void *) stringval; + data.len = 4; + attribute1->set_value_chunk(attribute1,data); + attribute1->set_attribute_type(attribute1,0); + logger->log(logger,CONTROL,"attribute1 created"); + + /* create attribute 2 */ + attribute2 = transform_attribute_create(); + stringval = "efgh"; + data.ptr = (void *) stringval; + data.len = 4; + attribute2->set_value_chunk(attribute2,data); + attribute2->set_attribute_type(attribute2,0); + logger->log(logger,CONTROL,"attribute2 created"); + + /* create attribute 3 */ + attribute3 = transform_attribute_create(); + stringval = "ijkl"; + data.ptr = (void *) stringval; + data.len = 4; + attribute3->set_value_chunk(attribute3,data); + attribute3->set_attribute_type(attribute3,0); + logger->log(logger,CONTROL,"attribute3 created"); + + /* create transform 1*/ + transform1 = transform_substructure_create(); + tester->assert_true(tester,(transform1 != NULL), "transform create check"); + transform1->add_transform_attribute(transform1,attribute1); + transform1->add_transform_attribute(transform1,attribute2); + transform1->set_transform_type(transform1,5); /* hex 5 */ + transform1->set_transform_id(transform1,65000); /* hex FDE8 */ + + /* create transform 2*/ + transform2 = transform_substructure_create(); + tester->assert_true(tester,(transform2 != NULL), "transform create check"); + transform2->add_transform_attribute(transform2,attribute3); + transform2->set_transform_type(transform2,3); /* hex 3 */ + transform2->set_transform_id(transform2,4); /* hex 4 */ + + logger->log(logger,CONTROL,"transforms created"); + + /* create proposal 1 */ + proposal_str1 = proposal_substructure_create(); + tester->assert_true(tester,(proposal1 != NULL), "proposal create check"); + + stringval = "ABCDEFGH"; + data.ptr = (void *) stringval; + data.len = 8; + + proposal_str1->add_transform_substructure(proposal_str1,transform1); + proposal_str1->add_transform_substructure(proposal_str1,transform2); + proposal_str1->set_spi(proposal_str1,data); + proposal_str1->set_proposal_number(proposal_str1,7); + proposal_str1->set_protocol_id(proposal_str1,4); + + /* create proposal 2 */ + proposal_str2 = proposal_substructure_create(); + tester->assert_true(tester,(proposal_str2 != NULL), "proposal create check"); + proposal_str2->set_proposal_number(proposal_str2,7); + proposal_str2->set_protocol_id(proposal_str2,5); + + /* create sa_payload */ + sa_payload = sa_payload_create(); + + sa_payload->add_proposal_substructure(sa_payload,proposal_str1); + sa_payload->add_proposal_substructure(sa_payload,proposal_str2); + + ike_header = ike_header_create(); + ike_header->set_initiator_spi(ike_header,0x22000054231234LL); + ike_header->set_responder_spi(ike_header,0x122398); + ((payload_t *) ike_header)->set_next_type((payload_t *) ike_header,SECURITY_ASSOCIATION); + ike_header->set_exchange_type(ike_header, 0x12); + ike_header->set_initiator_flag(ike_header, TRUE); + ike_header->set_response_flag(ike_header, TRUE); + ike_header->set_message_id(ike_header,0x33AFF3); + + generator->generate_payload(generator,(payload_t *)ike_header); + generator->generate_payload(generator,(payload_t *)sa_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated transform",generated_data); + + u_int8_t expected_generation[] = { + /* sa payload header */ + 0x34,0x12,0x23,0x54, + 0x00,0x00,0x22,0x00, + 0x98,0x23,0x12,0x00, + 0x00,0x00,0x00,0x00, + 0x21,0x20,0x12,0x28, + 0x00,0x33,0xAF,0xF3, + 0x00,0x00,0x00,0x60, + + /* sa payload header */ + 0x00,0x00,0x00,0x44, + /* proposal header */ + 0x02,0x00,0x00,0x38, + 0x07,0x04,0x08,0x02, + /* SPI */ + 0x41,0x42,0x43,0x44, + 0x45,0x46,0x47,0x48, + /* first transform */ + 0x03,0x00,0x00,0x18, + 0x05,0x00,0xFD,0xE8, + /* first transform attributes */ + 0x00,0x00,0x00,0x04, + 0x61,0x62,0x63,0x64, + 0x00,0x00,0x00,0x04, + 0x65,0x66,0x67,0x68, + /* second transform */ + 0x00,0x00,0x00,0x10, + 0x03,0x00,0x00,0x04, + /* second transform attributes */ + 0x00,0x00,0x00,0x04, + 0x69,0x6A,0x6B,0x6C, + /* proposal header 2*/ + 0x00,0x00,0x00,0x08, + 0x07,0x05,0x00,0x00, + + }; + + logger->log_bytes(logger,RAW,"expected transform",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + ike_header->destroy(ike_header); + sa_payload->destroy(sa_payload); + generator->destroy(generator); + + /* --------------------------- */ + /* test with automatic created proposals */ + + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + + proposal1 = proposal_create(1); + proposal1->add_algorithm(proposal1, PROTO_IKE, ENCRYPTION_ALGORITHM, 1, 20); + proposal1->add_algorithm(proposal1, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, 2, 22); + proposal1->add_algorithm(proposal1, PROTO_IKE, INTEGRITY_ALGORITHM, 3, 24); + proposal1->add_algorithm(proposal1, PROTO_IKE, DIFFIE_HELLMAN_GROUP, 4, 0); + + proposal2 = proposal_create(2); + proposal2->add_algorithm(proposal2, PROTO_IKE, ENCRYPTION_ALGORITHM, 5, 26); + proposal2->add_algorithm(proposal2, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, 6, 28); + proposal2->add_algorithm(proposal2, PROTO_IKE, INTEGRITY_ALGORITHM, 7, 30); + proposal2->add_algorithm(proposal2, PROTO_IKE, DIFFIE_HELLMAN_GROUP, 8, 0); + + list = linked_list_create(); + list->insert_last(list, (void*)proposal1); + list->insert_last(list, (void*)proposal2); + sa_payload = sa_payload_create_from_proposal_list(list); + tester->assert_true(tester,(sa_payload != NULL), "sa_payload create check"); + + generator->generate_payload(generator,(payload_t *)sa_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated",generated_data); + + u_int8_t expected_generation2[] = { + 0x00,0x00,0x00,0x6C, /* payload header*/ + 0x02,0x00,0x00,0x34, /* a proposal */ + 0x01,0x01,0x00,0x04, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 2 */ + 0x02,0x00,0x00,0x02, + 0x80,0x0E,0x00,0x16, /* keylength attribute with 20 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 3 */ + 0x03,0x00,0x00,0x03, + 0x80,0x0E,0x00,0x18, /* keylength attribute with 20 bytes length */ + 0x00,0x00,0x00,0x08, /* transform 4 */ + 0x04,0x00,0x00,0x04, + 0x00,0x00,0x00,0x34, /* a proposal */ + 0x02,0x01,0x00,0x04, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x05, + 0x80,0x0E,0x00,0x1A, /* keylength attribute with 16 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 2 */ + 0x02,0x00,0x00,0x06, + 0x80,0x0E,0x00,0x1C, /* keylength attribute with 16 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 3 */ + 0x03,0x00,0x00,0x07, + 0x80,0x0E,0x00,0x1E, /* keylength attribute with 16 bytes length */ + 0x00,0x00,0x00,0x08, /* transform 4 */ + 0x04,0x00,0x00,0x08, + + }; + + logger->log_bytes(logger,RAW,"expected",expected_generation2,sizeof(expected_generation2)); + + tester->assert_true(tester,(memcmp(expected_generation2,generated_data.ptr,sizeof(expected_generation2)) == 0), "compare generated data"); + + sa_payload->destroy(sa_payload); + list->destroy(list); + proposal1->destroy(proposal1); + proposal2->destroy(proposal2); + chunk_free(&generated_data); + generator->destroy(generator); + + + /* --------------------------- */ + /* test with automatic created child proposals */ + + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + + proposal1 = proposal_create(1); + + proposal1->add_algorithm(proposal1, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + proposal1->add_algorithm(proposal1, PROTO_AH, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + proposal1->add_algorithm(proposal1, PROTO_AH, EXTENDED_SEQUENCE_NUMBERS, EXT_SEQ_NUMBERS, 0); + proposal1->set_spi(proposal1, PROTO_AH, 0x01010101l); + + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 20); + proposal1->add_algorithm(proposal1, PROTO_ESP, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + proposal1->set_spi(proposal1, PROTO_ESP, 0x02020202); + + + proposal2->add_algorithm(proposal2, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + proposal2->add_algorithm(proposal2, PROTO_AH, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + proposal2->add_algorithm(proposal2, PROTO_AH, EXTENDED_SEQUENCE_NUMBERS, EXT_SEQ_NUMBERS, 0); + proposal2->set_spi(proposal2, PROTO_AH, 0x01010101); + + proposal2->add_algorithm(proposal2, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 32); + proposal2->add_algorithm(proposal2, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + proposal2->add_algorithm(proposal2, PROTO_ESP, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + proposal2->set_spi(proposal2, PROTO_ESP, 0x02020202); + + list->insert_last(list, (void*)proposal1); + list->insert_last(list, (void*)proposal2); + + sa_payload = sa_payload_create_from_proposal_list(list); + tester->assert_true(tester,(sa_payload != NULL), "sa_payload create check"); + + generator->generate_payload(generator,(payload_t *)sa_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated",generated_data); + + u_int8_t expected_generation3[] = { + 0x00,0x00,0x00,0xA0, /* payload header*/ + + /* suite 1 */ + 0x02,0x00,0x00,0x28, /* a proposal */ + 0x01,0x02,0x04,0x03, + 0x01,0x01,0x01,0x01, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + + 0x03,0x00,0x00,0x08, /* transform 2 */ + 0x04,0x00,0x00,0x0E, + + 0x00,0x00,0x00,0x08, /* transform 3 */ + 0x05,0x00,0x00,0x01, + + + 0x02,0x00,0x00,0x20, /* a proposal */ + 0x01,0x03,0x04,0x02, + 0x02,0x02,0x02,0x02, + + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x0C, + 0x80,0x0E,0x00,0x20, /* keylength attribute with 32 bytes length */ + + 0x00,0x00,0x00,0x08, /* transform 2 */ + 0x04,0x00,0x00,0x02, + + /* suite 2 */ + 0x02,0x00,0x00,0x28, /* a proposal */ + 0x02,0x02,0x04,0x03, + 0x01,0x01,0x01,0x01, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + + 0x03,0x00,0x00,0x08, /* transform 2 */ + 0x04,0x00,0x00,0x0E, + + 0x00,0x00,0x00,0x08, /* transform 3 */ + 0x05,0x00,0x00,0x01, + + + 0x00,0x00,0x00,0x2C, /* a proposal */ + 0x02,0x03,0x04,0x03, + 0x02,0x02,0x02,0x02, + + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x0C, + 0x80,0x0E,0x00,0x20, /* keylength attribute with 32 bytes length */ + + 0x03,0x00,0x00,0x0C, /* transform 2 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + + 0x00,0x00,0x00,0x08, /* transform 3 */ + 0x04,0x00,0x00,0x02, + + }; + + + logger->log_bytes(logger,RAW,"expected",expected_generation3,sizeof(expected_generation3)); + + tester->assert_true(tester,(memcmp(expected_generation3,generated_data.ptr,sizeof(expected_generation3)) == 0), "compare generated data"); + + sa_payload->destroy(sa_payload); + proposal1->destroy(proposal1); + proposal2->destroy(proposal2); + list->destroy(list); + chunk_free(&generated_data); + generator->destroy(generator); + +} + +/* + * Described in header + */ +void test_generator_with_ke_payload(protected_tester_t *tester) +{ + generator_t *generator; + ke_payload_t *ke_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t key_exchange_data; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + ke_payload = ke_payload_create(); + + + key_exchange_data.ptr = "test-text"; + key_exchange_data.len = strlen(key_exchange_data.ptr); + + ke_payload->set_key_exchange_data(ke_payload,key_exchange_data); + + ke_payload->set_dh_group_number(ke_payload,7777); + + generator->generate_payload(generator,(payload_t *)ke_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x11, + 0x1E,0x61,0x00,0x00, + /* key exchange data */ + 0x74,0x65,0x73,0x74, + 0x2D,0x74,0x65,0x78, + 0x74 + }; + + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + ke_payload->destroy(ke_payload); + generator->destroy(generator); + +} + +/* + * Described in header + */ +void test_generator_with_notify_payload(protected_tester_t *tester) +{ + generator_t *generator; + notify_payload_t *notify_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t spi,notification_data; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + notify_payload = notify_payload_create(); + + + spi.ptr = "12345"; + spi.len = strlen(spi.ptr); + + notification_data.ptr = "67890"; + notification_data.len = strlen(notification_data.ptr); + + notify_payload->set_protocol_id(notify_payload,255); + notify_payload->set_notify_message_type(notify_payload,63333); /* Hex F765 */ + notify_payload->set_spi(notify_payload,spi); + notify_payload->set_notification_data(notify_payload,notification_data); + + generator->generate_payload(generator,(payload_t *)notify_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x12, + 0xFF,0x05,0xF7,0x65, + /* spi */ + 0x31,0x32,0x33,0x34, + 0x35, + /* notification data */ + 0x36,0x37,0x38,0x39, + 0x30, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + notify_payload->destroy(notify_payload); + generator->destroy(generator); +} + +/* + * Described in header + */ +void test_generator_with_nonce_payload(protected_tester_t *tester) +{ + generator_t *generator; + nonce_payload_t *nonce_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t nonce; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + nonce_payload = nonce_payload_create(); + + + nonce.ptr = "1234567890123456"; + nonce.len = strlen("1234567890123456"); + + nonce_payload->set_nonce(nonce_payload,nonce); + + generator->generate_payload(generator,(payload_t *)nonce_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x14, + /* nonce data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + 0x33,0x34,0x35,0x36 + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + + nonce_payload->destroy(nonce_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_id_payload(protected_tester_t *tester) +{ + generator_t *generator; + id_payload_t *id_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t id; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + id_payload = id_payload_create(FALSE); + + + id.ptr = "123456789012"; + id.len = strlen(id.ptr); + + id_payload->set_id_type(id_payload,ID_IPV4_ADDR); + id_payload->set_data(id_payload,id); + + generator->generate_payload(generator,(payload_t *)id_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x14, + 0x01,0x00,0x00,0x00, + /* id data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + id_payload->destroy(id_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_auth_payload(protected_tester_t *tester) +{ + generator_t *generator; + auth_payload_t *auth_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t auth; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + auth_payload = auth_payload_create(FALSE); + + + auth.ptr = "123456789012"; + auth.len = strlen(auth.ptr); + + auth_payload->set_auth_method(auth_payload,SHARED_KEY_MESSAGE_INTEGRITY_CODE); + auth_payload->set_data(auth_payload,auth); + + generator->generate_payload(generator,(payload_t *)auth_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x14, + 0x02,0x00,0x00,0x00, + /* auth data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + auth_payload->destroy(auth_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_ts_payload(protected_tester_t *tester) +{ + generator_t *generator; + ts_payload_t *ts_payload; + traffic_selector_substructure_t *ts1, *ts2; + host_t *start_host1, *start_host2, *end_host1, *end_host2; + logger_t *logger; + chunk_t generated_data; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + ts_payload = ts_payload_create(TRUE); + + /* first traffic selector */ + ts1 = traffic_selector_substructure_create(); + + start_host1 = host_create(AF_INET,"192.168.1.0",500); + ts1->set_start_host(ts1,start_host1); + start_host1->destroy(start_host1); + + end_host1 = host_create(AF_INET,"192.168.1.255",500); + ts1->set_end_host(ts1,end_host1); + end_host1->destroy(end_host1); + + ts_payload->add_traffic_selector_substructure(ts_payload,ts1); + + /* second traffic selector */ + + ts2 = traffic_selector_substructure_create(); + + start_host2 = host_create(AF_INET,"0.0.0.0",0); + ts2->set_start_host(ts2,start_host2); + ts2->set_protocol_id(ts2,3); + start_host2->destroy(start_host2); + + end_host2 = host_create(AF_INET,"255.255.255.255",65535); + ts2->set_end_host(ts2,end_host2); + end_host2->destroy(end_host2); + + ts_payload->add_traffic_selector_substructure(ts_payload,ts2); + + + generator->generate_payload(generator,(payload_t *)ts_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x28, + 0x02,0x00,0x00,0x00, + + /* traffic selector 1 */ + 0x07,0x00,0x00,0x10, + 0x01,0xF4,0x01,0xF4, + 0xC0,0xA8,0x01,0x00, + 0xC0,0xA8,0x01,0xFF, + + /* traffic selector 2 */ + 0x07,0x03,0x00,0x10, + 0x00,0x00,0xFF,0xFF, + 0x00,0x00,0x00,0x00, + 0xFF,0xFF,0xFF,0xFF, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + ts_payload->destroy(ts_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_cert_payload(protected_tester_t *tester) +{ + generator_t *generator; + cert_payload_t *cert_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t cert; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + cert_payload = cert_payload_create(); + + + cert.ptr = "123456789012"; + cert.len = strlen(cert.ptr); + + cert_payload->set_cert_encoding(cert_payload,PGP_CERTIFICATE); + cert_payload->set_data(cert_payload,cert); + + generator->generate_payload(generator,(payload_t *)cert_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x11, + 0x02, + /* cert data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + cert_payload->destroy(cert_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_certreq_payload(protected_tester_t *tester) +{ + generator_t *generator; + certreq_payload_t *certreq_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t certreq; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + certreq_payload = certreq_payload_create(); + + + certreq.ptr = "123456789012"; + certreq.len = strlen(certreq.ptr); + + certreq_payload->set_cert_encoding(certreq_payload,PGP_CERTIFICATE); + certreq_payload->set_data(certreq_payload,certreq); + + generator->generate_payload(generator,(payload_t *)certreq_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x11, + 0x02, + /* certreq data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + certreq_payload->destroy(certreq_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_delete_payload(protected_tester_t *tester) +{ + generator_t *generator; + delete_payload_t *delete_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t spis; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + delete_payload = delete_payload_create(); + + + spis.ptr = "123456789012"; + spis.len = strlen(spis.ptr); + + delete_payload->set_protocol_id(delete_payload, PROTO_AH); + delete_payload->set_spi_count(delete_payload,3); + delete_payload->set_spi_size(delete_payload,4); + delete_payload->set_spis(delete_payload,spis); + + generator->generate_payload(generator,(payload_t *)delete_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x14, + 0x02,0x04,0x00,0x03, + /* delete data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + delete_payload->destroy(delete_payload); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_vendor_id_payload(protected_tester_t *tester) +{ + generator_t *generator; + vendor_id_payload_t *vendor_id_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t data; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + vendor_id_payload = vendor_id_payload_create(); + + + data.ptr = "123456789012"; + data.len = strlen(data.ptr); +; + vendor_id_payload->set_data(vendor_id_payload,data); + generator->generate_payload(generator,(payload_t *)vendor_id_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x10, + /* vendor_id data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + vendor_id_payload->destroy(vendor_id_payload); + generator->destroy(generator); +} + +/* + * Described in header + */ +void test_generator_with_cp_payload(protected_tester_t *tester) +{ + generator_t *generator; + configuration_attribute_t *attribute1, *attribute2; + cp_payload_t *configuration; + chunk_t data; + chunk_t generated_data; + logger_t *logger; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + /* create attribute 1 */ + attribute1 = configuration_attribute_create(); + char *stringval = "abcd"; + data.ptr = (void *) stringval; + data.len = 4; + attribute1->set_value(attribute1,data); + attribute1->set_attribute_type(attribute1,3); + logger->log(logger,CONTROL,"attribute1 created"); + + /* create attribute 2 */ + attribute2 = configuration_attribute_create(); + stringval = "efgh"; + data.ptr = (void *) stringval; + data.len = 4; + attribute2->set_value(attribute2,data); + attribute2->set_attribute_type(attribute2,4); + logger->log(logger,CONTROL,"attribute2 created"); + + /* create configuration */ + configuration = cp_payload_create(); + tester->assert_true(tester,(configuration != NULL), "configuration create check"); + configuration->add_configuration_attribute(configuration,attribute1); + configuration->add_configuration_attribute(configuration,attribute2); + configuration->set_config_type(configuration,5); /* hex 5 */ + + + logger->log(logger,CONTROL,"cp payload created"); + + generator->generate_payload(generator,(payload_t *)configuration); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated configuration",generated_data); + + u_int8_t expected_generation3[] = { + /* cp payload header */ + 0x00,0x00,0x00,0x18, + 0x05,0x00,0x00,0x00, + /* configuration attribute 1*/ + 0x00,0x03,0x00,0x04, + 0x61,0x62,0x63,0x64, + /* configuration attribute 2*/ + 0x00,0x04,0x00,0x04, + 0x65,0x66,0x67,0x68, + }; + + logger->log_bytes(logger,RAW,"expected configuration",expected_generation3,sizeof(expected_generation3)); + + tester->assert_true(tester,(memcmp(expected_generation3,generated_data.ptr,sizeof(expected_generation3)) == 0), "compare generated data"); + + chunk_free(&generated_data); + configuration->destroy(configuration); + generator->destroy(generator); +} + +/* + * Described in header. + */ +void test_generator_with_eap_payload(protected_tester_t *tester) +{ + generator_t *generator; + eap_payload_t *eap_payload; + logger_t *logger; + chunk_t generated_data; + chunk_t message; + + logger = logger_manager->get_logger(logger_manager,TESTER); + + /* create generator */ + generator = generator_create(); + tester->assert_true(tester,(generator != NULL), "generator create check"); + + eap_payload = eap_payload_create(); + + + message.ptr = "123456789012"; + message.len = strlen(message.ptr); +; + eap_payload->set_message(eap_payload,message); + generator->generate_payload(generator,(payload_t *)eap_payload); + generator->write_to_chunk(generator,&generated_data); + logger->log_chunk(logger,RAW,"generated payload",generated_data); + + u_int8_t expected_generation[] = { + /* payload header */ + 0x00,0x00,0x00,0x10, + /* eap data */ + 0x31,0x32,0x33,0x34, + 0x35,0x36,0x37,0x38, + 0x39,0x30,0x31,0x32, + }; + + logger->log_bytes(logger,RAW,"expected payload",expected_generation,sizeof(expected_generation)); + + tester->assert_true(tester,(memcmp(expected_generation,generated_data.ptr,sizeof(expected_generation)) == 0), "compare generated data"); + + chunk_free(&generated_data); + + eap_payload->destroy(eap_payload); + generator->destroy(generator); +} diff --git a/src/charon/testing/generator_test.h b/src/charon/testing/generator_test.h new file mode 100644 index 000000000..204255fb7 --- /dev/null +++ b/src/charon/testing/generator_test.h @@ -0,0 +1,183 @@ +/** + * @file generator_test.h + * + * @brief Tests for the generator_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 GENERATOR_TEST_H_ +#define GENERATOR_TEST_H_ + +#include + +/** + * @brief Test function used to test the generator with header payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_header_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with transform attribute payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_transform_attribute(protected_tester_t *tester); + + +/** + * @brief Test function used to test the generator with transform substructure payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_transform_substructure(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with proposal substructure payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_proposal_substructure(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with SA payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_sa_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with KE payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_ke_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with Notify payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_notify_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with Nonce payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_nonce_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with ID payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_id_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with AUTH payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_auth_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with TS payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_ts_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with CERT payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_cert_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with CERTREQ payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_certreq_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with DELETE payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_delete_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with VENDOR ID payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_vendor_id_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with CP payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_cp_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the generator with EAP payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_generator_with_eap_payload(protected_tester_t *tester); + + +#endif /*GENERATOR_TEST_H_*/ diff --git a/src/charon/testing/hasher_test.c b/src/charon/testing/hasher_test.c new file mode 100644 index 000000000..9130a2092 --- /dev/null +++ b/src/charon/testing/hasher_test.c @@ -0,0 +1,170 @@ +/** + * @file hasher_test.h + * + * @brief Tests for the hasher_t classes. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "hasher_test.h" + + +/* + * described in Header-File + */ +void test_md5_hasher(protected_tester_t *tester) +{ + /* + * Test vectors from RFC1321: + * MD5 ("") = d41d8cd98f00b204e9800998ecf8427e + * MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661 + * MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72 + * MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0 + * MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b + * + * currently testing "", "abc", "abcdefghijklmnopqrstuvwxyz" + */ + hasher_t *hasher = hasher_create(HASH_MD5); + u_int8_t hash_buffer[16]; + chunk_t empty, abc, abcd, hash_chunk; + + u_int8_t hash_empty[] = { + 0xd4,0x1d,0x8c,0xd9, + 0x8f,0x00,0xb2,0x04, + 0xe9,0x80,0x09,0x98, + 0xec,0xf8,0x42,0x7e + }; + + u_int8_t hash_abc[] = { + 0x90,0x01,0x50,0x98, + 0x3c,0xd2,0x4f,0xb0, + 0xd6,0x96,0x3f,0x7d, + 0x28,0xe1,0x7f,0x72 + }; + + u_int8_t hash_abcd[] = { + 0xc3,0xfc,0xd3,0xd7, + 0x61,0x92,0xe4,0x00, + 0x7d,0xfb,0x49,0x6c, + 0xca,0x67,0xe1,0x3b + }; + + empty.ptr = ""; + empty.len = 0; + abc.ptr = "abc"; + abc.len = 3; + abcd.ptr = "abcdefghijklmnopqrstuvwxyz"; + abcd.len = strlen(abcd.ptr); + + tester->assert_true(tester, hasher->get_hash_size(hasher) == 16, "block size"); + + /* simple hashing, using empty */ + hasher->get_hash(hasher, empty, hash_buffer); + tester->assert_false(tester, memcmp(hash_buffer, hash_empty, 16), "hash for empty"); + + /* simple hashing, using "abc" */ + hasher->get_hash(hasher, abc, hash_buffer); + tester->assert_false(tester, memcmp(hash_buffer, hash_abc, 16), "hash for abc"); + + /* with allocation, using "abcdb..." */ + hasher->reset(hasher); + hasher->allocate_hash(hasher, abcd, &hash_chunk); + tester->assert_true(tester, hash_chunk.len == 16, "hash len"); + tester->assert_false(tester, memcmp(hash_chunk.ptr, hash_abcd, hash_chunk.len), "hash for abcd..."); + free(hash_chunk.ptr); + hasher->destroy(hasher); +} + +/* + * described in Header-File + */ +void test_sha1_hasher(protected_tester_t *tester) +{ + /* + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + hasher_t *hasher = hasher_create(HASH_SHA1); + u_int8_t hash_buffer[20]; + chunk_t abc, abcdb, aaa, hash_chunk; + u_int32_t i; + u_int8_t hash_abc[] = { + 0xA9,0x99,0x3E,0x36, + 0x47,0x06,0x81,0x6A, + 0xBA,0x3E,0x25,0x71, + 0x78,0x50,0xC2,0x6C, + 0x9C,0xD0,0xD8,0x9D + }; + u_int8_t hash_abcdb[] = { + 0x84,0x98,0x3E,0x44, + 0x1C,0x3B,0xD2,0x6E, + 0xBA,0xAE,0x4A,0xA1, + 0xF9,0x51,0x29,0xE5, + 0xE5,0x46,0x70,0xF1 + }; + u_int8_t hash_aaa[] = { + 0x34,0xAA,0x97,0x3C, + 0xD4,0xC4,0xDA,0xA4, + 0xF6,0x1E,0xEB,0x2B, + 0xDB,0xAD,0x27,0x31, + 0x65,0x34,0x01,0x6F + }; + abc.ptr = "abc"; + abc.len = 3; + abcdb.ptr = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + abcdb.len = strlen(abcdb.ptr); + aaa.ptr = "aaaaaaaaaa"; /* 10 a's */ + aaa.len = 10; + + tester->assert_true(tester, hasher->get_hash_size(hasher) == 20, "block size"); + + /* simple hashing, using "abc" */ + hasher->get_hash(hasher, abc, hash_buffer); + tester->assert_false(tester, memcmp(hash_buffer, hash_abc, 20), "hash for abc"); + + /* with allocation, using "abcdb..." */ + hasher->reset(hasher); + hasher->allocate_hash(hasher, abcdb, &hash_chunk); + tester->assert_true(tester, hash_chunk.len == 20, "chunk len"); + tester->assert_false(tester, memcmp(hash_chunk.ptr, hash_abcdb, hash_chunk.len), "hash for abcdb..."); + free(hash_chunk.ptr); + + /* updating, using "aaaaaaa..." */ + hasher->reset(hasher); + for(i=0; i<100000; i++) + { + if (i != 99999) + { + hasher->get_hash(hasher, aaa, NULL); + } + else + { + hasher->get_hash(hasher, aaa, hash_buffer); + } + } + tester->assert_false(tester, memcmp(hash_buffer, hash_aaa, 20), "hash for aaa..."); + + + hasher->destroy(hasher); +} diff --git a/src/charon/testing/hasher_test.h b/src/charon/testing/hasher_test.h new file mode 100644 index 000000000..cc6fe52c8 --- /dev/null +++ b/src/charon/testing/hasher_test.h @@ -0,0 +1,49 @@ +/** + * @file hasher_test.h + * + * @brief Tests for the hasher_t classes. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HASHER_TEST_H_ +#define HASHER_TEST_H_ + +#include +#include +#include +#include + +/** + * @brief Test function used to test the SHA1-hasher functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_sha1_hasher(protected_tester_t *tester); + +/** + * @brief Test function used to test the Md5-hasher functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_md5_hasher(protected_tester_t *tester); + +#endif /*HASHER_TEST_H_*/ diff --git a/src/charon/testing/hmac_signer_test.c b/src/charon/testing/hmac_signer_test.c new file mode 100644 index 000000000..a1ac8ea43 --- /dev/null +++ b/src/charon/testing/hmac_signer_test.c @@ -0,0 +1,203 @@ +/** + * @file hmac_signer_test.c + * + * @brief Tests for the hmac_signer_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "hmac_signer_test.h" + +#include +#include + + +/* + * Described in header. + */ +void test_hmac_md5_signer(protected_tester_t *tester) +{ + /* Test cases from RFC2202 + * + * test_case = 5 + * key = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + * key_len = 16 + * data = "Test With Truncation" + * data_len = 20 + * digest = 0x56461ef2342edc00f9bab995690efd4c + * digest-96 0x56461ef2342edc00f9bab995 + * + * currently only this test 5 gets performed! + */ + chunk_t keys[4]; + chunk_t data[4]; + chunk_t signature[4]; + chunk_t reference[4]; + chunk_t wrong_reference[4]; + int i; + logger_t *logger; + bool valid; + + logger = logger_manager->get_logger(logger_manager, TESTER); + + signer_t *signer = (signer_t *) signer_create(AUTH_HMAC_MD5_96); + tester->assert_true(tester, (signer != NULL), "signer create call check"); + + + /* + * values for test 5 + */ + u_int8_t key1[] = { + 0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c, + }; + keys[0].ptr = key1; + keys[0].len = sizeof(key1); + data[0].ptr = "Test With Truncation"; + data[0].len = 20; + u_int8_t reference1[] = { + 0x56,0x46,0x1e,0xf2,0x34,0x2e, + 0xdc,0x00,0xf9,0xba,0xb9,0x95 + }; + reference[0].ptr = reference1; + reference[0].len = sizeof(reference1); + + u_int8_t wrong_reference1[] = { + 0x56,0x46,0x1e,0xa2,0x34,0x2e, + 0xdc,0x00,0xf9,0xba,0xb9,0x95 + }; + + wrong_reference[0].ptr = wrong_reference1; + wrong_reference[0].len = sizeof(wrong_reference1); + + for (i=0; i<1; i++) + { + signer->set_key(signer, keys[i]); + signer->allocate_signature(signer, data[i], &signature[i]); + tester->assert_true(tester, signature[i].len == 12, "chunk len"); + tester->assert_true(tester, (memcmp(signature[i].ptr, reference[i].ptr, 12) == 0), "hmac value"); + logger->log_chunk(logger,RAW,"expected signature:",reference[i]); + logger->log_chunk(logger,RAW,"signature:",signature[i]); + free(signature[i].ptr); + valid = signer->verify_signature(signer, data[i],reference[i]); + tester->assert_true(tester, (valid == TRUE), "Signature valid check"); + + valid = signer->verify_signature(signer, data[i],wrong_reference[i]); + tester->assert_true(tester, (valid == FALSE), "Signature not valid check"); + } + signer->destroy(signer); +} + + +/* + * Described in header. + */ +void test_hmac_sha1_signer(protected_tester_t *tester) +{ + /* + * test_case = 7 + * key = 0xaa repeated 80 times + * key_len = 80 + * data = "Test Using Larger Than Block-Size Key and Larger + * Than One Block-Size Data" + * data_len = 73 + * digest = 0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04 + * digest-96 = 0x4c1a03424b55e07fe7f27be1 + */ + + chunk_t keys[4]; + chunk_t data[4]; + chunk_t signature[4]; + chunk_t reference[4]; + chunk_t wrong_reference[4]; + int i; + logger_t *logger; + bool valid; + + logger = logger_manager->get_logger(logger_manager, TESTER); + + signer_t *signer = (signer_t *) signer_create(AUTH_HMAC_SHA1_96); + tester->assert_true(tester, (signer != NULL), "signer create call check"); + + + /* + * values for test 5 + */ + u_int8_t key1[] = { + 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, + }; + keys[0].ptr = key1; + keys[0].len = sizeof(key1); + data[0].ptr = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"; + data[0].len = 73; + u_int8_t reference1[] = { + 0xe8,0xe9,0x9d,0x0f,0x45,0x23, + 0x7d,0x78,0x6d,0x6b,0xba,0xa7 + }; + reference[0].ptr = reference1; + reference[0].len = sizeof(reference1); + + u_int8_t wrong_reference1[] = { + 0xe8,0xe9,0x9d,0x0f,0x46,0x23, + 0x7d,0x71,0x6d,0x6b,0xba,0xa7 + }; + + wrong_reference[0].ptr = wrong_reference1; + wrong_reference[0].len = sizeof(wrong_reference1); + + for (i=0; i<1; i++) + { + signer->set_key(signer, keys[i]); + signer->allocate_signature(signer, data[i], &signature[i]); + tester->assert_true(tester, signature[i].len == 12, "chunk len"); + tester->assert_true(tester, (memcmp(signature[i].ptr, reference[i].ptr, 12) == 0), "hmac value"); + logger->log_chunk(logger,RAW,"expected signature:",reference[i]); + logger->log_chunk(logger,RAW,"signature:",signature[i]); + free(signature[i].ptr); + valid = signer->verify_signature(signer, data[i],reference[i]); + tester->assert_true(tester, (valid == TRUE), "Signature valid check"); + + valid = signer->verify_signature(signer, data[i],wrong_reference[i]); + tester->assert_true(tester, (valid == FALSE), "Signature not valid check"); + } + + signer->destroy(signer); +} diff --git a/src/charon/testing/hmac_signer_test.h b/src/charon/testing/hmac_signer_test.h new file mode 100644 index 000000000..4a2459a8e --- /dev/null +++ b/src/charon/testing/hmac_signer_test.h @@ -0,0 +1,46 @@ +/** + * @file hmac_signer_test.h + * + * @brief Tests for the hmac_signer_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HMAC_SIGNER_TEST_H_ +#define HMAC_SIGNER_TEST_H_ + +#include + +/** + * @brief Test function used to test the hmac sign functionality using MD5. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_hmac_md5_signer(protected_tester_t *tester); + +/** + * @brief Test function used to test the hmac sign functionality using SHA1. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_hmac_sha1_signer(protected_tester_t *tester); + +#endif /* HMAC_SIGNER_TEST_H_ */ diff --git a/src/charon/testing/hmac_test.c b/src/charon/testing/hmac_test.c new file mode 100644 index 000000000..c1341257c --- /dev/null +++ b/src/charon/testing/hmac_test.c @@ -0,0 +1,408 @@ +/** + * @file hmac_test.h + * + * @brief Tests for the hmac_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "hmac_test.h" + +#include + + +/* + * described in Header-File + */ +void test_hmac_sha1(protected_tester_t *tester) +{ + /* + * Test cases from RFC2202 + * + * test_case = 1 + * key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + * key_len = 20 + * data = "Hi There" + * data_len = 8 + * digest = 0xb617318655057264e28bc0b6fb378c8ef146be00 + * + * test_case = 2 + * key = "Jefe" + * key_len = 4 + * data = "what do ya want for nothing?" + * data_len = 28 + * digest = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79 + * + * test_case = 3 + * key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + * key_len = 20 + * data = 0xdd repeated 50 times + * data_len = 50 + * digest = 0x125d7342b9ac11cd91a39af48aa17b4f63f175d3 + * + * test_case = 4 + * key = 0x0102030405060708090a0b0c0d0e0f10111213141516171819 + * key_len = 25 + * data = 0xcd repeated 50 times + * data_len = 50 + * digest = 0x4c9007f4026250c6bc8414f9bf50c86c2d7235da + * + * test_case = 5 + * key = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + * key_len = 20 + * data = "Test With Truncation" + * data_len = 20 + * digest = 0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04 + * digest-96 = 0x4c1a03424b55e07fe7f27be1 + * + * test_case = 6 + * key = 0xaa repeated 80 times + * key_len = 80 + * data = "Test Using Larger Than Block-Size Key - Hash Key First" + * data_len = 54 + * digest = 0xaa4ae5e15272d00e95705637ce8a3b55ed402112 + * + * test_case = 7 + * key = 0xaa repeated 80 times + * key_len = 80 + * data = "Test Using Larger Than Block-Size Key and Larger + * Than One Block-Size Data" + * data_len = 73 + * digest = 0xe8e99d0f45237d786d6bbaa7965c7808bbff1a91 + * + * currently performing test 1, 2, 4 and 7 + */ + + chunk_t keys[4]; + chunk_t data[4]; + chunk_t digest[4]; + chunk_t reference[4]; + int i; + + /* + * values for test 1 + */ + u_int8_t key1[] = { + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b + }; + keys[0].ptr = key1; + keys[0].len = sizeof(key1); + data[0].ptr = "Hi There"; + data[0].len = 8; + u_int8_t reference1[] = { + 0xb6,0x17,0x31,0x86, + 0x55,0x05,0x72,0x64, + 0xe2,0x8b,0xc0,0xb6, + 0xfb,0x37,0x8c,0x8e, + 0xf1,0x46,0xbe,0x00 + }; + reference[0].ptr = reference1; + reference[0].len = sizeof(reference1); + + /* + * values for test 2 + */ + u_int8_t reference2[] = { + 0xef,0xfc,0xdf,0x6a, + 0xe5,0xeb,0x2f,0xa2, + 0xd2,0x74,0x16,0xd5, + 0xf1,0x84,0xdf,0x9c, + 0x25,0x9a,0x7c,0x79 + }; + keys[1].ptr = "Jefe"; + keys[1].len = 4; + data[1].ptr = "what do ya want for nothing?"; + data[1].len = 28; + reference[1].ptr = reference2; + reference[1].len = sizeof(reference2); + + /* + * values for test 7 + */ + u_int8_t key7[] = { + 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, + }; + u_int8_t reference7[] = { + 0xe8,0xe9,0x9d,0x0f, + 0x45,0x23,0x7d,0x78, + 0x6d,0x6b,0xba,0xa7, + 0x96,0x5c,0x78,0x08, + 0xbb,0xff,0x1a,0x91 + }; + keys[2].ptr = key7; + keys[2].len = sizeof(key7); + data[2].ptr = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"; + data[2].len = 73; + reference[2].ptr = reference7; + reference[2].len = sizeof(reference7); + + + for (i=0; i<3; i++) + { + hmac_t *hmac = hmac_create(HASH_SHA1); + hmac->set_key(hmac, keys[i]); + hmac->allocate_mac(hmac, data[i], &digest[i]); + hmac->destroy(hmac); + + tester->assert_true(tester, digest[i].len == 20, "chunk len"); + tester->assert_false(tester, memcmp(digest[i].ptr, reference[i].ptr, 20), "hmac value"); + free(digest[i].ptr); + } + + /* + * test 4 is donne in append mode + */ + u_int8_t val = 0xcd; + + u_int8_t key4[] = { + 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 + }; + keys[3].ptr = key4; + keys[3].len = sizeof(key4); + u_int8_t reference4[] = { + 0x4c,0x90,0x07,0xf4, + 0x02,0x62,0x50,0xc6, + 0xbc,0x84,0x14,0xf9, + 0xbf,0x50,0xc8,0x6c, + 0x2d,0x72,0x35,0xda + }; + reference[3].ptr = reference4; + reference[3].len = sizeof(reference4); + + hmac_t *hmac = hmac_create(HASH_SHA1); + hmac->set_key(hmac, keys[3]); + data[3].ptr = &val; + data[3].len = 1; + for (i=0; i<49; i++) + { + hmac->get_mac(hmac, data[3], NULL); + } + hmac->allocate_mac(hmac, data[3], &digest[3]); + hmac->destroy(hmac); + + tester->assert_true(tester, digest[3].len == 20, "chunk len append mode"); + tester->assert_false(tester, memcmp(digest[3].ptr, reference[3].ptr, 20), "hmac value append mode"); + free(digest[3].ptr); +} + +/* + * described in Header-File + */ +void test_hmac_md5(protected_tester_t *tester) +{ + /* + * Test cases from RFC2202 + * + * test_case = 1 + * key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + * key_len = 16 + * data = "Hi There" + * data_len = 8 + * digest = 0x9294727a3638bb1c13f48ef8158bfc9d + * + * test_case = 2 + * key = "Jefe" + * key_len = 4 + * data = "what do ya want for nothing?" + * data_len = 28 + * digest = 0x750c783e6ab0b503eaa86e310a5db738 + * + * test_case = 3 + * key = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + * key_len 16 + * data = 0xdd repeated 50 times + * data_len = 50 + * digest = 0x56be34521d144c88dbb8c733f0e8b3f6 + * + * test_case = 4 + * key = 0x0102030405060708090a0b0c0d0e0f10111213141516171819 + * key_len 25 + * data = 0xcd repeated 50 times + * data_len = 50 + * digest = 0x697eaf0aca3a3aea3a75164746ffaa79 + * + * test_case = 5 + * key = 0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + * key_len = 16 + * data = "Test With Truncation" + * data_len = 20 + * digest = 0x56461ef2342edc00f9bab995690efd4c + * digest-96 0x56461ef2342edc00f9bab995 + * + * test_case = 6 + * key = 0xaa repeated 80 times + * key_len = 80 + * data = "Test Using Larger Than Block-Size Key - Hash Key First" + * data_len = 54 + * digest = 0x6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd + * + * test_case = 7 + * key = 0xaa repeated 80 times + * key_len = 80 + * data = "Test Using Larger Than Block-Size Key and Larger + * Than One Block-Size Data" + * data_len = 73 + * digest = 0x6f630fad67cda0ee1fb1f562db3aa53e + * + * + * + * currently performing test 1, 2, 4 and 7 + * + */ + chunk_t keys[4]; + chunk_t data[4]; + chunk_t digest[4]; + chunk_t reference[4]; + int i; + + /* + * values for test 1 + */ + u_int8_t key1[] = { + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, + }; + keys[0].ptr = key1; + keys[0].len = sizeof(key1); + data[0].ptr = "Hi There"; + data[0].len = 8; + u_int8_t reference1[] = { + 0x92,0x94,0x72,0x7a, + 0x36,0x38,0xbb,0x1c, + 0x13,0xf4,0x8e,0xf8, + 0x15,0x8b,0xfc,0x9d + }; + reference[0].ptr = reference1; + reference[0].len = sizeof(reference1); + + /* + * values for test 2 + */ + u_int8_t reference2[] = { + 0x75,0x0c,0x78,0x3e, + 0x6a,0xb0,0xb5,0x03, + 0xea,0xa8,0x6e,0x31, + 0x0a,0x5d,0xb7,0x38 + }; + keys[1].ptr = "Jefe"; + keys[1].len = 4; + data[1].ptr = "what do ya want for nothing?"; + data[1].len = 28; + reference[1].ptr = reference2; + reference[1].len = sizeof(reference2); + + /* + * values for test 7 + */ + u_int8_t key7[] = { + 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, + }; + u_int8_t reference7[] = { + 0x6f,0x63,0x0f,0xad, + 0x67,0xcd,0xa0,0xee, + 0x1f,0xb1,0xf5,0x62, + 0xdb,0x3a,0xa5,0x3e + }; + keys[2].ptr = key7; + keys[2].len = sizeof(key7); + data[2].ptr = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"; + data[2].len = 73; + reference[2].ptr = reference7; + reference[2].len = sizeof(reference7); + + + for (i=0; i<3; i++) + { + hmac_t *hmac = hmac_create(HASH_MD5); + hmac->set_key(hmac, keys[i]); + hmac->allocate_mac(hmac, data[i], &digest[i]); + hmac->destroy(hmac); + tester->assert_true(tester, digest[i].len == 16, "chunk len"); + tester->assert_false(tester, memcmp(digest[i].ptr, reference[i].ptr, 16), "hmac value"); + free(digest[i].ptr); + } + + /* + * test 4 is donne in append mode + */ + u_int8_t val = 0xcd; + + u_int8_t key4[] = { + 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 + }; + keys[3].ptr = key4; + keys[3].len = sizeof(key4); + u_int8_t reference4[] = { + 0x69,0x7e,0xaf,0x0a, + 0xca,0x3a,0x3a,0xea, + 0x3a,0x75,0x16,0x47, + 0x46,0xff,0xaa,0x79 + }; + reference[3].ptr = reference4; + reference[3].len = sizeof(reference4); + + hmac_t *hmac = hmac_create(HASH_MD5); + hmac->set_key(hmac, keys[3]); + data[3].ptr = &val; + data[3].len = 1; + for (i=0; i<49; i++) + { + hmac->get_mac(hmac, data[3], NULL); + } + hmac->allocate_mac(hmac, data[3], &digest[3]); + hmac->destroy(hmac); + + tester->assert_true(tester, digest[3].len == 16, "chunk len append mode"); + tester->assert_false(tester, memcmp(digest[3].ptr, reference[3].ptr, 16), "hmac value append mode"); + free(digest[3].ptr); +} diff --git a/src/charon/testing/hmac_test.h b/src/charon/testing/hmac_test.h new file mode 100644 index 000000000..1eef93cd3 --- /dev/null +++ b/src/charon/testing/hmac_test.h @@ -0,0 +1,49 @@ +/** + * @file hmac_test.h + * + * @brief Tests for the hmac_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 HMAC_TEST_H_ +#define HMAC_TEST_H_ + +#include +#include + +/** + * @brief Test function used to test the hmac functionality + * using SHA1. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_hmac_sha1(protected_tester_t *tester); + +/** + * @brief Test function used to test the hmac functionality + * using MD5. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_hmac_md5(protected_tester_t *tester); + +#endif /*HMAC_TEST_H_*/ diff --git a/src/charon/testing/identification_test.c b/src/charon/testing/identification_test.c new file mode 100644 index 000000000..b148b53e0 --- /dev/null +++ b/src/charon/testing/identification_test.c @@ -0,0 +1,166 @@ +/** + * @file identification_test.c + * + * @brief Tests for the identification_t class. + * + */ + +/* + * 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 "identification_test.h" + +#include +#include + +/* + * described in Header-File + */ +void test_identification(protected_tester_t *tester) +{ + identification_t *a, *b, *c, *d; + bool result; + + { /* test RFC822_ADDR */ + char *bob_string = "bob@wonderland.net"; + chunk_t bob_chunk = {bob_string, strlen(bob_string)}; + + a = identification_create_from_string("alice@wonderland.net"); + b = identification_create_from_encoding(ID_RFC822_ADDR, bob_chunk); + c = identification_create_from_string("*@wonderland.net"); + d = identification_create_from_string("*@badlands.com"); + + result = a->belongs_to(a, c); + tester->assert_true(tester, result, "alice belongs to wonderland"); + result = b->belongs_to(b, c); + tester->assert_true(tester, result, "bob belongs to wonderland"); + result = a->belongs_to(a, d); + tester->assert_false(tester, result, "alice does not belong to badlands"); + result = b->belongs_to(b, d); + tester->assert_false(tester, result, "bob does not belong to badlands"); + result = c->belongs_to(c, d); + tester->assert_false(tester, result, "wonderland is not in badlands"); + result = a->belongs_to(a, a); + tester->assert_true(tester, result, "alice belongs to alice alice"); + result = a->equals(a, a); + tester->assert_true(tester, result, "alice is alice"); + result = a->equals(a, b); + tester->assert_false(tester, result, "alice is not bob"); + + a->destroy(a); + b->destroy(b); + c->destroy(c); + d->destroy(d); + } + + { /* test FQDN */ + char *bob_string = "@dave.nirvana.org"; + chunk_t bob_chunk = {bob_string, strlen(bob_string)}; + + a = identification_create_from_string("@carol.nirvana.org"); + b = identification_create_from_encoding(ID_FQDN, bob_chunk); + c = identification_create_from_string("@*.nirvana.org"); + d = identification_create_from_string("@*.samsara.com"); + + result = a->belongs_to(a, c); + tester->assert_true(tester, result, "carol belongs to nirvana"); + result = b->belongs_to(b, c); + tester->assert_true(tester, result, "dave belongs to nirvana"); + result = a->belongs_to(a, d); + tester->assert_false(tester, result, "carol does not belong to samsara"); + result = b->belongs_to(b, d); + tester->assert_false(tester, result, "dave does not belong to samsara"); + result = c->belongs_to(c, d); + tester->assert_false(tester, result, "nirvana is not in samsara"); + result = a->belongs_to(a, a); + tester->assert_true(tester, result, "carol belongs to carol carol"); + result = a->equals(a, a); + tester->assert_true(tester, result, "carol is carol"); + result = a->equals(a, b); + tester->assert_false(tester, result, "carol is not dave"); + + a->destroy(a); + b->destroy(b); + c->destroy(c); + d->destroy(d); + } + + + { /* test ID IPV4 ADDR, no wildcards yet */ + char bob_addr[] = {192,168,0,2}; + chunk_t bob_chunk = chunk_from_buf(bob_addr); + + a = identification_create_from_string("192.168.0.1"); + b = identification_create_from_encoding(ID_IPV4_ADDR, bob_chunk); + c = identification_create_from_string("192.168.0.2"); /* as bob */ + + result = a->equals(a, a); + tester->assert_true(tester, result, "IPV4_ADDR of alice equals IPV4_ADDR of alice"); + result = b->equals(b, c); + tester->assert_true(tester, result, "IPV4_ADDR of bob equals IPV4_ADDR of carol"); + result = a->equals(a, b); + tester->assert_false(tester, result, "IPV4_ADDR of alice doesn't equal IPV4_ADDR of bob"); + + a->destroy(a); + b->destroy(b); + c->destroy(c); + } + + { /* test ID IPV6 ADDR, no wildcards yet */ + char bob_addr[] = {0x20,0x01,0x0d,0xb8,0x85,0xa3,0x08,0xd3,0x13,0x19,0x8a,0x2e,0x03,0x70,0x73,0x44}; + chunk_t bob_chunk = chunk_from_buf(bob_addr); + + a = identification_create_from_string("2001:0db8:85a3:08d3:1319:8a2e:0370:7345"); + b = identification_create_from_encoding(ID_IPV6_ADDR, bob_chunk); + c = identification_create_from_string("2001:0db8:85a3:08d3:1319:8a2e:0370:7344"); /* as bob */ + + result = a->equals(a, a); + tester->assert_true(tester, result, "IPV6_ADDR of alice equals IPV6_ADDR of alice"); + result = b->equals(b, c); + tester->assert_true(tester, result, "IPV6_ADDR of bob equals IPV6_ADDR of carol"); + result = a->equals(a, b); + tester->assert_false(tester, result, "IPV6_ADDR of alice doesn't equal IPV6_ADDR of bob"); + + a->destroy(a); + b->destroy(b); + c->destroy(c); + } + + { /* test ID DER_ASN1_DN */ + a = identification_create_from_string("C=CH, O=Linux strongSwan, CN=alice"); + b = identification_create_from_string("O=Linux strongSwan, C=CH, CN=bob"); + c = identification_create_from_string("C=CH, O=Linux strongSwan, CN=*"); + d = identification_create_from_string("C=CH, O=Linux openswan, CN=*"); + + result = a->equals(a, a); + tester->assert_true(tester, result, "DN of alice equals DN of alice"); + result = a->equals(a, b); + tester->assert_false(tester, result, "DN of alice doesn't equal DN of bob"); + result = a->belongs_to(a, c); + tester->assert_true(tester, result, "DN of alice belongs to DN of carol"); + /* TODO: This does NOT work, wildcard check should work with unordered RDNs */ + result = b->belongs_to(b, c); + tester->assert_true(tester, result, "DN of bob belongs to DN of carol"); + result = b->belongs_to(b, d); + tester->assert_false(tester, result, "DN of bob doesn't belong to DN of dave"); + + a->destroy(a); + b->destroy(b); + c->destroy(c); + d->destroy(d); + } +} diff --git a/src/charon/testing/identification_test.h b/src/charon/testing/identification_test.h new file mode 100644 index 000000000..b1078c52f --- /dev/null +++ b/src/charon/testing/identification_test.h @@ -0,0 +1,37 @@ +/** + * @file identification_test.h + * + * @brief Tests for the identification_t class. + * + */ + +/* + * 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 IDENTIFICATION_TEST_H_ +#define IDENTIFICATION_TEST_H_ + +#include + +/** + * @brief Test function used to test the identification functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_identification(protected_tester_t *tester); + +#endif /* IDENTIFICATION_TEST_H_ */ diff --git a/src/charon/testing/ike_sa_id_test.c b/src/charon/testing/ike_sa_id_test.c new file mode 100644 index 000000000..ba44363fb --- /dev/null +++ b/src/charon/testing/ike_sa_id_test.c @@ -0,0 +1,84 @@ +/** + * @file ike_sa_id_test.c + * + * @brief Tests for the ike_sa_id_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_sa_id_test.h" + +#include + +/* + * described in Header-File + */ +void test_ike_sa_id(protected_tester_t *tester) +{ + ike_sa_id_t *ike_sa_id, *clone, *equal, *other1, *other2, *other3, *other4; + u_int64_t initiator, initiator2, responder, responder2; + bool is_initiator; + + initiator = 0; + + initiator2 = 12345612; + + responder = 34334; + + responder2 = 987863; + + is_initiator = TRUE; + + ike_sa_id = ike_sa_id_create(initiator, responder, is_initiator); + equal = ike_sa_id_create(initiator, responder, is_initiator); + other1 = ike_sa_id_create(initiator, responder2, is_initiator); + other2 = ike_sa_id_create(initiator2, responder2, is_initiator); + other3 = ike_sa_id_create(initiator2, responder, is_initiator); + is_initiator = FALSE; + other4 = ike_sa_id_create(initiator, responder, is_initiator); + + /* check equality */ + tester->assert_true(tester,(ike_sa_id->equals(ike_sa_id,equal) == TRUE), "equal check"); + tester->assert_true(tester,(equal->equals(equal,ike_sa_id) == TRUE), "equal check"); + + /* check clone functionality and equality*/ + clone = ike_sa_id->clone(ike_sa_id); + tester->assert_false(tester,(clone == ike_sa_id), "clone pointer check"); + tester->assert_true(tester,(ike_sa_id->equals(ike_sa_id,clone) == TRUE), "equal check"); + + /* check for non equality */ + tester->assert_false(tester,(ike_sa_id->equals(ike_sa_id,other1) == TRUE), "equal check"); + + tester->assert_false(tester,(ike_sa_id->equals(ike_sa_id,other2) == TRUE), "equal check"); + + tester->assert_false(tester,(ike_sa_id->equals(ike_sa_id,other3) == TRUE), "equal check"); + + tester->assert_false(tester,(ike_sa_id->equals(ike_sa_id,other4) == TRUE), "equal check"); + + other4->replace_values(other4,ike_sa_id); + tester->assert_true(tester,(ike_sa_id->equals(ike_sa_id,other4) == TRUE), "equal check"); + + + /* check destroy functionality */ + ike_sa_id->destroy(ike_sa_id); + equal->destroy(equal); + clone->destroy(clone); + other1->destroy(other1); + other2->destroy(other2); + other3->destroy(other3); + other4->destroy(other4); +} diff --git a/src/charon/testing/ike_sa_id_test.h b/src/charon/testing/ike_sa_id_test.h new file mode 100644 index 000000000..75429e4fb --- /dev/null +++ b/src/charon/testing/ike_sa_id_test.h @@ -0,0 +1,40 @@ +/** + * @file ike_sa_id_test.h + * + * @brief Tests for the ike_sa_id_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_ID_TEST_H_ +#define IKE_SA_ID_TEST_H_ + +#include + +/** + * @brief Test function used to test the ike_sa_id functionality. + * + * Tests are performed using one thread to test the + * features of the ike_sa_id_t. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_ike_sa_id(protected_tester_t *tester); + +#endif /*IKE_SA_ID_TEST_H_*/ diff --git a/src/charon/testing/ike_sa_manager_test.c b/src/charon/testing/ike_sa_manager_test.c new file mode 100644 index 000000000..5247be7f0 --- /dev/null +++ b/src/charon/testing/ike_sa_manager_test.c @@ -0,0 +1,185 @@ +/** + * @file ike_sa_manager_test.c + * + * @brief Tests for the ike_sa_manager_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "ike_sa_manager_test.h" + +#include +#include + + +static struct ike_sa_manager_test_struct_s { + protected_tester_t *tester; + ike_sa_manager_t *isam; +} td; + +static void test1_thread(ike_sa_id_t *ike_sa_id) +{ + ike_sa_t *ike_sa; + status_t status; + + status = td.isam->checkout(td.isam, ike_sa_id, &ike_sa); + td.tester->assert_true(td.tester, (status == SUCCESS), "checkout of a blocked ike_sa"); + usleep(10000); + status = td.isam->checkin(td.isam, ike_sa); + td.tester->assert_true(td.tester, (status == SUCCESS), "checkin of a requested ike_sa"); +} + + +static void test3_thread(ike_sa_id_t *ike_sa_id) +{ + ike_sa_t *ike_sa; + status_t status; + + status = td.isam->checkout(td.isam, ike_sa_id, &ike_sa); + td.tester->assert_true(td.tester, (status == NOT_FOUND), "IKE_SA already deleted"); +} + + + + +void test_ike_sa_manager(protected_tester_t *tester) +{ + status_t status; + u_int64_t initiator, responder; + ike_sa_id_t *ike_sa_id, *sa_id; + ike_sa_t *ike_sa; + int thread_count = 200; + int sa_count = 100; + int i; + pthread_t threads[thread_count]; + + td.tester = tester; + td.isam = ike_sa_manager_create(); + tester->assert_true(tester, (td.isam != NULL), "ike_sa_manager creation"); + + + + + /* First Test: + * we play initiator for IKE_SA_INIT first + * create an IKE_SA, + * + */ + + td.isam->create_and_checkout(td.isam, &ike_sa); + /* for testing purposes, we manipulate the responder spi. + * this is usually done be the response from the communication partner, + * but we don't have one... + */ + responder = 123; + + sa_id = ike_sa->get_id(ike_sa); + sa_id->set_responder_spi(sa_id, responder); + + ike_sa_id = sa_id->clone(sa_id); + + /* check in, so we should have a "completed" sa, specified by ike_sa_id */ + status = td.isam->checkin(td.isam, ike_sa); + tester->assert_true(tester, (status == SUCCESS), "checkin modified IKE_SA"); + + /* now we check it out and start some other threads */ + status = td.isam->checkout(td.isam, ike_sa_id, &ike_sa); + tester->assert_true(tester, (status == SUCCESS), "checkout existing IKE_SA 1"); + + for (i = 0; i < thread_count; i++) + { + if (pthread_create(&threads[i], NULL, (void*(*)(void*))test1_thread, (void*)ike_sa_id)) + { + /* failed, decrease list */ + thread_count--; + i--; + } + } + sleep(1); + + + status = td.isam->checkin(td.isam, ike_sa); + tester->assert_true(tester, (status == SUCCESS), "checkin IKE_SA"); + + + sleep(1); + /* we now delete the IKE_SA, while it is requested by the threads. + * this should block until the have done their work.*/ + status = td.isam->delete(td.isam, ike_sa_id); + tester->assert_true(tester, (status == SUCCESS), "delete IKE_SA by id"); + + + for (i = 0; i < thread_count; i++) + { + pthread_join(threads[i], NULL); + } + + ike_sa_id->destroy(ike_sa_id); + + + /* Second Test: + * now we simulate our partner initiates an IKE_SA_INIT, + * so we are the responder. + * + */ + memset(&initiator, 0, sizeof(initiator)); + memset(&responder, 0, sizeof(responder)); + + initiator = 123; + ike_sa_id = ike_sa_id_create(initiator, responder, TRUE); + + status = td.isam->checkout(td.isam, ike_sa_id, &ike_sa); + tester->assert_false(tester, (status == SUCCESS), "checkout unexisting IKE_SA 2"); + + /* let them go acquiring */ + sleep(1); + + + ike_sa_id->destroy(ike_sa_id); + + /* Third Test: + * put in a lot of IKE_SAs, check it out, set a thread waiting + * and destroy the manager... + */ + thread_count = sa_count; + + for (i = 0; i < sa_count; i++) + { + td.isam->create_and_checkout(td.isam, &ike_sa); + + if (pthread_create(&threads[i], NULL, (void*(*)(void*))test3_thread, (void*)ike_sa->get_id(ike_sa))) + { + /* failed, decrease list */ + thread_count--; + } + } + + /* let them go acquiring */ + sleep(1); + + td.isam->destroy(td.isam); + + for (i = 0; i < thread_count; i++) + { + pthread_join(threads[i], NULL); + } +} + diff --git a/src/charon/testing/ike_sa_manager_test.h b/src/charon/testing/ike_sa_manager_test.h new file mode 100644 index 000000000..c3e9f99f1 --- /dev/null +++ b/src/charon/testing/ike_sa_manager_test.h @@ -0,0 +1,39 @@ +/** + * @file ike_sa_manager_test.h + * + * @brief Tests for the ike_sa_manager_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_MANAGER_TEST_H_ +#define IKE_SA_MANAGER_TEST_H_ + +#include + +/** + * @brief Test function used to test the ike_sa_manager_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_ike_sa_manager(protected_tester_t *tester); + + + +#endif /*IKE_SA_MANAGER_TEST_H_*/ diff --git a/src/charon/testing/ike_sa_test.c b/src/charon/testing/ike_sa_test.c new file mode 100644 index 000000000..798b5edc9 --- /dev/null +++ b/src/charon/testing/ike_sa_test.c @@ -0,0 +1,56 @@ +/** + * @file ike_sa_test.c + * + * @brief Tests for the ike_sa_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_sa_test.h" + +#include +#include +#include + +void test_ike_sa(protected_tester_t *tester) +{ + ike_sa_t *ike_sa; + ike_sa_id_t *ike_sa_id; + u_int64_t initiator, responder; + bool is_initiator; + + + initiator = 0; + responder = 34334LL; + is_initiator = TRUE; + /* create a ike_sa_id object for the new IKE_SA */ + ike_sa_id = ike_sa_id_create(initiator, responder, is_initiator); + + /* empty message and configuration objects are created */ + + + /* test every ike_sa function */ + ike_sa = ike_sa_create(ike_sa_id); + +/* ike_sa->initialize_connection(ike_sa, NULL); + + tester->assert_true(tester,(ike_sa != NULL), "ike_sa pointer check"); +*/ + ike_sa->destroy(ike_sa); + + ike_sa_id->destroy(ike_sa_id); +} diff --git a/src/charon/testing/ike_sa_test.h b/src/charon/testing/ike_sa_test.h new file mode 100644 index 000000000..e93bc34fd --- /dev/null +++ b/src/charon/testing/ike_sa_test.h @@ -0,0 +1,37 @@ +/** + * @file ike_sa_test.h + * + * @brief Tests for the ike_sa_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_SA_TEST_H_ +#define IKE_SA_TEST_H_ + +#include + +/** + * @brief Test function used to test the ike_sa_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_ike_sa(protected_tester_t *tester); + +#endif /*IKE_SA_TEST_H_*/ diff --git a/src/charon/testing/job_queue_test.c b/src/charon/testing/job_queue_test.c new file mode 100644 index 000000000..336a9a188 --- /dev/null +++ b/src/charon/testing/job_queue_test.c @@ -0,0 +1,132 @@ +/** + * @file job_queue_test.c + * + * @brief Tests for the job_queue_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "job_queue_test.h" + +#include +#include + + +typedef struct job_queue_test_s job_queue_test_t; + +/** + * @brief Informations for the involved test-thread used in this test + * + */ +struct job_queue_test_s{ + protected_tester_t *tester; + job_queue_t *job_queue; + /** + * number of items to be inserted in the job-queue + */ + int insert_item_count; + /** + * number of items to be removed by each + * receiver thread from the job-queue + */ + int remove_item_count; +}; + +/** + * @brief sender thread used in the the job_queue test function + * + * @param testinfo informations for the specific thread. + */ +static void test_job_queue_sender(job_queue_test_t * testinfo) +{ + int i; + for (i = 0; i < testinfo->insert_item_count; i++) + { + job_t *job = (job_t *) initiate_ike_sa_job_create(NULL); + testinfo->job_queue->add(testinfo->job_queue,job); + } +} + +/** + * @brief receiver thread used in the the job_queue test function + * + * @param testinfo informations for the specific thread. + */ +static void test_job_queue_receiver(job_queue_test_t * testinfo) +{ + int i; + for (i = 0; i < testinfo->remove_item_count; i++) + { + job_t *job; + job = testinfo->job_queue->get(testinfo->job_queue); + testinfo->tester->assert_true(testinfo->tester,(job->get_type(job) == INITIATE_IKE_SA), "job type check"); + job->destroy(job); + } +} + +/* + * description is in header file + */ +void test_job_queue(protected_tester_t *tester) +{ + int desired_value, i; + int sender_count = 10; + int receiver_count = 2; + pthread_t sender_threads[sender_count]; + pthread_t receiver_threads[receiver_count]; + job_queue_t *job_queue = job_queue_create(); + job_queue_test_t test_infos; + + test_infos.tester = tester; + test_infos.job_queue = job_queue; + test_infos.insert_item_count = 10000; + test_infos.remove_item_count = 50000; + + + desired_value = test_infos.insert_item_count * sender_count - + test_infos.remove_item_count * receiver_count; + + for (i = 0; i < receiver_count;i++) + { + pthread_create( &receiver_threads[i], NULL,(void*(*)(void*)) &test_job_queue_receiver, (void*) &test_infos); + } + for (i = 0; i < sender_count;i++) + { + pthread_create( &sender_threads[i], NULL,(void*(*)(void*)) &test_job_queue_sender, (void*) &test_infos); + } + + + /* Wait for all threads */ + for (i = 0; i < sender_count;i++) + { + pthread_join(sender_threads[i], NULL); + } + for (i = 0; i < receiver_count;i++) + { + pthread_join(receiver_threads[i], NULL); + } + + /* the job-queue has to have disered_value count entries! */ + tester->assert_true(tester,(job_queue->get_count(job_queue) == desired_value), "get count value check"); + + job_queue->destroy(job_queue); +} diff --git a/src/charon/testing/job_queue_test.h b/src/charon/testing/job_queue_test.h new file mode 100644 index 000000000..f2d3edc4c --- /dev/null +++ b/src/charon/testing/job_queue_test.h @@ -0,0 +1,40 @@ +/** + * @file job_queue_test.h + * + * @brief Tests for the job_queue_test_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 JOB_QUEUE_TEST_H_ +#define JOB_QUEUE_TEST_H_ + +#include + +/** + * @brief Test function used to test the job_queue functionality. + * + * Tests are performed using different threads to test the multi-threaded + * features of the job_queue_t. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_job_queue(protected_tester_t *tester); + +#endif /*JOB_QUEUE_TEST_H_*/ diff --git a/src/charon/testing/kernel_interface_test.c b/src/charon/testing/kernel_interface_test.c new file mode 100644 index 000000000..86553e15e --- /dev/null +++ b/src/charon/testing/kernel_interface_test.c @@ -0,0 +1,84 @@ +/** + * @file kernel_interface_test.h + * + * @brief Tests for the kernel_interface_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "kernel_interface_test.h" + +#include +#include +#include +#include + + +/* + * described in Header-File + */ +void test_kernel_interface(protected_tester_t *tester) +{ + kernel_interface_t *kernel_interface; + u_int32_t spi; + host_t *me, *other, *left, *right; + status_t status; + + u_int8_t enc_key_bytes[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 + }; + + u_int8_t inc_key_bytes[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 + }; + + chunk_t enc_key,inc_key; + enc_key.ptr = enc_key_bytes; + enc_key.len = sizeof(enc_key_bytes); + inc_key.ptr = inc_key_bytes; + inc_key.len = sizeof(inc_key_bytes); + + + + kernel_interface = kernel_interface_create(); + + me = host_create(AF_INET, "192.168.0.2", 0); + other = host_create(AF_INET, "192.168.0.3", 0); + + status = kernel_interface->get_spi(kernel_interface, me, other, 50, 1234, &spi); + tester->assert_true(tester, status == SUCCESS, "spi get"); + + status = kernel_interface->add_sa(kernel_interface, me, other, spi, 50, 1234, ENCR_AES_CBC, enc_key,AUTH_UNDEFINED,inc_key,TRUE); + tester->assert_true(tester, status == SUCCESS, "add sa"); + + left = host_create(AF_INET, "10.1.0.0", 0); + right = host_create(AF_INET, "10.2.0.0", 0); + + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_OUT, 0, TRUE, FALSE, 1234); + tester->assert_true(tester, status == SUCCESS, "add policy"); + + me->destroy(me); + other->destroy(other); + left->destroy(left); + right->destroy(right); + + kernel_interface->destroy(kernel_interface); + +} diff --git a/src/charon/testing/kernel_interface_test.h b/src/charon/testing/kernel_interface_test.h new file mode 100644 index 000000000..fc8dab4b6 --- /dev/null +++ b/src/charon/testing/kernel_interface_test.h @@ -0,0 +1,38 @@ +/** + * @file kernel_interface_test.h + * + * @brief Tests for the kernel_interface_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 KERNEL_INTERFACE_TEST_H_ +#define KERNEL_INTERFACE_TEST_H_ + +#include + +/** + * @brief Test function used to test the kernel_interface functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_kernel_interface(protected_tester_t *tester); + + +#endif /*KERNEL_INTERFACE_TEST_H_*/ diff --git a/src/charon/testing/leak_detective_test.c b/src/charon/testing/leak_detective_test.c new file mode 100644 index 000000000..8d71d9f0f --- /dev/null +++ b/src/charon/testing/leak_detective_test.c @@ -0,0 +1,79 @@ +/** + * @file leak_detective_test.h + * + * @brief Tests for the leak_detective_test. + * + */ + +/* + * 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 "leak_detective_test.h" + + +void *mem_a, *mem_b, *mem_c; + +void a() +{ + mem_a = malloc(4); +} + +void b() +{ + a(); + mem_b = malloc(5); +} + +void c() +{ + b(); + mem_c = malloc(6); +} + +void recursive(int depth) +{ + void *tiny = malloc(1); + if (--depth > 0) + { + recursive(depth); + } + free(tiny); +} + + +/* + * described in Header-File + */ +void test_leak_detective(protected_tester_t *tester) +{ + void *m1, *m2, *m3; + + + m1 = malloc(1); + m2 = calloc(1, 2); + m3 = malloc(3); + + m3 = realloc(m3, 4); + + free(m2); + free(m3); + free(m1); + + c(); + free(mem_a); + free(mem_c); + free(mem_b); + recursive(10000); +} diff --git a/src/charon/testing/leak_detective_test.h b/src/charon/testing/leak_detective_test.h new file mode 100644 index 000000000..e64266bd5 --- /dev/null +++ b/src/charon/testing/leak_detective_test.h @@ -0,0 +1,38 @@ +/** + * @file leak_detective_test.h + * + * @brief Tests for the leak_detective_public_key_t and leak_detective_private_key classes. + * + */ + +/* + * 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_DETEICTVE_TEST_H +#define LEAK_DETEICTVE_TEST_H + +#include + +/** + * @brief Test function used to test the leak_detective functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_leak_detective(protected_tester_t *tester); + + +#endif /*LEAK_DETEICTVE_TEST_H*/ diff --git a/src/charon/testing/linked_list_test.c b/src/charon/testing/linked_list_test.c new file mode 100644 index 000000000..3d5666f64 --- /dev/null +++ b/src/charon/testing/linked_list_test.c @@ -0,0 +1,241 @@ +/** + * @file linked_list_test.c + * + * @brief Tests for the linked_list_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "linked_list_test.h" + +#include + + /* + * Description in header-file + */ +void test_linked_list(protected_tester_t *tester) +{ + void *test_value = NULL; + + linked_list_t *linked_list = linked_list_create(); + + tester->assert_true(tester,(linked_list->get_count(linked_list) == 0), "count check"); + + linked_list->insert_first(linked_list,"one"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 1), "count check"); + + linked_list->insert_first(linked_list,"two"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 2), "count check"); + + linked_list->insert_first(linked_list,"three"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 3), "count check"); + + linked_list->insert_first(linked_list,"four"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 4), "count check"); + + linked_list->insert_first(linked_list,"five"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 5), "count check"); + + tester->assert_true(tester,(linked_list->get_first(linked_list,&test_value) == SUCCESS), "get_first call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"five") == 0), "get_first value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 5), "count check"); + + tester->assert_true(tester,(linked_list->get_last(linked_list,&test_value) == SUCCESS), "get_last call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"one") == 0), "get_last value check"); + tester->assert_true(tester,( linked_list->get_count(linked_list) == 5), "count check"); + + tester->assert_true(tester,(linked_list->remove_first(linked_list,&test_value) == SUCCESS), "remove_first call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"five") == 0), "remove_first value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 4), "count check"); + + tester->assert_true(tester,(linked_list->get_first(linked_list,&test_value) == SUCCESS), "get_first call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"four") == 0), "get_first value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 4), "count check"); + + tester->assert_true(tester,(linked_list->get_last(linked_list,&test_value) == SUCCESS), "get_last call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"one") == 0), "get_last value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 4), "count check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,0,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"four") == 0), "get_at_position value check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,1,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"three") == 0), "get_at_position value check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,2,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"two") == 0), "get_at_position value check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,3,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"one") == 0), "get_at_position value check"); + + tester->assert_false(tester,(linked_list->get_at_position(linked_list,4,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_false(tester,(linked_list->remove_at_position(linked_list,4,&test_value) == SUCCESS), "remove_at_position call check"); + tester->assert_false(tester,(linked_list->insert_at_position(linked_list,5,test_value) == SUCCESS), "insert_at_position call 1 check"); + + tester->assert_true(tester,(linked_list->insert_at_position(linked_list,3,"six") == SUCCESS), "insert_at_position call 2 check"); + tester->assert_true(tester,(linked_list->insert_at_position(linked_list,3,"seven") == SUCCESS), "insert_at_position call 3 check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,3,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"seven") == 0), "get_at_position value 1 check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,4,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"six") == 0), "get_at_position value 2 check"); + + tester->assert_true(tester,(linked_list->get_at_position(linked_list,5,&test_value) == SUCCESS), "get_at_position call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"one") == 0), "get_at_position value 3 check"); + + tester->assert_true(tester,(linked_list->remove_at_position(linked_list,3,&test_value) == SUCCESS), "remove_at_position call check"); + tester->assert_true(tester,(linked_list->remove_at_position(linked_list,3,&test_value) == SUCCESS), "remove_at_position call check"); + + + tester->assert_true(tester,(linked_list->remove_last(linked_list,&test_value) == SUCCESS), "remove_last call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"one") == 0), "remove_last value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 3), "count check"); + + tester->assert_true(tester,(linked_list->get_last(linked_list,&test_value) == SUCCESS), "get_last call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"two") == 0), "get_last value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 3), "count check"); + + tester->assert_true(tester,(linked_list->get_first(linked_list,&test_value) == SUCCESS), "get_first call check"); + tester->assert_true(tester,(strcmp((char *) test_value,"four") == 0), "get_first value check"); + tester->assert_true(tester,(linked_list->get_count(linked_list) == 3), "count check"); + + linked_list->destroy(linked_list); +} + + /* + * Description in header-file + */ +void test_linked_list_iterator(protected_tester_t *tester) +{ + void * value; + + linked_list_t *linked_list = linked_list_create(); + linked_list->insert_first(linked_list,"one"); + linked_list->insert_first(linked_list,"two"); + linked_list->insert_first(linked_list,"three"); + linked_list->insert_first(linked_list,"four"); + linked_list->insert_first(linked_list,"five"); + + iterator_t * iterator; + iterator_t * iterator2; + + + iterator = linked_list->create_iterator(linked_list,TRUE); + + tester->assert_true(tester,iterator->has_next(iterator), "it 1 has_next value check"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"five") == 0), "it 1 current value check"); + + tester->assert_true(tester,iterator->has_next(iterator), "it 1 has_next value check"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"four") == 0), "it 1 current value check"); + + iterator2 = linked_list->create_iterator(linked_list,FALSE); + + tester->assert_true(tester,iterator2->has_next(iterator2), "it 2 has_next value check"); + iterator2->current(iterator2,&value); + tester->assert_true(tester,(strcmp((char *) value,"one") == 0), "it 2 current value check"); + + tester->assert_true(tester,iterator->has_next(iterator), "it 1 has_next value check"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"three") == 0), "it 1 current value check"); + + tester->assert_true(tester,iterator2->has_next(iterator2), "it 2 has_next value check"); + iterator2->current(iterator2,&value); + tester->assert_true(tester,(strcmp((char *) value,"two") == 0), "it 2 current value check"); + + tester->assert_true(tester,iterator->has_next(iterator), "it 1 has_next value check"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"two") == 0), "it 1 current value check"); + + tester->assert_true(tester,iterator2->has_next(iterator2), "it 2 has_next value check"); + iterator2->current(iterator2,&value); + tester->assert_true(tester,(strcmp((char *) value,"three") == 0), "it 2 current value check"); + + tester->assert_true(tester,iterator->has_next(iterator), "it 1 has_next value check"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"one") == 0), "it 1 current value check"); + + tester->assert_false(tester,iterator->has_next(iterator), "it 1 has_next value check"); + + tester->assert_true(tester,iterator2->has_next(iterator2), "it 2 has_next value check"); + tester->assert_true(tester,iterator2->has_next(iterator2), "it 2 has_next value check"); + tester->assert_false(tester,iterator2->has_next(iterator2), "it 2 has_next value check"); + + iterator->destroy(iterator); + iterator2->destroy(iterator2); + linked_list->destroy(linked_list); +} + + /* + * Description in header-file + */ +void test_linked_list_insert_and_remove(protected_tester_t *tester) +{ + void *value; + iterator_t * iterator; + + linked_list_t *linked_list = linked_list_create(); + linked_list->insert_first(linked_list,"one"); + linked_list->insert_first(linked_list,"two"); + + linked_list->insert_first(linked_list,"three"); + linked_list->insert_first(linked_list,"four"); + linked_list->insert_first(linked_list,"five"); + + + + iterator = linked_list->create_iterator(linked_list,TRUE); + + iterator->has_next(iterator); + iterator->has_next(iterator); + iterator->has_next(iterator); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"three") == 0), "current value check"); + + iterator->insert_before(iterator,"before_three"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"three") == 0), "current value check"); + + + iterator->insert_after(iterator,"after_three"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"three") == 0), "current value check"); + + + tester->assert_true(tester,(iterator->remove(iterator) == SUCCESS), "remove call check"); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"before_three") == 0), "current value check"); + + iterator->reset(iterator); + + iterator->has_next(iterator); + iterator->has_next(iterator); + iterator->has_next(iterator); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"before_three") == 0), "current value check"); + iterator->has_next(iterator); + iterator->current(iterator,&value); + tester->assert_true(tester,(strcmp((char *) value,"after_three") == 0), "current value check"); + + iterator->destroy(iterator); + + linked_list->destroy(linked_list); +} diff --git a/src/charon/testing/linked_list_test.h b/src/charon/testing/linked_list_test.h new file mode 100644 index 000000000..a9773f8f0 --- /dev/null +++ b/src/charon/testing/linked_list_test.h @@ -0,0 +1,74 @@ +/** + * @file linked_list_test.h + * + * @brief Tests for the linked_list_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 LINKED_LIST_TEST_H_ +#define LINKED_LIST_TEST_H_ + +#include + +/** + * @brief Test function for the type linked_list_t. + * + * Performs different kinds of assertions to check the functionality + * of the linked_list_t in a Single-Threaded environment. + * + * @warning To be usable in multi-threaded software + * this list has to get protected with locks. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_linked_list(protected_tester_t *tester); + +/** + * @brief Test function for the type linked_list_t and its iterator. + * + * Performs different kinds of assertions to check the functionality + * of the linked_list_t and its iterator in a Single-Threaded environment. + * + * @warning To be usable in multi-threaded software + * this list has to get protected with locks. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_linked_list_iterator(protected_tester_t *tester); + +/** + * @brief Test function for the type linked_list_t and its insert and remove + * functions. + * + * Performs different kinds of assertions to check the functionality + * of the linked_list_t and its insert and remove functions + * + * @warning To be usable in multi-threaded software + * this list has to get protected with locks. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_linked_list_insert_and_remove(protected_tester_t *tester); + +#endif /*LINKED_LIST_TEST_H_*/ diff --git a/src/charon/testing/packet_test.c b/src/charon/testing/packet_test.c new file mode 100644 index 000000000..fdb195ec1 --- /dev/null +++ b/src/charon/testing/packet_test.c @@ -0,0 +1,55 @@ +/** + * @file packet_test.c + * + * @brief Tests for the packet_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "packet_test.h" + +#include +#include +#include + + +/* + * Described in Header + */ +void test_packet(protected_tester_t *tester) +{ + packet_t *packet = packet_create(); + packet_t *packet2; + chunk_t data; + char *string_to_copy = "aha, soso"; + + data.len = strlen(string_to_copy) + 1; + data.ptr = malloc(data.len); + memcpy(data.ptr, string_to_copy, data.len); + + packet->set_data(packet, data); + packet2 = packet->clone(packet); + data = packet2->get_data(packet2); + + tester->assert_true(tester,(data.len == (strlen(string_to_copy) + 1)),"value length check"); + tester->assert_true(tester,(memcmp(data.ptr,string_to_copy,data.len) == 0),"cloned value check"); + + packet2->destroy(packet2); + packet->destroy(packet); +} diff --git a/src/charon/testing/packet_test.h b/src/charon/testing/packet_test.h new file mode 100644 index 000000000..8bc297e1b --- /dev/null +++ b/src/charon/testing/packet_test.h @@ -0,0 +1,37 @@ +/** + * @file packet_test.h + * + * @brief Tests for the packet_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PACKET_TEST_H_ +#define PACKET_TEST_H_ + +#include + +/** + * @brief Test function used to test the packet_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_packet(protected_tester_t *tester); + +#endif /*PACKET_TEST_H_*/ diff --git a/src/charon/testing/parser_test.c b/src/charon/testing/parser_test.c new file mode 100644 index 000000000..263c6eb70 --- /dev/null +++ b/src/charon/testing/parser_test.c @@ -0,0 +1,963 @@ +/** + * @file parser_test.c + * + * @brief Tests for the parser_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "parser_test.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Described in Header + */ +void test_parser_with_header_payload(protected_tester_t *tester) +{ + parser_t *parser; + ike_header_t *ike_header; + status_t status; + chunk_t header_chunk; + + u_int8_t header_bytes[] = { + 0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x03,0x45,0x06,0x28, + 0x00,0x00,0x00,0x07, + 0x00,0x00,0x00,0x1C, + }; + header_chunk.ptr = header_bytes; + header_chunk.len = sizeof(header_bytes); + + + parser = parser_create(header_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, HEADER, (payload_t**)&ike_header); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + + tester->assert_true(tester,(ike_header->get_initiator_spi(ike_header) == 1),"parsed initiator_spi value"); + tester->assert_true(tester,(ike_header->get_responder_spi(ike_header) == 2),"parsed responder_spi value"); + tester->assert_true(tester,(ike_header->payload_interface.get_next_type((payload_t*)ike_header) == 3),"parsed next_payload value"); + tester->assert_true(tester,(ike_header->get_maj_version(ike_header) == 4),"parsed maj_version value"); + tester->assert_true(tester,(ike_header->get_min_version(ike_header) == 5),"parsed min_version value"); + tester->assert_true(tester,(ike_header->get_exchange_type(ike_header) == 6),"parsed exchange_type value"); + tester->assert_true(tester,(ike_header->get_initiator_flag(ike_header) == TRUE),"parsed flags.initiator value"); + tester->assert_true(tester,(ike_header->get_version_flag(ike_header) == FALSE),"parsed flags.version value"); + tester->assert_true(tester,(ike_header->get_response_flag(ike_header) == TRUE),"parsed flags.response value"); + tester->assert_true(tester,(ike_header->get_message_id(ike_header) == 7),"parsed message_id value"); + tester->assert_true(tester,(ike_header->payload_interface.get_length((payload_t*)ike_header) == 0x1C),"parsed length value"); + + ike_header->destroy(ike_header); +} + +/* + * Described in Header + */ +void test_parser_with_sa_payload(protected_tester_t *tester) +{ + parser_t *parser; + sa_payload_t *sa_payload; + status_t status; + chunk_t sa_chunk, sa_chunk2, sa_chunk3; + iterator_t *proposals, *transforms, *attributes; + + /* first test generic parsing functionality */ + + u_int8_t sa_bytes[] = { + 0x00,0x80,0x00,0x24, /* payload header*/ + 0x00,0x00,0x00,0x20, /* a proposal */ + 0x01,0x02,0x04,0x05, + 0x01,0x02,0x03,0x04, /* spi */ + 0x00,0x00,0x00,0x14, /* transform */ + 0x07,0x00,0x00,0x03, + 0x80,0x01,0x00,0x05, /* attribute without length */ + 0x00,0x03,0x00,0x04, /* attribute with length */ + 0x01,0x02,0x03,0x04 + + + }; + + sa_chunk.ptr = sa_bytes; + sa_chunk.len = sizeof(sa_bytes); + + + parser = parser_create(sa_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, SECURITY_ASSOCIATION, (payload_t**)&sa_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + + + proposals = sa_payload->create_proposal_substructure_iterator(sa_payload, TRUE); + while (proposals->has_next(proposals)) + { + proposal_substructure_t *proposal; + proposals->current(proposals, (void**)&proposal); + chunk_t spi; + u_int8_t spi_should[] = {0x01, 0x02, 0x03, 0x04}; + + tester->assert_true(tester,(proposal->get_proposal_number(proposal) == 1),"proposal number"); + tester->assert_true(tester,(proposal->get_protocol_id(proposal) == 2),"proposal id"); + spi = proposal->get_spi(proposal); + tester->assert_false(tester,(memcmp(&spi_should, spi.ptr, spi.len)),"proposal spi"); + + transforms = proposal->create_transform_substructure_iterator(proposal, TRUE); + while(transforms->has_next(transforms)) + { + transform_substructure_t *transform; + int loopi; + transforms->current(transforms, (void**)&transform); + tester->assert_true(tester,(transform->get_transform_type(transform) == 7),"transform type"); + tester->assert_true(tester,(transform->get_transform_id(transform) == 3),"transform id"); + attributes = transform->create_transform_attribute_iterator(transform, TRUE); + loopi = 0; + while (attributes->has_next(attributes)) + { + transform_attribute_t *attribute; + attributes->current(attributes, (void**)&attribute); + if (loopi == 0) + { + u_int8_t value[] = {0x05, 0x00}; + chunk_t attribute_value; + tester->assert_true(tester,(attribute->get_attribute_type(attribute) == 1),"attribute 1 type"); + attribute_value = attribute->get_value_chunk(attribute); + tester->assert_false(tester,(memcmp(&value, attribute_value.ptr, attribute_value.len)),"attribute 1 value"); + } + if (loopi == 1) + { + u_int8_t value[] = {0x01, 0x02, 0x03, 0x04}; + chunk_t attribute_value; + tester->assert_true(tester,(attribute->get_attribute_type(attribute) == 3),"attribute 2 type"); + attribute_value = attribute->get_value_chunk(attribute); + tester->assert_false(tester,(memcmp(&value, attribute_value.ptr, attribute_value.len)),"attribute 2 value"); + } + loopi++; + } + attributes->destroy(attributes); + } + transforms->destroy(transforms); + } + proposals->destroy(proposals); + + sa_payload->destroy(sa_payload); + + + + /* now test SA functionality after parsing an SA payload*/ + + u_int8_t sa_bytes2[] = { + 0x00,0x00,0x00,0x6C, /* payload header*/ + 0x02,0x00,0x00,0x34, /* a proposal */ + 0x01,0x01,0x00,0x04, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 2 */ + 0x02,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 3 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + 0x00,0x00,0x00,0x08, /* transform 4 */ + 0x04,0x00,0x00,0x01, + 0x00,0x00,0x00,0x34, /* a proposal */ + 0x01,0x01,0x00,0x04, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x02, + 0x80,0x0E,0x00,0x10, /* keylength attribute with 16 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 2 */ + 0x02,0x00,0x00,0x02, + 0x80,0x0E,0x00,0x10, /* keylength attribute with 16 bytes length */ + 0x03,0x00,0x00,0x0C, /* transform 3 */ + 0x03,0x00,0x00,0x02, + 0x80,0x0E,0x00,0x10, /* keylength attribute with 16 bytes length */ + 0x00,0x00,0x00,0x08, /* transform 4 */ + 0x04,0x00,0x00,0x02, + }; + + sa_chunk2.ptr = sa_bytes2; + sa_chunk2.len = sizeof(sa_bytes2); + + parser = parser_create(sa_chunk2); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, SECURITY_ASSOCIATION, (payload_t**)&sa_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + + status = sa_payload->payload_interface.verify(&(sa_payload->payload_interface)); + tester->assert_true(tester,(status == SUCCESS),"verify call check"); + /* + status = sa_payload->get_ike_proposals (sa_payload, &ike_proposals, &ike_proposal_count); + tester->assert_true(tester,(status == SUCCESS),"get ike proposals call check"); + + tester->assert_true(tester,(ike_proposal_count == 2),"ike proposal count check"); + tester->assert_true(tester,(ike_proposals[0].encryption_algorithm == 1),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[0].encryption_algorithm_key_length == 20),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[0].integrity_algorithm == 1),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[0].integrity_algorithm_key_length == 20),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[0].pseudo_random_function == 1),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[0].pseudo_random_function_key_length == 20),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[0].diffie_hellman_group == 1),"ike proposal content check"); + + tester->assert_true(tester,(ike_proposals[1].encryption_algorithm == 2),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[1].encryption_algorithm_key_length == 16),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[1].integrity_algorithm == 2),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[1].integrity_algorithm_key_length == 16),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[1].pseudo_random_function == 2),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[1].pseudo_random_function_key_length == 16),"ike proposal content check"); + tester->assert_true(tester,(ike_proposals[1].diffie_hellman_group == 2),"ike proposal content check"); + + + if (status == SUCCESS) + { + free(ike_proposals); + } + */ + sa_payload->destroy(sa_payload); + + /* now test SA functionality after parsing an SA payload with child sa proposals*/ + u_int8_t sa_bytes3[] = { + 0x00,0x00,0x00,0xA0, /* payload header*/ + + /* suite 1 */ + 0x02,0x00,0x00,0x28, /* a proposal */ + 0x01,0x02,0x04,0x03, + 0x01,0x01,0x01,0x01, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + + 0x03,0x00,0x00,0x08, /* transform 2 */ + 0x04,0x00,0x00,0x0E, + + 0x00,0x00,0x00,0x08, /* transform 3 */ + 0x05,0x00,0x00,0x01, + + + 0x02,0x00,0x00,0x20, /* a proposal */ + 0x01,0x03,0x04,0x02, + 0x02,0x02,0x02,0x02, + + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x0C, + 0x80,0x0E,0x00,0x20, /* keylength attribute with 32 bytes length */ + + 0x00,0x00,0x00,0x08, /* transform 2 */ + 0x04,0x00,0x00,0x02, + + /* suite 2 */ + 0x02,0x00,0x00,0x28, /* a proposal */ + 0x02,0x02,0x04,0x03, + 0x01,0x01,0x01,0x01, + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + + 0x03,0x00,0x00,0x08, /* transform 2 */ + 0x04,0x00,0x00,0x0E, + + 0x00,0x00,0x00,0x08, /* transform 3 */ + 0x05,0x00,0x00,0x01, + + + 0x00,0x00,0x00,0x2C, /* a proposal */ + 0x02,0x03,0x04,0x03, + 0x02,0x02,0x02,0x02, + + 0x03,0x00,0x00,0x0C, /* transform 1 */ + 0x01,0x00,0x00,0x0C, + 0x80,0x0E,0x00,0x20, /* keylength attribute with 32 bytes length */ + + 0x03,0x00,0x00,0x0C, /* transform 2 */ + 0x03,0x00,0x00,0x01, + 0x80,0x0E,0x00,0x14, /* keylength attribute with 20 bytes length */ + + 0x00,0x00,0x00,0x08, /* transform 3 */ + 0x04,0x00,0x00,0x02, + }; + + sa_chunk3.ptr = sa_bytes3; + sa_chunk3.len = sizeof(sa_bytes3); + + parser = parser_create(sa_chunk3); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, SECURITY_ASSOCIATION, (payload_t**)&sa_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + + status = sa_payload->payload_interface.verify(&(sa_payload->payload_interface)); + tester->assert_true(tester,(status == SUCCESS),"verify call check"); +/* + status = sa_payload->get_ike_proposals (sa_payload, &ike_proposals, &ike_proposal_count); + tester->assert_false(tester,(status == SUCCESS),"get ike proposals call check"); + + status = sa_payload->get_proposals (sa_payload, &proposals, &proposal_count); + tester->assert_true(tester,(status == SUCCESS),"get child proposals call check"); + + + tester->assert_true(tester,(proposal_count == 2),"child proposal count check"); + tester->assert_true(tester,(proposals[0].ah.is_set == TRUE),"is ah set check"); + tester->assert_true(tester,(proposals[0].ah.integrity_algorithm == AUTH_HMAC_MD5_96),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[0].ah.integrity_algorithm_key_size == 20),"integrity_algorithm_key_size check"); + tester->assert_true(tester,(proposals[0].ah.diffie_hellman_group == MODP_2048_BIT),"diffie_hellman_group check"); + tester->assert_true(tester,(proposals[0].ah.extended_sequence_numbers == EXT_SEQ_NUMBERS),"extended_sequence_numbers check"); + tester->assert_true(tester,(proposals[0].ah.spi[0] == 1),"spi check"); + tester->assert_true(tester,(proposals[0].ah.spi[1] == 1),"spi check"); + tester->assert_true(tester,(proposals[0].ah.spi[2] == 1),"spi check"); + tester->assert_true(tester,(proposals[0].ah.spi[3] == 1),"spi check"); + + tester->assert_true(tester,(proposals[0].esp.is_set == TRUE),"is ah set check"); + tester->assert_true(tester,(proposals[0].esp.encryption_algorithm == ENCR_AES_CBC),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[0].esp.encryption_algorithm_key_size == 32),"integrity_algorithm_key_size check"); + tester->assert_true(tester,(proposals[0].esp.diffie_hellman_group == MODP_1024_BIT),"diffie_hellman_group check"); + tester->assert_true(tester,(proposals[0].esp.integrity_algorithm == AUTH_UNDEFINED),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[0].esp.spi[0] == 2),"spi check"); + tester->assert_true(tester,(proposals[0].esp.spi[1] == 2),"spi check"); + tester->assert_true(tester,(proposals[0].esp.spi[2] == 2),"spi check"); + tester->assert_true(tester,(proposals[0].esp.spi[3] == 2),"spi check"); + + tester->assert_true(tester,(proposals[1].ah.is_set == TRUE),"is ah set check"); + tester->assert_true(tester,(proposals[1].ah.integrity_algorithm == AUTH_HMAC_MD5_96),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[1].ah.integrity_algorithm_key_size == 20),"integrity_algorithm_key_size check"); + tester->assert_true(tester,(proposals[1].ah.diffie_hellman_group == MODP_2048_BIT),"diffie_hellman_group check"); + tester->assert_true(tester,(proposals[1].ah.extended_sequence_numbers == EXT_SEQ_NUMBERS),"extended_sequence_numbers check"); + tester->assert_true(tester,(proposals[1].ah.spi[0] == 1),"spi check"); + tester->assert_true(tester,(proposals[1].ah.spi[1] == 1),"spi check"); + tester->assert_true(tester,(proposals[1].ah.spi[2] == 1),"spi check"); + tester->assert_true(tester,(proposals[1].ah.spi[3] == 1),"spi check"); + + tester->assert_true(tester,(proposals[1].esp.is_set == TRUE),"is ah set check"); + tester->assert_true(tester,(proposals[1].esp.encryption_algorithm == ENCR_AES_CBC),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[1].esp.encryption_algorithm_key_size == 32),"integrity_algorithm_key_size check"); + tester->assert_true(tester,(proposals[1].esp.diffie_hellman_group == MODP_1024_BIT),"diffie_hellman_group check"); + tester->assert_true(tester,(proposals[1].esp.integrity_algorithm == AUTH_HMAC_MD5_96),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[1].esp.integrity_algorithm_key_size == 20),"integrity_algorithm check"); + tester->assert_true(tester,(proposals[1].esp.spi[0] == 2),"spi check"); + tester->assert_true(tester,(proposals[1].esp.spi[1] == 2),"spi check"); + tester->assert_true(tester,(proposals[1].esp.spi[2] == 2),"spi check"); + tester->assert_true(tester,(proposals[1].esp.spi[3] == 2),"spi check"); + + if (status == SUCCESS) + { + free(proposals); + } + */ + + sa_payload->destroy(sa_payload); +} + +/* + * Described in Header + */ +void test_parser_with_nonce_payload(protected_tester_t *tester) +{ + parser_t *parser; + nonce_payload_t *nonce_payload; + status_t status; + chunk_t nonce_chunk, result; + + u_int8_t nonce_bytes[] = { + 0x00,0x00,0x00,0x14, /* payload header */ + 0x00,0x01,0x02,0x03, /* 16 Byte nonce */ + 0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + nonce_chunk.ptr = nonce_bytes; + nonce_chunk.len = sizeof(nonce_bytes); + + parser = parser_create(nonce_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, NONCE, (payload_t**)&nonce_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = nonce_payload->get_nonce(nonce_payload); + tester->assert_true(tester,(result.len == 16), "parsed nonce lenght"); + tester->assert_false(tester,(memcmp(nonce_bytes + 4, result.ptr, result.len)), "parsed nonce data"); + nonce_payload->destroy(nonce_payload); + chunk_free(&result); +} + +/* + * Described in Header + */ +void test_parser_with_id_payload(protected_tester_t *tester) +{ + parser_t *parser; + id_payload_t *id_payload; + status_t status; + chunk_t id_chunk, result; + + u_int8_t id_bytes[] = { + 0x00,0x00,0x00,0x14, /* payload header */ + 0x05,0x01,0x02,0x03, + 0x04,0x05,0x06,0x07,/* 12 Byte nonce */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + id_chunk.ptr = id_bytes; + id_chunk.len = sizeof(id_bytes); + + parser = parser_create(id_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, ID_INITIATOR, (payload_t**)&id_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = id_payload->get_data_clone(id_payload); + tester->assert_true(tester,(id_payload->get_initiator(id_payload) == TRUE), "is IDi payload"); + tester->assert_true(tester,(id_payload->get_id_type(id_payload) == ID_IPV6_ADDR), "is ID_IPV6_ADDR ID type"); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(id_bytes + 8, result.ptr, result.len)), "parsed nonce data"); + id_payload->destroy(id_payload); + chunk_free(&result); +} + + +/* + * Described in Header + */ +void test_parser_with_ke_payload(protected_tester_t *tester) +{ + parser_t *parser; + ke_payload_t *ke_payload; + status_t status; + chunk_t ke_chunk, result; + + u_int8_t ke_bytes[] = { + 0x00,0x00,0x00,0x18, /* payload header */ + 0x00,0x03,0x00,0x00, /* dh group 3 */ + 0x01,0x02,0x03,0x03, /* 16 Byte dh data */ + 0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + ke_chunk.ptr = ke_bytes; + ke_chunk.len = sizeof(ke_bytes); + + parser = parser_create(ke_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, KEY_EXCHANGE, (payload_t**)&ke_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + tester->assert_true(tester,(ke_payload->get_dh_group_number(ke_payload) == 3), "DH group"); + result = ke_payload->get_key_exchange_data(ke_payload); + tester->assert_true(tester,(result.len == 16), "parsed key lenght"); + tester->assert_false(tester,(memcmp(ke_bytes + 8, result.ptr, result.len)), "parsed key data"); + ke_payload->destroy(ke_payload); +} + + +/* + * Described in Header + */ +void test_parser_with_notify_payload(protected_tester_t *tester) +{ + parser_t *parser; + notify_payload_t *notify_payload; + status_t status; + chunk_t notify_chunk, result; + + u_int8_t notify_bytes[] = { + 0x00,0x00,0x00,0x1C, /* payload header */ + 0x03,0x04,0x00,0x01, + 0x01,0x02,0x03,0x03, /* spi */ + 0x04,0x05,0x06,0x07, /* noti dati */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F, + 0x0C,0x0D,0x0E,0x0F + }; + + notify_chunk.ptr = notify_bytes; + notify_chunk.len = sizeof(notify_bytes); + + parser = parser_create(notify_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, NOTIFY, (payload_t**)¬ify_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + tester->assert_true(tester,(notify_payload->get_protocol_id(notify_payload) == 3), "Protocol id"); + tester->assert_true(tester,(notify_payload->get_notify_message_type(notify_payload) == 1), "notify message type"); + + result = notify_payload->get_spi(notify_payload); + tester->assert_false(tester,(memcmp(notify_bytes + 8, result.ptr, result.len)), "parsed spi"); + + result = notify_payload->get_notification_data(notify_payload); + tester->assert_false(tester,(memcmp(notify_bytes + 12, result.ptr, result.len)), "parsed notification data"); + + notify_payload->destroy(notify_payload); +} + +/* + * Described in Header + */ +void test_parser_with_auth_payload(protected_tester_t *tester) +{ + parser_t *parser; + auth_payload_t *auth_payload; + status_t status; + chunk_t auth_chunk, result; + + u_int8_t auth_bytes[] = { + 0x00,0x00,0x00,0x14, /* payload header */ + 0x03,0x01,0x02,0x03, + 0x04,0x05,0x06,0x07,/* 12 Byte nonce */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + auth_chunk.ptr = auth_bytes; + auth_chunk.len = sizeof(auth_bytes); + + parser = parser_create(auth_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, AUTHENTICATION, (payload_t**)&auth_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = auth_payload->get_data_clone(auth_payload); + tester->assert_true(tester,(auth_payload->get_auth_method(auth_payload) == DSS_DIGITAL_SIGNATURE), "is DSS_DIGITAL_SIGNATURE method"); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(auth_bytes + 8, result.ptr, result.len)), "parsed nonce data"); + auth_payload->destroy(auth_payload); + chunk_free(&result); +} + +/* + * Described in Header + */ +void test_parser_with_ts_payload(protected_tester_t *tester) +{ + parser_t *parser; + ts_payload_t *ts_payload; + status_t status; + chunk_t ts_chunk; + traffic_selector_substructure_t *ts1, *ts2; + host_t *start_host1, *start_host2, *end_host1, *end_host2; + iterator_t *iterator; + + u_int8_t ts_bytes[] = { + /* payload header */ + 0x00,0x00,0x00,0x28, + 0x02,0x00,0x00,0x00, + + /* traffic selector 1 */ + 0x07,0x00,0x00,0x10, + 0x01,0xF4,0x01,0xF4, + 0xC0,0xA8,0x01,0x00, + 0xC0,0xA8,0x01,0xFF, + + /* traffic selector 2 */ + 0x07,0x03,0x00,0x10, + 0x00,0x00,0xFF,0xFF, + 0x00,0x00,0x00,0x00, + 0xFF,0xFF,0xFF,0xFF, + }; + + ts_chunk.ptr = ts_bytes; + ts_chunk.len = sizeof(ts_bytes); + + parser = parser_create(ts_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, TRAFFIC_SELECTOR_RESPONDER, (payload_t**)&ts_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + + iterator = ts_payload->create_traffic_selector_substructure_iterator(ts_payload,TRUE); + + tester->assert_true(tester,(iterator->has_next(iterator)), "has next check"); + + /* check first ts */ + iterator->current(iterator,(void **)&ts1); + tester->assert_true(tester,(ts1->get_protocol_id(ts1) == 0), "ip protocol id check"); + start_host1 = ts1->get_start_host(ts1); + end_host1 = ts1->get_end_host(ts1); + tester->assert_true(tester,(start_host1->get_port(start_host1) == 500), "start port check"); + tester->assert_true(tester,(end_host1->get_port(end_host1) == 500), "start port check"); + tester->assert_true(tester,(memcmp(start_host1->get_address(start_host1),"192.168.1.0",strlen("192.168.1.0")) == 0), "start address check"); + tester->assert_true(tester,(memcmp(end_host1->get_address(end_host1),"192.168.1.255",strlen("192.168.1.255")) == 0), "end address check"); + + start_host1->destroy(start_host1); + end_host1->destroy(end_host1); + + tester->assert_true(tester,(iterator->has_next(iterator)), "has next check"); + + /* check second ts */ + + iterator->current(iterator,(void **)&ts2); + + tester->assert_true(tester,(ts2->get_protocol_id(ts2) == 3), "ip protocol id check"); + start_host2 = ts2->get_start_host(ts2); + end_host2 = ts2->get_end_host(ts2); + tester->assert_true(tester,(start_host2->get_port(start_host2) == 0), "start port check"); + tester->assert_true(tester,(end_host2->get_port(end_host2) == 65535), "start port check"); + tester->assert_true(tester,(memcmp(start_host2->get_address(start_host2),"0.0.0.0",strlen("0.0.0.0")) == 0), "start address check"); + tester->assert_true(tester,(memcmp(end_host2->get_address(end_host2),"255.255.255.255",strlen("255.255.255.255")) == 0), "end address check"); + start_host2->destroy(start_host2); + end_host2->destroy(end_host2); + + + + tester->assert_false(tester,(iterator->has_next(iterator)), "has next check"); + + iterator->destroy(iterator); + + ts_payload->destroy(ts_payload); +} + +/* + * Described in Header + */ +void test_parser_with_cert_payload(protected_tester_t *tester) +{ + parser_t *parser; + cert_payload_t *cert_payload; + status_t status; + chunk_t cert_chunk, result; + + u_int8_t cert_bytes[] = { + 0x00,0x00,0x00,0x11, /* payload header */ + 0x03, + 0x04,0x05,0x06,0x07,/* 12 Byte nonce */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + cert_chunk.ptr = cert_bytes; + cert_chunk.len = sizeof(cert_bytes); + + parser = parser_create(cert_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, CERTIFICATE, (payload_t**)&cert_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = cert_payload->get_data_clone(cert_payload); + tester->assert_true(tester,(cert_payload->get_cert_encoding(cert_payload) == DNS_SIGNED_KEY), "is DNS_SIGNED_KEY encoding"); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(cert_bytes + 5, result.ptr, result.len)), "parsed data"); + cert_payload->destroy(cert_payload); + chunk_free(&result); +} + +/* + * Described in Header + */ +void test_parser_with_certreq_payload(protected_tester_t *tester) +{ + parser_t *parser; + certreq_payload_t *certreq_payload; + status_t status; + chunk_t certreq_chunk, result; + + u_int8_t certreq_bytes[] = { + 0x00,0x00,0x00,0x11, /* payload header */ + 0x03, + 0x04,0x05,0x06,0x07,/* 12 Byte data */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + certreq_chunk.ptr = certreq_bytes; + certreq_chunk.len = sizeof(certreq_bytes); + + parser = parser_create(certreq_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, CERTIFICATE_REQUEST, (payload_t**)&certreq_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = certreq_payload->get_data_clone(certreq_payload); + tester->assert_true(tester,(certreq_payload->get_cert_encoding(certreq_payload) == DNS_SIGNED_KEY), "is DNS_SIGNED_KEY encoding"); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(certreq_bytes + 5, result.ptr, result.len)), "parsed data"); + certreq_payload->destroy(certreq_payload); + chunk_free(&result); +} + +/* + * Described in Header + */ +void test_parser_with_delete_payload(protected_tester_t *tester) +{ + parser_t *parser; + delete_payload_t *delete_payload; + status_t status; + chunk_t delete_chunk, result; + + u_int8_t delete_bytes[] = { + 0x00,0x00,0x00,0x14, /* payload header */ + 0x03,0x03,0x00,0x04, + 0x04,0x05,0x06,0x07,/* 12 Byte data */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + delete_chunk.ptr = delete_bytes; + delete_chunk.len = sizeof(delete_bytes); + + parser = parser_create(delete_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, DELETE, (payload_t**)&delete_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = delete_payload->get_spis(delete_payload); + tester->assert_true(tester,(delete_payload->get_protocol_id(delete_payload) == PROTO_ESP), "is ESP protocol"); + tester->assert_true(tester,(delete_payload->get_spi_size(delete_payload) == 3), "SPI size check"); + tester->assert_true(tester,(delete_payload->get_spi_count(delete_payload) == 4), "SPI count check"); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(delete_bytes + 8, result.ptr, result.len)), "parsed data"); + tester->assert_true(tester,(((payload_t *)delete_payload)->verify((payload_t *)delete_payload) == SUCCESS), "verify check"); + + delete_payload->destroy(delete_payload); +} + + +/* + * Described in Header + */ +void test_parser_with_vendor_id_payload(protected_tester_t *tester) +{ + parser_t *parser; + vendor_id_payload_t *vendor_id_payload; + status_t status; + chunk_t vendor_id_chunk, result; + + u_int8_t vendor_id_bytes[] = { + 0x00,0x00,0x00,0x10, /* payload header */ + 0x04,0x05,0x06,0x07,/* 12 Byte data */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + vendor_id_chunk.ptr = vendor_id_bytes; + vendor_id_chunk.len = sizeof(vendor_id_bytes); + + parser = parser_create(vendor_id_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, VENDOR_ID, (payload_t**)&vendor_id_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = vendor_id_payload->get_data(vendor_id_payload); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(vendor_id_bytes + 4, result.ptr, result.len)), "parsed data"); + tester->assert_true(tester,(((payload_t *)vendor_id_payload)->verify((payload_t *)vendor_id_payload) == SUCCESS), "verify check"); + + vendor_id_payload->destroy(vendor_id_payload); +} + +/* + * Described in Header + */ +void test_parser_with_cp_payload(protected_tester_t *tester) +{ + parser_t *parser; + cp_payload_t *cp_payload; + configuration_attribute_t *attribute; + status_t status; + chunk_t cp_chunk; + iterator_t *iterator; + + /* first test generic parsing functionality */ + + u_int8_t cp_bytes[] = { + /* cp payload header */ + 0x00,0x00,0x00,0x18, + 0x05,0x00,0x00,0x00, + /* configuration attribute 1*/ + 0x00,0x03,0x00,0x04, + 0x61,0x62,0x63,0x64, + /* configuration attribute 2*/ + 0x00,0x04,0x00,0x04, + 0x65,0x66,0x67,0x68, + }; + + cp_chunk.ptr = cp_bytes; + cp_chunk.len = sizeof(cp_bytes); + + + parser = parser_create(cp_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, CONFIGURATION, (payload_t**)&cp_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + + iterator = cp_payload->create_configuration_attribute_iterator(cp_payload,TRUE); + + tester->assert_true(tester,(iterator->has_next(iterator)),"has_next call check"); + + iterator->current(iterator,(void **)&attribute); + + + tester->assert_true(tester,(attribute->get_attribute_type(attribute) == 3),"get type check"); + tester->assert_true(tester,(attribute->get_attribute_length(attribute) == 4),"get type check"); + + tester->assert_true(tester,(iterator->has_next(iterator)),"has_next call check"); + + iterator->current(iterator,(void **)&attribute); + + + tester->assert_true(tester,(attribute->get_attribute_type(attribute) == 4),"get type check"); + tester->assert_true(tester,(attribute->get_attribute_length(attribute) == 4),"get type check"); + + iterator->current(iterator,(void **)&attribute); + + tester->assert_false(tester,(iterator->has_next(iterator)),"has_next call check"); + + + iterator->destroy(iterator); + + if (status != SUCCESS) + { + return; + } + + cp_payload->destroy(cp_payload); + parser->destroy(parser); + +} + +/* + * Described in Header + */ +void test_parser_with_eap_payload(protected_tester_t *tester) +{ + parser_t *parser; + eap_payload_t *eap_payload; + status_t status; + chunk_t eap_chunk, result; + + u_int8_t eap_bytes[] = { + 0x00,0x00,0x00,0x10, /* payload header */ + 0x04,0x05,0x06,0x07,/* 12 Byte data */ + 0x08,0x09,0x0A,0x2B, + 0x0C,0x0D,0x0E,0x0F + }; + + eap_chunk.ptr = eap_bytes; + eap_chunk.len = sizeof(eap_bytes); + + parser = parser_create(eap_chunk); + tester->assert_true(tester,(parser != NULL), "parser create check"); + status = parser->parse_payload(parser, VENDOR_ID, (payload_t**)&eap_payload); + tester->assert_true(tester,(status == SUCCESS),"parse_payload call check"); + parser->destroy(parser); + + if (status != SUCCESS) + { + return; + } + result = eap_payload->get_message(eap_payload); + tester->assert_true(tester,(result.len == 12), "parsed data lenght"); + tester->assert_false(tester,(memcmp(eap_bytes + 4, result.ptr, result.len)), "parsed data"); + tester->assert_true(tester,(((payload_t *)eap_payload)->verify((payload_t *)eap_payload) == SUCCESS), "verify check"); + + eap_payload->destroy(eap_payload); +} + diff --git a/src/charon/testing/parser_test.h b/src/charon/testing/parser_test.h new file mode 100644 index 000000000..4956df13e --- /dev/null +++ b/src/charon/testing/parser_test.h @@ -0,0 +1,170 @@ +/** + * @file parser_test.h + * + * @brief Tests for the parser_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PARSER_TEST_H_ +#define PARSER_TEST_H_ + +#include + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a header payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_header_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a sa payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_sa_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a nonce payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_nonce_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a ID payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_id_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a ke payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_ke_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a notify payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_notify_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a AUTH payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_auth_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a TS payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_ts_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a CERT payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_cert_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a CERTREQ payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_certreq_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a CERTREQ payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_delete_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a VENDOR ID payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_vendor_id_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a CP payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_cp_payload(protected_tester_t *tester); + +/** + * @brief Test function used to test the parser_t functionality when + * parsing a EAP payload. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_parser_with_eap_payload(protected_tester_t *tester); + + + +#endif /*PARSER_TEST_H_*/ diff --git a/src/charon/testing/policy_test.c b/src/charon/testing/policy_test.c new file mode 100644 index 000000000..9003eeff0 --- /dev/null +++ b/src/charon/testing/policy_test.c @@ -0,0 +1,246 @@ +/** + * @file policy_test.c + * + * @brief Tests for the policy_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "policy_test.h" + +#include +#include +#include +#include +#include + + +/** + * Described in header. + */ +void test_policy(protected_tester_t *tester) +{ + policy_t *policy; +// traffic_selector_t *ts; +// linked_list_t *ts_stored, *ts_supplied, *ts_selected, *ts_expected; + proposal_t *proposal1, *proposal2, *proposal3, *proposal_sel; + linked_list_t *proposals_list; + iterator_t *iterator; + logger_t *logger; + identification_t *alice, *bob; + + logger = logger_manager->get_logger(logger_manager, TESTER); + logger->disable_level(logger, FULL); + + alice = identification_create_from_string("152.96.193.131"); + bob = identification_create_from_string("152.96.193.130"); + policy = policy_create(alice, bob); + + tester->assert_true(tester, (policy != NULL), "policy construction"); + + + /* + * test proposal getting and selection + * + */ + + /* esp only prop */ + proposal1 = proposal_create(1); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + + /* ah only prop */ + proposal2 = proposal_create(2); + proposal2->add_algorithm(proposal2, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + + /* ah and esp prop */ + proposal3 = proposal_create(3); + proposal3->add_algorithm(proposal3, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_3DES, 16); + proposal3->add_algorithm(proposal3, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + + + policy->add_proposal(policy, proposal1); + policy->add_proposal(policy, proposal2); + policy->add_proposal(policy, proposal3); + + + proposals_list = policy->get_proposals(policy); + tester->assert_true(tester, (proposals_list->get_count(proposals_list) == 3), "proposal count"); + + + proposals_list = linked_list_create(); + proposal1 = proposal_create(1); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 32); + proposal2 = proposal_create(2); + proposal2->add_algorithm(proposal2, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + proposal2->add_algorithm(proposal2, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_3DES, 16); + proposal2->add_algorithm(proposal2, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 0); + proposal2->add_algorithm(proposal2, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + proposal2->add_algorithm(proposal2, PROTO_AH, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + + proposals_list->insert_last(proposals_list, proposal1); + proposals_list->insert_last(proposals_list, proposal2); + + proposal_sel = policy->select_proposal(policy, proposals_list); + tester->assert_false(tester, proposal_sel == NULL, "proposal select"); + /* check ESP encryption algo */ + iterator = proposal_sel->create_algorithm_iterator(proposal_sel, PROTO_ESP, ENCRYPTION_ALGORITHM); + tester->assert_false(tester, iterator == NULL, "algorithm select ESP"); + while (iterator->has_next(iterator)) + { + algorithm_t *algo; + iterator->current(iterator, (void**)&algo); + tester->assert_true(tester, algo->algorithm == ENCR_3DES, "ESP encryption algo"); + tester->assert_true(tester, algo->key_size == 16, "ESP encryption keysize"); + } + iterator->destroy(iterator); + iterator = proposal_sel->create_algorithm_iterator(proposal_sel, PROTO_AH, INTEGRITY_ALGORITHM); + /* check AH integrity algo */ + tester->assert_false(tester, iterator == NULL, "algorithm select AH"); + while (iterator->has_next(iterator)) + { + algorithm_t *algo; + iterator->current(iterator, (void**)&algo); + tester->assert_true(tester, algo->algorithm == AUTH_HMAC_MD5_96, "ESP encryption algo"); + tester->assert_true(tester, algo->key_size == 20, "ESP encryption keysize"); + } + iterator->destroy(iterator); + + proposal_sel->destroy(proposal_sel); + + /* cleanup */ + proposal1->destroy(proposal1); + proposal1->destroy(proposal2); + proposals_list->destroy(proposals_list); + +// /* +// * test traffic selection getting and matching +// * +// */ +// +// ts_stored = linked_list_create(); +// +// /* allow any tcp */ +// ts = traffic_selector_create_from_string(6, TS_IPV4_ADDR_RANGE, "0.0.0.0", 0, "255.255.255.255", 65535); +// ts_stored->insert_last(ts_stored, (void*)ts); +// /* allow udp on port 123 to ".122" */ +// ts = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.122", 123, "152.96.193.122", 123); +// ts_stored->insert_last(ts_stored, (void*)ts); +// /* allow udp on ports > 2000 in subnet ... */ +// ts = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.0", 2000, "152.96.193.255", 65535); +// ts_stored->insert_last(ts_stored, (void*)ts); +// +// +// +// /* define request and result */ +// +// /* udp on subnet:123, should be reduced to ".122" */ +// ts = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.0", 123, "152.96.193.255", 123); +// ts_supplied->insert_last(ts_supplied, (void*)ts); +// ts_reference[0] = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.122", 123, "152.96.193.122", 123); +// +// /* should be granted. */ +// ts_request[1] = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.0", 2000, "152.96.193.255", 2000); +// ts_reference[1] = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.0", 2000, "152.96.193.255", 2000); +// +// /* should be reduced to port 2000 - 3000. and range ".193.*" */ +// ts_request[2] = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.191.0", 1000, "152.96.194.255", 3000); +// ts_reference[2] = traffic_selector_create_from_string(7, TS_IPV4_ADDR_RANGE, "152.96.193.0", 2000, "152.96.193.255", 3000); +// +// /* icmp request, should be discarded */ +// ts_request[3] = traffic_selector_create_from_string(1, TS_IPV4_ADDR_RANGE, "0.0.0.0", 0, "255.255.255.255", 65535); +// +// policy->add_my_traffic_selector(policy, ts_policy[0]); +// policy->add_my_traffic_selector(policy, ts_policy[1]); +// policy->add_my_traffic_selector(policy, ts_policy[2]); +// +// count = policy->get_my_traffic_selectors(policy, &ts_result); +// tester->assert_true(tester, (count == 3), "ts get count"); +// ts_result[0]->destroy(ts_result[0]); +// ts_result[0]->destroy(ts_result[1]); +// ts_result[0]->destroy(ts_result[2]); +// free(ts_result); +// +// count = policy->select_my_traffic_selectors(policy, &ts_request[0], 4, &ts_result); +// tester->assert_true(tester, (count == 3), "ts select count"); +// +// +// /* store and restore into ts payload, tricky tricky */ +// ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_result, count); +// +// /* destroy */ +// ts_result[0]->destroy(ts_result[0]); +// ts_result[0]->destroy(ts_result[1]); +// ts_result[0]->destroy(ts_result[2]); +// free(ts_result); +// +// /* get them again out of the payload */ +// count = ts_payload->get_traffic_selectors(ts_payload, &ts_result); +// ts_payload->destroy(ts_payload); +// +// +// +// int i; +// for (i = 0; iget_from_address(ts_result[i]); +// chunk_t fa_ref = ts_reference[i]->get_from_address(ts_reference[i]); +// chunk_t ta_res = ts_result[i]->get_to_address(ts_result[i]); +// chunk_t ta_ref = ts_reference[i]->get_to_address(ts_reference[i]); +// u_int16_t fp_res = ts_result[i]->get_from_port(ts_result[i]); +// u_int16_t fp_ref = ts_reference[i]->get_from_port(ts_reference[i]); +// u_int16_t tp_res = ts_result[i]->get_to_port(ts_result[i]); +// u_int16_t tp_ref = ts_reference[i]->get_to_port(ts_reference[i]); +// +// +// logger->log_chunk(logger, RAW, "from address result", fa_res); +// logger->log_chunk(logger, RAW, "from address reference", fa_ref); +// logger->log_chunk(logger, RAW, "to address result", ta_res); +// logger->log_chunk(logger, RAW, "to address reference", ta_ref); +// tester->assert_true(tester, fa_res.len == fa_ref.len, "from address len"); +// tester->assert_false(tester, memcmp(fa_res.ptr, fa_ref.ptr,fa_res.len), "from address value"); +// tester->assert_true(tester, ta_res.len == ta_ref.len, "to address len"); +// tester->assert_false(tester, memcmp(ta_res.ptr, ta_ref.ptr,ta_res.len), "to address value"); +// +// tester->assert_true(tester, fp_res == fp_ref, "from port"); +// tester->assert_true(tester, tp_res == tp_ref, "to port"); +// +// free(fa_res.ptr); +// free(fa_ref.ptr); +// free(ta_res.ptr); +// free(ta_ref.ptr); +// } +// +// +// /* destroy */ +// ts_result[0]->destroy(ts_result[0]); +// ts_result[0]->destroy(ts_result[1]); +// ts_result[0]->destroy(ts_result[2]); +// free(ts_result); +// +// ts_policy[0]->destroy(ts_policy[0]); +// ts_policy[1]->destroy(ts_policy[1]); +// ts_policy[2]->destroy(ts_policy[2]); +// ts_request[0]->destroy(ts_request[0]); +// ts_reference[0]->destroy(ts_reference[0]); +// ts_request[1]->destroy(ts_request[1]); +// ts_reference[1]->destroy(ts_reference[1]); +// ts_request[2]->destroy(ts_request[2]); +// ts_reference[2]->destroy(ts_reference[2]); +// ts_request[3]->destroy(ts_request[3]); + + policy->destroy(policy); +} diff --git a/src/charon/testing/policy_test.h b/src/charon/testing/policy_test.h new file mode 100644 index 000000000..6c8072a9c --- /dev/null +++ b/src/charon/testing/policy_test.h @@ -0,0 +1,42 @@ +/** + * @file policy_test.h + * + * @brief Tests for the policy_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SA_CONFIG_TEST_H_ +#define SA_CONFIG_TEST_H_ + +#include + +/** + * @brief Test function used to test the policy_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_policy(protected_tester_t *tester); + +#endif /* SA_CONFIG_TEST_H_ */ + + + + diff --git a/src/charon/testing/prf_plus_test.c b/src/charon/testing/prf_plus_test.c new file mode 100644 index 000000000..818c5c17e --- /dev/null +++ b/src/charon/testing/prf_plus_test.c @@ -0,0 +1,145 @@ +/** + * @file prf_plus_test.h + * + * @brief Tests for the prf_plus_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "prf_plus_test.h" + +#include + + +/* + * described in Header-File + */ +void test_prf_plus(protected_tester_t *tester) +{ + prf_plus_t *prf_plus; + prf_t *prf; + chunk_t key, seed; + u_int8_t buffer[10000]; + int i; + + u_int8_t key_bytes[] = { + 0x01,0x02,0x03,0x04 + }; + u_int8_t seed_bytes[] = { + 0x01,0x02,0x03,0x04 + }; + + + + key.ptr = key_bytes; + key.len = sizeof(key_bytes); + seed.ptr = seed_bytes; + seed.len = sizeof(seed_bytes); + + prf = prf_create(PRF_HMAC_SHA1); + prf->set_key(prf, key); + + prf_plus = prf_plus_create(prf, seed); + + + for (i=0; i<100; i++) + { + prf_plus->get_bytes(prf_plus, i*i, buffer); + + } + + //tester->assert_true(tester, digest[3].len == 20, "chunk len append mode"); + //tester->assert_false(tester, memcmp(digest[3].ptr, reference[3].ptr, 20), "prf_plus value append mode"); + + prf_plus->destroy(prf_plus); + prf->destroy(prf); +} + +void test_prf_plus_md5(protected_tester_t *tester) +{ + /* md5 test data + u_int8_t nonce[] = { + 0x58,0xCC,0x4C,0xA3,0x81,0x81,0xDA,0x7D, + 0x19,0xA6,0x9F,0xB1,0xE8,0xD3,0xE7,0x96, + 0xC2,0x2A,0x6E,0xCB,0x09,0x43,0xDC,0x6E, + 0x75,0x22,0x34,0xAE,0xF8,0x53,0x7F,0xEC, + 0x00,0xC9,0xF6,0x1C,0x4A,0x39,0xB4,0x29, + 0x23,0xD8,0x24,0x22,0x95,0x52,0x77,0x29 + }; + + u_int8_t shared_key[] = { + 0xC0,0xDB,0x75,0x0A,0x40,0xBE,0xE2,0x8C,0x68,0x3C,0xB4,0xAA,0xE7,0xA7,0x6E,0xCC, + 0x2A,0x4B,0x9C,0x8E,0xC6,0x71,0xAD,0xF4,0xB7,0xC4,0xD6,0x53,0x41,0xB3,0x4A,0xE4, + 0x0D,0xC2,0x0C,0x60,0x9F,0x93,0x9E,0x87,0x30,0xCC,0xDC,0x51,0x9F,0x94,0x91,0x5D, + 0x31,0xE0,0x6E,0x22,0x3A,0x66,0x53,0xA6,0xD4,0x54,0x5E,0x71,0x61,0xA6,0x64,0x3B, + 0x19,0x40,0x6E,0x6F,0x3B,0xE3,0x64,0x3F,0x3B,0x68,0xEB,0x8E,0x4B,0x2A,0x53,0xEC, + 0xB0,0xB6,0x8E,0x5C,0x42,0xA1,0xC2,0x7F,0x4F,0x0B,0x7D,0xFC,0xF6,0x7E,0xF5,0xC0, + 0xBA,0xA8,0xFB,0x13,0xEF,0xA8,0xBD,0x90,0x95,0x08,0x2C,0x81,0xA9,0xDA,0x7D,0x45, + 0xDC,0x35,0x33,0x75,0xA8,0x4D,0xE2,0x34,0xA9,0x66,0x7F,0xAD,0x04,0x3A,0xE5,0x21 + }; + + u_int8_t skeyseed[] = { + 0xCD,0xC6,0xC0,0x68, + 0x60,0xDF,0x0C,0xC2, + 0x10,0xDB,0x0E,0xF7, + 0x20,0x6E,0x6C,0xB1 + }; + u_int8_t sk_d[] = { + 0xE1,0x74,0xA8,0x50, + 0x14,0xDB,0x79,0x64, + 0x92,0x3E,0x82,0x28, + 0x48,0x75,0x64,0xE7 + }; + u_int8_t sk_ai[] = { + 0xCA,0x19,0x73,0x69, + 0x38,0x35,0x40,0xA6, + 0xB1,0x98,0x4F,0x63, + 0xE6,0xF9,0x66,0xFF + }; + u_int8_t sk_ar[] = { + 0x14,0x1D,0x0A,0xC2, + 0x7B,0x1C,0x87,0xD2, + 0x65,0xA5,0xEF,0x0C, + 0x47,0xF4,0xCE,0xE2 + }; + u_int8_t sk_ei[] = { + 0x52,0x50,0x7E,0xDA, + 0x02,0x1D,0x8E,0xCF, + 0x20,0xA3,0x67,0xA6, + 0x4D,0xA0,0xAB,0x61 + }; + u_int8_t sk_er[] = { + 0xB9,0x65,0x0A,0x3C, + 0x30,0xA8,0x26,0x78, + 0x60,0x5A,0x74,0xBB, + 0x5C,0xC4,0xF8,0x71 + }; + u_int8_t sk_pi[] = { + 0xDD,0x61,0xAB,0x53, + 0xC8,0xDD,0x3A,0x44, + 0xDA,0x47,0x09,0x9B, + 0x3B,0xD2,0xBB,0xB6 + }; + u_int8_t sk_pr[] = { + 0x18,0x75,0xE4,0xC6, + 0x57,0xC4,0xDE,0x65, + 0x10,0xEB,0xA7,0xB6, + 0x24,0x0D,0xEC,0xB4 + };*/ +} diff --git a/src/charon/testing/prf_plus_test.h b/src/charon/testing/prf_plus_test.h new file mode 100644 index 000000000..2ad8ce0c1 --- /dev/null +++ b/src/charon/testing/prf_plus_test.h @@ -0,0 +1,38 @@ +/** + * @file prf_plus_test.h + * + * @brief Tests for the prf_plus_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 PRF_PLUS_TEST_H_ +#define PRF_PLUS_TEST_H_ + +#include +#include + +/** + * @brief Test function used to test the prf_plus class. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_prf_plus(protected_tester_t *tester); + +#endif /*PRF_PLUS_TEST_H_*/ diff --git a/src/charon/testing/proposal_test.c b/src/charon/testing/proposal_test.c new file mode 100644 index 000000000..1b16390d3 --- /dev/null +++ b/src/charon/testing/proposal_test.c @@ -0,0 +1,98 @@ +/** + * @file proposal_test.c + * + * @brief Tests for the proposal_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "proposal_test.h" + +#include +#include +#include + + +/** + * Described in header. + */ +void test_proposal(protected_tester_t *tester) +{ + proposal_t *proposal1, *proposal2, *proposal3; + iterator_t *iterator; + algorithm_t *algo; + bool result; + + proposal1 = proposal_create(1); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_3DES, 0); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 32); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + proposal1->add_algorithm(proposal1, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_BLOWFISH, 0); + proposal1->add_algorithm(proposal1, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 20); + proposal1->add_algorithm(proposal1, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + proposal1->add_algorithm(proposal1, PROTO_AH, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + proposal1->add_algorithm(proposal1, PROTO_AH, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0); + + proposal2 = proposal_create(2); + proposal2->add_algorithm(proposal2, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_3IDEA, 0); + proposal2->add_algorithm(proposal2, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); + proposal2->add_algorithm(proposal2, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 20); + proposal1->add_algorithm(proposal2, PROTO_AH, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0); + + /* ah and esp prop */ + proposal3 = proposal1->select(proposal1, proposal2); + tester->assert_false(tester, proposal3 == NULL, "proposal select"); + if (proposal3) + { + result = proposal3->get_algorithm(proposal3, PROTO_ESP, ENCRYPTION_ALGORITHM, &algo); + tester->assert_true(tester, result, "encryption algo select"); + tester->assert_true(tester, algo->algorithm == ENCR_AES_CBC, "encryption algo"); + tester->assert_true(tester, algo->key_size == 16, "encryption keylen"); + + + result = proposal3->get_algorithm(proposal3, PROTO_ESP, INTEGRITY_ALGORITHM, &algo); + tester->assert_true(tester, result, "integrity algo select"); + tester->assert_true(tester, algo->algorithm == AUTH_HMAC_MD5_96, "integrity algo"); + tester->assert_true(tester, algo->key_size == 20, "integrity keylen"); + + iterator = proposal3->create_algorithm_iterator(proposal3, PROTO_ESP, INTEGRITY_ALGORITHM); + tester->assert_false(tester, iterator == NULL, "integrity algo select"); + while(iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&algo); + tester->assert_true(tester, algo->algorithm == AUTH_HMAC_MD5_96, "integrity algo"); + tester->assert_true(tester, algo->key_size == 20, "integrity keylen"); + } + iterator->destroy(iterator); + + iterator = proposal3->create_algorithm_iterator(proposal3, PROTO_AH, DIFFIE_HELLMAN_GROUP ); + tester->assert_false(tester, iterator == NULL, "dh group algo select"); + while(iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&algo); + tester->assert_true(tester, algo->algorithm == MODP_1024_BIT, "dh group algo"); + tester->assert_true(tester, algo->key_size == 0, "dh gorup keylen"); + } + iterator->destroy(iterator); + + proposal3->destroy(proposal3); + } + + proposal1->destroy(proposal1); + proposal2->destroy(proposal2); + return; +} diff --git a/src/charon/testing/proposal_test.h b/src/charon/testing/proposal_test.h new file mode 100644 index 000000000..059af11cc --- /dev/null +++ b/src/charon/testing/proposal_test.h @@ -0,0 +1,42 @@ +/** + * @file proposal_test.h + * + * @brief Tests for the proposal_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_PROPOSAL_TEST_H_ +#define CHILD_PROPOSAL_TEST_H_ + +#include + +/** + * @brief Test function used to test the proposal_t functionality. + * + * @param tester associated protected_tester_t object + * + * @ingroup testcases + */ +void test_proposal(protected_tester_t *tester); + +#endif /* CHILD_PROPOSAL_TEST_H_ */ + + + + diff --git a/src/charon/testing/rsa_test.c b/src/charon/testing/rsa_test.c new file mode 100644 index 000000000..696901531 --- /dev/null +++ b/src/charon/testing/rsa_test.c @@ -0,0 +1,226 @@ +/** + * @file rsa_test.h + * + * @brief Tests for the hasher_t classes. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "rsa_test.h" + +#include +#include +#include + +char private_key_buffer[] = { + 0x30,0x82,0x04,0xa2,0x02,0x00,0x02,0x82,0x01,0x00,0x6f,0x25,0x74,0x63,0x2a,0x2f, + 0x5d,0xd4,0x54,0x03,0xbe,0xd5,0x34,0x71,0xe0,0x30,0x37,0xe5,0x2e,0x39,0xda,0xe7, + 0x04,0xd4,0xe2,0x5b,0x43,0xc3,0x6a,0x50,0x61,0xe8,0x4b,0x5d,0x58,0x30,0xa4,0xcc, + 0x6d,0xab,0xdf,0x8b,0x75,0x8c,0x22,0x43,0xd4,0xd0,0x18,0x5e,0x32,0x24,0xba,0x38, + 0x6f,0xab,0x64,0x86,0x8f,0x54,0x40,0x77,0xcb,0x3a,0xb5,0x30,0xde,0xb4,0xcd,0x98, + 0xbb,0xb1,0xdb,0x7a,0xd4,0xc7,0x0e,0xc7,0x99,0xc4,0x05,0xd5,0xa4,0x96,0x2a,0x3c, + 0x71,0x0f,0x31,0xa4,0xcd,0xc2,0x15,0x28,0xec,0x16,0x02,0x28,0x61,0x5e,0x8e,0xcf, + 0xb6,0x0b,0x8c,0x81,0x79,0x58,0xfc,0x9b,0x5b,0x32,0x26,0xcb,0xbc,0xf2,0xc9,0x8a, + 0x76,0x26,0x4e,0x87,0xaa,0x1b,0xd4,0xa7,0xb3,0xcf,0x96,0x99,0x86,0xcc,0xcb,0x5e, + 0xb2,0x66,0xc9,0xe0,0x10,0xbe,0xf7,0xd9,0x99,0xa3,0x49,0x5c,0x41,0x1f,0xa4,0xd0, + 0xd0,0x48,0x77,0xad,0x0f,0xbc,0x2c,0x2a,0x29,0x34,0x3f,0x20,0xb5,0x15,0xa1,0xa7, + 0x22,0xda,0x15,0xf3,0xf1,0x51,0x83,0x1f,0x3d,0x49,0x26,0x81,0x6d,0x65,0xa6,0x9c, + 0x09,0x01,0xfa,0x10,0x26,0x76,0xec,0x46,0x77,0xa6,0xc1,0xf5,0xc7,0xa3,0x2d,0xf9, + 0x60,0xa1,0x8f,0x94,0x17,0x58,0x0d,0xc9,0x55,0x50,0x2a,0xeb,0x44,0x5e,0xea,0x69, + 0xc9,0x76,0x67,0x9c,0x8e,0xd1,0x9c,0x4f,0x9d,0x9e,0x0a,0xec,0x44,0x6b,0x7e,0x01, + 0x2d,0x53,0xf2,0xd6,0x7c,0x27,0x30,0x3d,0x40,0x6c,0x3c,0xef,0x82,0xd1,0x7f,0xe2, + 0xd2,0x9b,0xb6,0x96,0x08,0xd2,0xe0,0x8a,0x28,0xeb,0x02,0x03,0x01,0x00,0x01,0x02, + 0x82,0x01,0x00,0x0b,0x89,0xcc,0x5c,0xd5,0x0e,0xc8,0xc3,0x57,0x9b,0x71,0xee,0xa9, + 0x3c,0x9f,0x24,0xf2,0x50,0x88,0xed,0x79,0xa3,0x9c,0xf5,0x4a,0xb0,0x65,0xc6,0xfe, + 0x1c,0xed,0x25,0x13,0xd9,0xd3,0x63,0x6d,0x60,0x49,0x8c,0x5b,0xaf,0x1b,0x1b,0x5a, + 0x9d,0x47,0x14,0xf9,0x4a,0xa2,0x12,0xfd,0x00,0x09,0xdb,0xb5,0x9a,0x60,0x7b,0xc3, + 0x1b,0x8c,0x8e,0x02,0x2c,0x5a,0x1a,0x53,0xf3,0xa4,0x9c,0x90,0xa7,0xde,0x39,0xf1, + 0xf7,0x57,0xa7,0xa9,0x61,0x65,0xee,0x2e,0xe1,0x4a,0x6d,0x64,0xde,0x72,0x7b,0xd0, + 0xfd,0x88,0x10,0xba,0xd5,0x9d,0x52,0x17,0x2a,0x4a,0x00,0x45,0xec,0x55,0x00,0x1f, + 0x6d,0x33,0x58,0xef,0xfd,0x1b,0x96,0xea,0xc4,0x44,0x82,0xb2,0x89,0x53,0xe8,0x02, + 0xba,0x0c,0x28,0xff,0xd1,0xda,0xdc,0xea,0xae,0x80,0xbe,0x33,0x86,0xbc,0x38,0xe9, + 0x1c,0xa9,0x39,0xc5,0x28,0x14,0x53,0x5a,0x52,0x3e,0xff,0xb8,0xc6,0x77,0x7c,0xe2, + 0xf9,0x50,0x9a,0x58,0x46,0x4e,0xdf,0x11,0x0f,0x4d,0x70,0xf3,0xe7,0xe7,0x9a,0x8a, + 0x9f,0x58,0x05,0x54,0xda,0x60,0x52,0xec,0xa8,0x10,0x60,0x9c,0x83,0xf0,0xd7,0x15, + 0xaf,0xf9,0x44,0xe8,0x3e,0x68,0xb2,0x06,0xa5,0x6d,0x6d,0xc1,0x8d,0x55,0xa3,0x6e, + 0x7e,0xe4,0x86,0x2a,0x6e,0x23,0xe0,0xf5,0xe6,0x08,0x05,0xb5,0x1a,0x6d,0x9c,0xf4, + 0xbd,0x18,0x20,0x58,0x43,0x67,0x72,0xde,0x47,0x56,0xee,0x94,0xc6,0x70,0xc1,0xda, + 0x15,0x2a,0xb9,0xb7,0x6a,0x10,0xc4,0x02,0x6e,0xae,0x93,0xe8,0x5e,0x8f,0x55,0x80, + 0x4e,0x9a,0x75,0x02,0x81,0x81,0x00,0xc4,0x25,0x05,0xab,0x8d,0x6b,0xa0,0xac,0x82, + 0x44,0xe2,0x13,0x06,0x2e,0x1c,0x7b,0x6b,0x37,0x69,0x93,0x9e,0x33,0x41,0xb1,0xf0, + 0x54,0x6e,0xe1,0x52,0x98,0x83,0x36,0x2b,0xe4,0x86,0x85,0x19,0x53,0x1f,0xd7,0x2f, + 0xbb,0x76,0xef,0x8d,0xb1,0x42,0xd3,0xfc,0xba,0xc7,0xb6,0xe4,0x73,0x42,0x83,0x1d, + 0x08,0xf9,0x17,0x83,0xd3,0xb7,0xe7,0xb4,0x26,0x74,0x59,0xb6,0x07,0xe1,0x1f,0x97, + 0x1e,0x66,0x77,0xe2,0x7a,0x3e,0xb2,0x43,0x2a,0x60,0x34,0xa6,0x2e,0x4a,0x13,0xb9, + 0x4f,0xc3,0x64,0xc5,0xee,0x04,0x40,0xf4,0xa5,0x01,0x45,0xba,0x9e,0x09,0x22,0xd9, + 0x99,0x0c,0x0e,0x23,0xd9,0x43,0x8b,0x01,0x1a,0x3f,0xd4,0xa8,0x8d,0x9a,0xfc,0x9c, + 0x05,0x1d,0x6d,0x7b,0x18,0xe0,0x95,0x02,0x81,0x81,0x00,0x91,0x10,0x4b,0x84,0xdc, + 0x10,0x67,0x22,0x84,0x60,0x96,0x2e,0x11,0x1a,0xe9,0x1c,0xb7,0x2f,0xa4,0x4c,0xf4, + 0xd0,0x57,0xa2,0x4b,0xbc,0xa2,0x02,0x0f,0x33,0x1b,0x1f,0x19,0x19,0x68,0x8d,0xb6, + 0x8a,0x36,0xe3,0xeb,0x2c,0x8c,0xba,0x69,0xb4,0x17,0x97,0xfe,0x0b,0x76,0x2a,0x97, + 0x87,0x0c,0xdf,0x1e,0x7a,0xbc,0xc0,0x86,0x27,0x31,0xb9,0x9d,0xc2,0xf2,0xb7,0xcc, + 0x83,0x6a,0x5a,0xa1,0xab,0x05,0x60,0xa0,0x04,0x90,0xe2,0xc4,0x03,0xb4,0xd8,0x30, + 0xaa,0x93,0xd8,0x90,0x4e,0x3c,0x33,0x1f,0x43,0xa2,0x3a,0x2c,0x34,0xb9,0x01,0x89, + 0xbb,0xdc,0x0b,0x2e,0x4f,0x89,0x1b,0xf8,0x77,0x4c,0x4c,0x25,0xc5,0xca,0x38,0x00, + 0xd4,0x3a,0xaa,0x7c,0xf6,0xb6,0xad,0x69,0x0d,0x03,0x7f,0x02,0x81,0x81,0x00,0xa3, + 0xcc,0xef,0x21,0x46,0xe6,0xdc,0xb5,0x73,0xcc,0xa6,0xa7,0x90,0x7f,0xad,0x95,0x7c, + 0x02,0x38,0x8e,0xe8,0x8c,0x91,0x8e,0x51,0xcf,0x91,0x11,0x66,0x72,0xab,0x10,0xf0, + 0x32,0xd6,0x0c,0x0d,0x0c,0x18,0x09,0x12,0x79,0x91,0x67,0x98,0x82,0xb1,0xf6,0x6a, + 0x96,0x68,0xf6,0x59,0x6d,0xcf,0xdb,0xc2,0xc1,0x9d,0x93,0x7f,0xa9,0xad,0x69,0x38, + 0x4e,0xec,0xd7,0x86,0x66,0xaa,0x20,0x41,0x89,0x47,0xb5,0x52,0x53,0x18,0x4c,0xb2, + 0x3e,0x8f,0x3d,0x28,0x92,0x7b,0x96,0x61,0x29,0x35,0x59,0xd0,0xd9,0x66,0x80,0x00, + 0x4e,0x53,0xf3,0xb1,0x57,0x0c,0xf6,0x27,0x95,0xe2,0x35,0x64,0xc6,0xa9,0xdb,0x49, + 0xbe,0x6c,0x13,0xe1,0xf6,0xef,0xb9,0x89,0x69,0xd4,0x1b,0x7b,0xb3,0x58,0xc9,0x02, + 0x81,0x80,0x40,0x28,0x3d,0xce,0x37,0xea,0x05,0x43,0x2d,0xda,0xed,0xf0,0xd7,0xdd, + 0xd8,0x05,0xbc,0x3b,0x14,0xe6,0x78,0x4c,0x00,0xc6,0x25,0xca,0xfa,0xb8,0x00,0x72, + 0xf0,0xe6,0xd3,0x19,0xfa,0xb4,0xda,0x6b,0xcc,0x95,0x06,0xf9,0x00,0x10,0x9e,0x19, + 0x69,0x69,0xee,0x90,0xb1,0x25,0x6b,0x38,0xee,0x87,0x6b,0x9a,0x8b,0x0a,0x77,0x0a, + 0xb4,0xa2,0x4c,0x54,0xe1,0x36,0x4a,0xfc,0x40,0x38,0x6f,0x52,0x0d,0x21,0xcc,0x03, + 0xd8,0xf4,0x82,0x0e,0xc5,0x97,0xec,0x06,0x35,0x37,0x4d,0xb3,0x5c,0x4a,0x9b,0xe4, + 0x34,0xc6,0x97,0xb0,0x85,0xb6,0x59,0x6d,0x3d,0x87,0xb0,0x66,0xba,0xd4,0x25,0x12, + 0xd6,0x2a,0xc3,0x75,0xf3,0xd6,0xca,0xff,0x12,0x27,0x3e,0xf7,0x7a,0x99,0xbd,0x61, + 0x65,0x0f,0x02,0x81,0x81,0x00,0xb2,0xcb,0x21,0xf9,0x77,0x44,0x20,0xee,0xe9,0x60, + 0xf2,0x32,0x7e,0xd0,0xb2,0x8b,0xa7,0x96,0x20,0x20,0xf2,0x88,0xbd,0xbe,0x1f,0x92, + 0x59,0x26,0x7c,0x26,0x64,0x13,0xfc,0x9a,0x1c,0xd6,0x48,0xbf,0xe3,0xad,0x2d,0x89, + 0xd4,0x11,0x9b,0xed,0x38,0x99,0x3e,0xf4,0xe3,0x54,0xa3,0x0c,0x2a,0x91,0xdc,0xf9, + 0x38,0x94,0xbe,0xd7,0x90,0xc2,0x8d,0xcc,0x5a,0x28,0xbd,0x46,0x4e,0xd7,0x86,0x52, + 0x95,0xb1,0x39,0xb9,0x30,0x33,0x1f,0xe8,0xe7,0x37,0xfe,0x37,0xa5,0x20,0x82,0x1a, + 0xfd,0xc3,0x30,0xd0,0xdc,0x8d,0x71,0x66,0x30,0xb4,0x9a,0xb2,0xd6,0x03,0xfe,0xc5, + 0x4b,0xfd,0xd2,0x1b,0x3e,0x4e,0xc6,0xb0,0xe8,0x6c,0x83,0x44,0x6b,0xaa,0x05,0x51, + 0xd3,0xb2,0x04,0xca,0xf6,0xf3, +}; + +char public_key_buffer[] = { +// 0x30,0x82,0x01,0x21,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01, +// 0x01,0x05,0x00,0x03,0x82,0x01,0x0e,0x00, + 0x30,0x82,0x01,0x09,0x02,0x82,0x01,0x00, + 0x6f,0x25,0x74,0x63,0x2a,0x2f,0x5d,0xd4,0x54,0x03,0xbe,0xd5,0x34,0x71,0xe0,0x30, + 0x37,0xe5,0x2e,0x39,0xda,0xe7,0x04,0xd4,0xe2,0x5b,0x43,0xc3,0x6a,0x50,0x61,0xe8, + 0x4b,0x5d,0x58,0x30,0xa4,0xcc,0x6d,0xab,0xdf,0x8b,0x75,0x8c,0x22,0x43,0xd4,0xd0, + 0x18,0x5e,0x32,0x24,0xba,0x38,0x6f,0xab,0x64,0x86,0x8f,0x54,0x40,0x77,0xcb,0x3a, + 0xb5,0x30,0xde,0xb4,0xcd,0x98,0xbb,0xb1,0xdb,0x7a,0xd4,0xc7,0x0e,0xc7,0x99,0xc4, + 0x05,0xd5,0xa4,0x96,0x2a,0x3c,0x71,0x0f,0x31,0xa4,0xcd,0xc2,0x15,0x28,0xec,0x16, + 0x02,0x28,0x61,0x5e,0x8e,0xcf,0xb6,0x0b,0x8c,0x81,0x79,0x58,0xfc,0x9b,0x5b,0x32, + 0x26,0xcb,0xbc,0xf2,0xc9,0x8a,0x76,0x26,0x4e,0x87,0xaa,0x1b,0xd4,0xa7,0xb3,0xcf, + 0x96,0x99,0x86,0xcc,0xcb,0x5e,0xb2,0x66,0xc9,0xe0,0x10,0xbe,0xf7,0xd9,0x99,0xa3, + 0x49,0x5c,0x41,0x1f,0xa4,0xd0,0xd0,0x48,0x77,0xad,0x0f,0xbc,0x2c,0x2a,0x29,0x34, + 0x3f,0x20,0xb5,0x15,0xa1,0xa7,0x22,0xda,0x15,0xf3,0xf1,0x51,0x83,0x1f,0x3d,0x49, + 0x26,0x81,0x6d,0x65,0xa6,0x9c,0x09,0x01,0xfa,0x10,0x26,0x76,0xec,0x46,0x77,0xa6, + 0xc1,0xf5,0xc7,0xa3,0x2d,0xf9,0x60,0xa1,0x8f,0x94,0x17,0x58,0x0d,0xc9,0x55,0x50, + 0x2a,0xeb,0x44,0x5e,0xea,0x69,0xc9,0x76,0x67,0x9c,0x8e,0xd1,0x9c,0x4f,0x9d,0x9e, + 0x0a,0xec,0x44,0x6b,0x7e,0x01,0x2d,0x53,0xf2,0xd6,0x7c,0x27,0x30,0x3d,0x40,0x6c, + 0x3c,0xef,0x82,0xd1,0x7f,0xe2,0xd2,0x9b,0xb6,0x96,0x08,0xd2,0xe0,0x8a,0x28,0xeb, + 0x02,0x03,0x01,0x00,0x01 + +}; + +/* + * described in Header-File + */ +void test_rsa(protected_tester_t *tester) +{ + rsa_private_key_t *private_key; + rsa_public_key_t *public_key; + x509_t *certificate; + chunk_t data, signature; + chunk_t der_private_key = {private_key_buffer, sizeof(private_key_buffer)}; + chunk_t der_public_key = {public_key_buffer, sizeof(public_key_buffer)}; + logger_t *logger; + status_t status; + + u_int8_t test_data[] = { + 0x01,0x02,0x03,0x04, + 0x11,0x12,0x13,0x14, + 0x21,0x22,0x23,0x24, + 0x31,0x32,0x33,0x34, + 0x41,0x42,0x43,0x44, + 0x51,0x52,0x53,0x54, + 0x61,0x62,0x63,0x64, + 0x71,0x72,0x73,0x74, + 0x81,0x82,0x83,0x84, + }; + data.ptr = test_data; + data.len = sizeof(test_data); + + logger = logger_manager->get_logger(logger_manager, TESTER); + logger->disable_level(logger, FULL); + + /* key generation and signing */ +// private_key = rsa_private_key_create(512); +// tester->assert_true(tester, private_key != NULL, "generating private key"); +// +// status = private_key->build_emsa_pkcs1_signature(private_key, HASH_MD5, data, &signature); +// tester->assert_true(tester, status == SUCCESS, "build emsa_pkcs1_signature (genkey)"); +// +// public_key = private_key->get_public_key(private_key); +// tester->assert_true(tester, public_key != NULL, "extracting public key"); +// +// status = public_key->verify_emsa_pkcs1_signature(public_key, data, signature); +// tester->assert_true(tester, status == SUCCESS, "verify emsa_pkcs1_signature (genkey)"); +// +// free(signature.ptr); +// +// private_key->destroy(private_key); +// public_key->destroy(public_key); + + /* key setting */ + private_key = rsa_private_key_create_from_chunk(der_private_key); + tester->assert_true(tester, private_key != NULL, "loading private key from chunk"); + public_key = rsa_public_key_create_from_chunk(der_public_key); + tester->assert_true(tester, public_key != NULL, "loading public key from chunk"); + + status = private_key->build_emsa_pkcs1_signature(private_key, HASH_MD5, data, &signature); + tester->assert_true(tester, status == SUCCESS, "build emsa_pkcs1_signature (setkey)"); + status = public_key->verify_emsa_pkcs1_signature(public_key, data, signature); + tester->assert_true(tester, status == SUCCESS, "verify emsa_pkcs1_signature (setkey)"); + + free(signature.ptr); + + /* key comparison */ + tester->assert_true(tester, private_key->belongs_to(private_key, public_key), "key belongs to"); + + private_key->destroy(private_key); + private_key = rsa_private_key_create(512); + tester->assert_false(tester, private_key->belongs_to(private_key, public_key), "key belongs not to"); + + public_key->destroy(public_key); + private_key->destroy(private_key); + + /* key loading */ + private_key = rsa_private_key_create_from_file("alice.der", NULL); + tester->assert_true(tester, private_key != NULL, "loading private key from file"); + certificate = x509_create_from_file("alice-cert.der"); + tester->assert_true(tester, public_key != NULL, "loading certificate from file"); + public_key = certificate->get_public_key(certificate); + tester->assert_true(tester, public_key != NULL, "loading public key from certificate"); + + tester->assert_true(tester, private_key->belongs_to(private_key, public_key), "key belongs to"); + + status = private_key->build_emsa_pkcs1_signature(private_key, HASH_SHA1, data, &signature); + tester->assert_true(tester, status == SUCCESS, "build emsa_pkcs1_signature (loadkey)"); + status = public_key->verify_emsa_pkcs1_signature(public_key, data, signature); + tester->assert_true(tester, status == SUCCESS, "verify emsa_pkcs1_signature (loadkey)"); + + free(signature.ptr); + + certificate->destroy(certificate); + public_key->destroy(public_key); + private_key->destroy(private_key); + +} diff --git a/src/charon/testing/rsa_test.h b/src/charon/testing/rsa_test.h new file mode 100644 index 000000000..baeccf402 --- /dev/null +++ b/src/charon/testing/rsa_test.h @@ -0,0 +1,41 @@ +/** + * @file rsa_test.h + * + * @brief Tests for the rsa_public_key_t and rsa_private_key classes. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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_TEST_H +#define RSA_TEST_H + +#include +#include + +#include + +/** + * @brief Test function used to test the rsa functionality. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_rsa(protected_tester_t *tester); + + +#endif /*RSA_TEST_H*/ diff --git a/src/charon/testing/scheduler_test.c b/src/charon/testing/scheduler_test.c new file mode 100644 index 000000000..de7346d83 --- /dev/null +++ b/src/charon/testing/scheduler_test.c @@ -0,0 +1,92 @@ +/** + * @file scheduler_test.c + * + * @brief Tests for the scheduler_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "scheduler_test.h" + +#include +#include +#include +#include +#include + + +/** + * @brief implementation of a scheduler test + * + * This one uses relative time events, which are not that exact. + * Test may fail on too slow machines. + */ +void test_scheduler(protected_tester_t *tester) +{ + int job_count = 5; + job_t *jobs[job_count]; + int current; + scheduler_t *scheduler = scheduler_create(); + + /* schedule 5 jobs */ + for (current = 0; current < job_count; current++) + { + /* misusing for testing only */ + jobs[current] = (job_t *) incoming_packet_job_create((packet_t*)(current+1)); + charon->event_queue->add_relative(charon->event_queue, jobs[current], (current+1) * 500); + } + + + for (current = 0; current < job_count; current++) + { + jobs[current] = NULL; + } + + usleep(50 * 1000); + + /* check if times are correct */ + for (current = 0; current < job_count; current++) + { + usleep(400 * 1000); + + tester->assert_true(tester, (charon->job_queue->get_count(charon->job_queue) == current ), "job-queue size before event"); + tester->assert_true(tester, (charon->event_queue->get_count(charon->event_queue) == job_count - current), "event-queue size before event"); + usleep(100 * 1000); + + tester->assert_true(tester, (charon->job_queue->get_count(charon->job_queue) == current + 1), "job-queue size after event"); + tester->assert_true(tester, (charon->event_queue->get_count(charon->event_queue) == job_count - current - 1), "event-queue size after event"); + } + + /* check job order */ + for (current = 0; current < job_count; current++) + { + jobs[current] = charon->job_queue->get(charon->job_queue); + incoming_packet_job_t *current_job; + current_job = (incoming_packet_job_t*) jobs[current]; + packet_t *packet; + packet = current_job->get_packet(current_job); + + tester->assert_true(tester, (((int)packet) == current+1), "job order"); + jobs[current]->destroy(jobs[current]); + } + + /* destruction test */ + scheduler->destroy(scheduler); +} diff --git a/src/charon/testing/scheduler_test.h b/src/charon/testing/scheduler_test.h new file mode 100644 index 000000000..746848e49 --- /dev/null +++ b/src/charon/testing/scheduler_test.h @@ -0,0 +1,37 @@ +/** + * @file scheduler_test.h + * + * @brief Tests for the scheduler_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SCHEDULER_TEST_H_ +#define SCHEDULER_TEST_H_ + +#include + +/** + * @brief Test function for the type scheduler_t. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_scheduler(protected_tester_t *tester); + +#endif /*SCHEDULER_TEST_H_*/ diff --git a/src/charon/testing/send_queue_test.c b/src/charon/testing/send_queue_test.c new file mode 100644 index 000000000..a56f8e5a2 --- /dev/null +++ b/src/charon/testing/send_queue_test.c @@ -0,0 +1,142 @@ +/** + * @file send_queue_test.c + * + * @brief Tests for the send_queue_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "send_queue_test.h" + +#include + + +/** + * @brief Informations for the involved test-thread used in this test + * + */ +typedef struct send_queue_test_s send_queue_test_t; + + +struct send_queue_test_s{ + /** + * Associated protected_tester_t object + */ + protected_tester_t *tester; + + /** + * Queue to test + */ + send_queue_t *send_queue; + + /** + * number of items to be inserted in the send-queue by each thread + */ + int insert_item_count; + + /** + * number of items to be removed by each + * receiver thread from the send-queue + */ + int remove_item_count; +}; + +/** + * @brief sender thread used in the the send_queue test function + * + * @param testinfo informations for the specific thread. + */ +static void test_send_queue_sender(send_queue_test_t * testinfo) +{ + int i; + for (i = 0; i < testinfo->insert_item_count; i++) + { + packet_t *packet = packet_create(AF_INET); + testinfo->tester->assert_true(testinfo->tester,(packet != NULL), "create packet call check"); + testinfo->send_queue->add(testinfo->send_queue,packet); + } +} + +/** + * @brief receiver thread used in the the send_queue test function + * + * @param testinfo informations for the specific thread. + */ +static void test_send_queue_receiver(send_queue_test_t * testinfo) +{ + int i; + for (i = 0; i < testinfo->remove_item_count; i++) + { + packet_t *packet; + packet = testinfo->send_queue->get(testinfo->send_queue); + + testinfo->tester->assert_true(testinfo->tester,( packet != NULL), "packet not NULL call check"); + + packet->destroy(packet); + } +} + +/* + * description is in header file + */ +void test_send_queue(protected_tester_t *tester) +{ + int desired_value, i; + int sender_count = 10; + int receiver_count = 2; + pthread_t sender_threads[sender_count]; + pthread_t receiver_threads[receiver_count]; + send_queue_t *send_queue = send_queue_create(); + send_queue_test_t test_infos; + + test_infos.tester = tester; + test_infos.send_queue = send_queue; + test_infos.insert_item_count = 10000; + test_infos.remove_item_count = 10000; + + + desired_value = test_infos.insert_item_count * sender_count - + test_infos.remove_item_count * receiver_count; + + for (i = 0; i < receiver_count;i++) + { + pthread_create( &receiver_threads[i], NULL,(void*(*)(void*)) &test_send_queue_receiver, (void*) &test_infos); + } + + for (i = 0; i < sender_count;i++) + { + pthread_create( &sender_threads[i], NULL,(void*(*)(void*)) &test_send_queue_sender, (void*) &test_infos); + } + + + /* Wait for all threads */ + for (i = 0; i < sender_count;i++) + { + pthread_join(sender_threads[i], NULL); + } + for (i = 0; i < receiver_count;i++) + { + pthread_join(receiver_threads[i], NULL); + } + + + /* the send-queue has to have diserd_value count entries*/ + tester->assert_true(tester,(send_queue->get_count(send_queue) == desired_value), "count value check"); + send_queue->destroy(send_queue); +} diff --git a/src/charon/testing/send_queue_test.h b/src/charon/testing/send_queue_test.h new file mode 100644 index 000000000..138657e10 --- /dev/null +++ b/src/charon/testing/send_queue_test.h @@ -0,0 +1,40 @@ +/** + * @file send_queue_test.h + * + * @brief Tests for the send_queue_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SEND_QUEUE_TEST_H_ +#define SEND_QUEUE_TEST_H_ + +#include + +/** + * @brief Test function used to test the send_queue functionality. + * + * Tests are performed using different threads to test the multi-threaded + * features of the send_queue_t. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_send_queue(protected_tester_t *tester); + +#endif /*SEND_QUEUE_TEST_H_*/ diff --git a/src/charon/testing/sender_test.c b/src/charon/testing/sender_test.c new file mode 100644 index 000000000..391d71fbc --- /dev/null +++ b/src/charon/testing/sender_test.c @@ -0,0 +1,88 @@ +/** + * @file sender_test.h + * + * @brief Tests for the sender_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "sender_test.h" + +#include +#include +#include +#include +#include +#include +#include + +/** + * Number of packets to send by sender-thread + */ +#define NUMBER_OF_PACKETS_TO_SEND 5 + +void test_sender(protected_tester_t *tester) +{ + int i; + sender_t *sender; + receiver_t *receiver; + job_t *job; + packet_t *packet; + packet_t *received_packet; + char test_data[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, /* spi */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05, /* spi */ + 0x05, /* next payload */ + 0x20, /* IKE version */ + 0x00, /* exchange type */ + 0x00, /* flags */ + 0x00,0x00,0x00,0x01, /* message id */ + 0x00,0x00,0x00,0x24, /* length */ + 0x12,0x34,0x56,0x67, /* some data */ + 0x12,0x34,0x56,0x67, + }; + chunk_t data = chunk_from_buf(test_data); + chunk_t received; + sender = sender_create(); + receiver = receiver_create(); + + for (i = 0; i < NUMBER_OF_PACKETS_TO_SEND; i++) + { + packet = packet_create(AF_INET); + packet->set_destination(packet, host_create(AF_INET, "127.0.0.1", 500)); + packet->set_source(packet, host_create(AF_INET, "127.0.0.1", 500)); + packet->set_data(packet, chunk_clone(data)); + charon->send_queue->add(charon->send_queue,packet); + } + + for (i = 0; i < NUMBER_OF_PACKETS_TO_SEND; i++) + { + job = charon->job_queue->get(charon->job_queue); + tester->assert_true(tester, (job->get_type(job) == INCOMING_PACKET), "job type check"); + received_packet = ((incoming_packet_job_t *)(job))->get_packet((incoming_packet_job_t *)(job)); + received = received_packet->get_data(received_packet); + tester->assert_true(tester, received.len == data.len, "received data length check"); + tester->assert_true(tester, memcmp(received.ptr, data.ptr, data.len) == 0, "received data value check"); + received_packet->destroy(received_packet); + job->destroy(job); + } + + sender->destroy(sender); + receiver->destroy(receiver); +} diff --git a/src/charon/testing/sender_test.h b/src/charon/testing/sender_test.h new file mode 100644 index 000000000..1fdfed69d --- /dev/null +++ b/src/charon/testing/sender_test.h @@ -0,0 +1,37 @@ +/** + * @file sender_test.h + * + * @brief Tests for the sender_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SENDER_TEST_H_ +#define SENDER_TEST_H_ + +#include + +/** + * @brief Test function for the class sender_t. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_sender(protected_tester_t *tester); + +#endif /*SENDER_TEST_H_*/ diff --git a/src/charon/testing/socket_test.c b/src/charon/testing/socket_test.c new file mode 100644 index 000000000..9ae1b0fbc --- /dev/null +++ b/src/charon/testing/socket_test.c @@ -0,0 +1,82 @@ +/** + * @file socket_test.c + * + * @brief Tests for the socket_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "socket_test.h" + +#include +#include + +/* + * Description in header file + */ +void test_socket(protected_tester_t *tester) +{ + int packet_count = 10; + int current; + socket_t *skt = socket_create(500); + packet_t *pkt = packet_create(AF_INET); + char test_data[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, /* spi */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05, /* spi */ + 0x05, /* next payload */ + 0x20, /* IKE version */ + 0x00, /* exchange type */ + 0x00, /* flags */ + 0x00,0x00,0x00,0x01, /* message id */ + 0x00,0x00,0x00,0x24, /* length */ + 0x12,0x34,0x56,0x67, /* some data */ + 0x12,0x34,0x56,0x67, + }; + chunk_t data = chunk_from_buf(test_data); + chunk_t received; + + /* send to previously bound socket */ + pkt->set_destination(pkt, host_create(AF_INET, "127.0.0.1", 500)); + pkt->set_source(pkt, host_create(AF_INET, "127.0.0.1", 500)); + pkt->set_data(pkt, chunk_clone(data)); + + /* send packet_count packets */ + for (current = 0; current < packet_count; current++) + { + if (skt->send(skt, pkt) == FAILED) + { + tester->assert_true(tester, 0, "packet send"); + } + } + pkt->destroy(pkt); + + + /* receive packet_count packets */ + for (current = 0; current < packet_count; current++) + { + skt->receive(skt, &pkt); + received = pkt->get_data(pkt); + tester->assert_false(tester, memcmp(received.ptr, data.ptr, max(received.len, data.len)), "packet exchange"); + pkt->destroy(pkt); + } + + skt->destroy(skt); + +} diff --git a/src/charon/testing/socket_test.h b/src/charon/testing/socket_test.h new file mode 100644 index 000000000..a59995297 --- /dev/null +++ b/src/charon/testing/socket_test.h @@ -0,0 +1,38 @@ +/** + * @file socket_test.h + * + * @brief Tests for the socket_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 SOCKET_TEST_H_ +#define SOCKET_TEST_H_ + +#include + +/** + * @brief Test function for the class socket_t. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_socket(protected_tester_t *tester); + + +#endif /*SOCKET_TEST_H_*/ diff --git a/src/charon/testing/testcases.c b/src/charon/testing/testcases.c new file mode 100644 index 000000000..e4d92becf --- /dev/null +++ b/src/charon/testing/testcases.c @@ -0,0 +1,263 @@ +/** + * @file tests.c + * + * @brief Main for all testcases. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "linked_list_test.h" +#include "thread_pool_test.h" +#include "job_queue_test.h" +#include "event_queue_test.h" +#include "send_queue_test.h" +#include "socket_test.h" +#include "sender_test.h" +#include "scheduler_test.h" +#include "ike_sa_id_test.h" +#include "ike_sa_test.h" +#include "ike_sa_manager_test.h" +#include "generator_test.h" +#include "parser_test.h" +#include "packet_test.h" +#include "diffie_hellman_test.h" +#include "hasher_test.h" +#include "hmac_test.h" +#include "prf_plus_test.h" +#include "aes_cbc_crypter_test.h" +#include "hmac_signer_test.h" +#include "encryption_payload_test.h" +#include "connection_test.h" +#include "policy_test.h" +#include "proposal_test.h" +#include "rsa_test.h" +#include "kernel_interface_test.h" +#include "child_sa_test.h" +#include "certificate_test.h" +#include "leak_detective_test.h" +#include "identification_test.h" + +/* output for test messages */ +extern FILE * stderr; + +test_t linked_list_test = {test_linked_list,"Linked List"}; +test_t iterator_test = {test_linked_list_iterator,"Linked List Iterator"}; +test_t linked_list_insert_and_remove_test = {test_linked_list_insert_and_remove,"Linked List Insert and remove"}; +test_t event_queue_test = {test_event_queue,"Event-Queue"}; +test_t job_queue_test1 = {test_job_queue,"Job-Queue"}; +test_t send_queue_test = {test_send_queue,"Send-Queue"}; +test_t socket_test = {test_socket,"Socket"}; +test_t thread_pool_test = {test_thread_pool,"Thread Pool"}; +test_t sender_test = {test_sender,"Sender"}; +test_t scheduler_test = {test_scheduler,"Scheduler"}; +test_t ike_sa_id_test = {test_ike_sa_id,"IKE_SA-Identifier"}; +test_t ike_sa_test = {test_ike_sa,"IKE_SA"}; +test_t ike_sa_manager_test = {test_ike_sa_manager, "IKE_SA-Manager"}; +test_t generator_test1 = {test_generator_with_header_payload,"Generator: header payload"}; +test_t generator_test2 = {test_generator_with_transform_attribute,"Generator: transform attribute"}; +test_t generator_test3 = {test_generator_with_transform_substructure,"Generator: transform substructure"}; +test_t generator_test4 = {test_generator_with_proposal_substructure,"Generator: proposal substructure"}; +test_t generator_test5 = {test_generator_with_sa_payload,"Generator: Message with SA Payload"}; +test_t generator_test6 = {test_generator_with_ke_payload,"Generator: KE Payload"}; +test_t generator_test7 = {test_generator_with_notify_payload,"Generator: Notify Payload"}; +test_t generator_test8 = {test_generator_with_nonce_payload,"Generator: Nonce Payload"}; +test_t generator_test9 = {test_generator_with_id_payload,"Generator: ID Payload"}; +test_t generator_test10 = {test_generator_with_auth_payload,"Generator: AUTH Payload"}; +test_t generator_test11 = {test_generator_with_ts_payload,"Generator: TS Payload"}; +test_t generator_test12 = {test_generator_with_cert_payload,"Generator: CERT Payload"}; +test_t generator_test13 = {test_generator_with_certreq_payload,"Generator: CERTREQ Payload"}; +test_t generator_test14 = {test_generator_with_delete_payload,"Generator: DELETE Payload"}; +test_t generator_test15 = {test_generator_with_vendor_id_payload,"Generator: VENDOR ID Payload"}; +test_t generator_test16 = {test_generator_with_cp_payload,"Generator: CP Payload"}; +test_t generator_test17 = {test_generator_with_eap_payload,"Generator: EAP Payload"}; +test_t parser_test1 = {test_parser_with_header_payload, "Parser: header payload"}; +test_t parser_test2 = {test_parser_with_sa_payload, "Parser: sa payload"}; +test_t parser_test3 = {test_parser_with_nonce_payload, "Parser: nonce payload"}; +test_t parser_test4 = {test_parser_with_ke_payload, "Parser: key exchange payload"}; +test_t parser_test5 = {test_parser_with_notify_payload, "Parser: notify payload"}; +test_t parser_test6 = {test_parser_with_id_payload, "Parser: ID payload"}; +test_t parser_test7 = {test_parser_with_auth_payload, "Parser: AUTH payload"}; +test_t parser_test8 = {test_parser_with_ts_payload, "Parser: TS payload"}; +test_t parser_test9 = {test_parser_with_cert_payload, "Parser: CERT payload"}; +test_t parser_test10 = {test_parser_with_certreq_payload, "Parser: CERTREQ payload"}; +test_t parser_test11 = {test_parser_with_delete_payload, "Parser: DELETE payload"}; +test_t parser_test12 = {test_parser_with_vendor_id_payload, "Parser: VENDOR ID payload"}; +test_t parser_test13 = {test_parser_with_cp_payload, "Parser: CP payload"}; +test_t parser_test14 = {test_parser_with_eap_payload, "Parser: EAP payload"}; +test_t packet_test = {test_packet,"Packet"}; +test_t diffie_hellman_test = {test_diffie_hellman,"Diffie Hellman"}; +test_t sha1_hasher_test = {test_sha1_hasher,"SHA1 hasher"}; +test_t md5_hasher_test = {test_md5_hasher,"MD5 hasher"}; +test_t hmac_test1 = {test_hmac_sha1, "HMAC using SHA1"}; +test_t hmac_test2 = {test_hmac_md5, "HMAC using MD5"}; +test_t prf_plus_test = {test_prf_plus, "prf+"}; +test_t aes_cbc_crypter_test = {test_aes_cbc_crypter, "AES CBC"}; +test_t hmac_signer_test1 = {test_hmac_md5_signer, "HMAC MD5 signer test"}; +test_t hmac_signer_test2 = {test_hmac_sha1_signer, "HMAC SHA1 signer test"}; +test_t encryption_payload_test = {test_encryption_payload, "encryption payload test"}; +test_t connection_test = {test_connection, "connection_t test"}; +test_t policy_test = {test_policy, "policy_t test"}; +test_t proposal_test = {test_proposal, "proposal_t test"}; +test_t rsa_test = {test_rsa, "RSA private/public key test"}; +test_t kernel_interface_test = {test_kernel_interface, "Kernel Interface"}; +test_t child_sa_test = {test_child_sa, "Child SA"}; +test_t certificate_test = {test_certificate, "X509 Certificate"}; +test_t leak_detective_test = {test_leak_detective, "LEAK detective"}; +test_t identification_test = {test_identification, "identification"}; + + +daemon_t* charon; + +static void daemon_kill(daemon_t *this, char* none) +{ + //this->socket->destroy(this->socket); + this->ike_sa_manager->destroy(this->ike_sa_manager); + this->job_queue->destroy(this->job_queue); + this->event_queue->destroy(this->event_queue); + this->send_queue->destroy(this->send_queue); + this->kernel_interface->destroy(this->kernel_interface); + //this->configuration->destroy(this->configuration); + free(charon); +} + +/** + * @brief Create the dummy daemon for testing. + * + * @return created daemon_t + */ +daemon_t *daemon_create() +{ + charon = malloc_thing(daemon_t); + + /* assign methods */ + charon->kill = daemon_kill; + + charon->socket = socket_create(500); + charon->ike_sa_manager = ike_sa_manager_create(); + charon->job_queue = job_queue_create(); + charon->event_queue = event_queue_create(); + charon->send_queue = send_queue_create(); + charon->kernel_interface = kernel_interface_create(); + //charon->configuration = configuration_create(RETRANSMIT_TIMEOUT,MAX_RETRANSMIT_COUNT,HALF_OPEN_IKE_SA_TIMEOUT); + charon->sender = NULL; + charon->receiver = NULL; + charon->scheduler = NULL; + charon->thread_pool = NULL; + + return charon; +} + + +int main() +{ + FILE * test_output = stderr; + + test_t *all_tests[] ={ + &linked_list_test, + &iterator_test, + &linked_list_insert_and_remove_test, + &thread_pool_test, + &job_queue_test1, + &event_queue_test, + &send_queue_test, + &scheduler_test, + &socket_test, + &sender_test, + &ike_sa_id_test, + &ike_sa_test, + &generator_test1, + &generator_test2, + &parser_test1, + &parser_test2, + &parser_test3, + &parser_test4, + &parser_test5, + &parser_test6, + &parser_test7, + &parser_test8, + &parser_test9, + &parser_test10, + &parser_test11, + &parser_test12, + &parser_test13, + &parser_test14, + &generator_test3, + &generator_test4, + &generator_test5, + &generator_test6, + &generator_test7, + &generator_test8, + &generator_test9, + &generator_test10, + &generator_test11, + &generator_test12, + &generator_test13, + &generator_test14, + &generator_test15, + &generator_test16, + &generator_test17, + &ike_sa_manager_test, + &packet_test, + &diffie_hellman_test, + &sha1_hasher_test, + &md5_hasher_test, + &hmac_test1, + &hmac_test2, + &prf_plus_test, + &aes_cbc_crypter_test, + &hmac_signer_test1, + &hmac_signer_test2, + &encryption_payload_test, + &connection_test, + &policy_test, + &proposal_test, + &rsa_test, + NULL + }; + /* get rid of compiler warning ;-) */ + *all_tests = *all_tests; + + daemon_create(); + + //logger_manager->enable_log_level(logger_manager, ALL_LOGGERS, FULL); + logger_manager->set_output(logger_manager, ALL_LOGGERS, stdout); + + tester_t *tester = tester_create(test_output, FALSE); + + tester->perform_tests(tester,all_tests); + //tester->perform_test(tester,&sender_test); + + + tester->destroy(tester); + + charon->kill(charon, NULL); + + return 0; +} diff --git a/src/charon/testing/thread_pool_test.c b/src/charon/testing/thread_pool_test.c new file mode 100644 index 000000000..ee7a5101f --- /dev/null +++ b/src/charon/testing/thread_pool_test.c @@ -0,0 +1,41 @@ +/** + * @file thread_pool_test.c + * + * @brief Tests for the thread_pool_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 "thread_pool_test.h" + +#include + +/* + * Description in header file + */ +void test_thread_pool(protected_tester_t *tester) +{ + size_t desired_pool_size = 10; + size_t pool_size; + + thread_pool_t *pool = thread_pool_create(desired_pool_size); + pool_size = pool->get_pool_size(pool); + tester->assert_true(tester, (desired_pool_size == pool_size), "thread creation"); + pool->destroy(pool); +} diff --git a/src/charon/testing/thread_pool_test.h b/src/charon/testing/thread_pool_test.h new file mode 100644 index 000000000..bdae797b7 --- /dev/null +++ b/src/charon/testing/thread_pool_test.h @@ -0,0 +1,37 @@ +/** + * @file thread_pool_test.h + * + * @brief Tests for the thread_pool_t class. + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, 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 THREAD_POOL_TEST_H_ +#define THREAD_POOL_TEST_H_ + +#include + +/** + * @brief Test function for the class thread_pool_t. + * + * @param tester tester object + * + * @ingroup testcases + */ +void test_thread_pool(protected_tester_t *tester); + +#endif /*THREAD_POOL_TEST_H_*/ diff --git a/src/ipsec/.cvsignore b/src/ipsec/.cvsignore new file mode 100644 index 000000000..70025a7f8 --- /dev/null +++ b/src/ipsec/.cvsignore @@ -0,0 +1 @@ +ipsec diff --git a/src/ipsec/Makefile b/src/ipsec/Makefile new file mode 100644 index 000000000..fdff3728a --- /dev/null +++ b/src/ipsec/Makefile @@ -0,0 +1,28 @@ +# Makefile for miscelaneous programs +# Copyright (C) 2002 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: Makefile,v 1.2 2006/02/10 11:27:31 as Exp $ + +FREESWANSRCDIR=../.. +include ${FREESWANSRCDIR}/Makefile.inc + +PROGRAM=ipsec +PROGRAMDIR=${SBINDIR} +MANPROGPREFIX:=./ +LIBFILES:=$(wildcard distro.txt) + +include ../Makefile.program + +install:: ipsec + @$(INSTALL) $(INSTBINFLAGS) ipsec $(RCDIR)/ipsec + diff --git a/src/ipsec/distro.txt b/src/ipsec/distro.txt new file mode 100644 index 000000000..80f4192a4 --- /dev/null +++ b/src/ipsec/distro.txt @@ -0,0 +1 @@ +distributed by Andreas Steffen diff --git a/src/ipsec/ipsec.8 b/src/ipsec/ipsec.8 new file mode 100644 index 000000000..823289372 --- /dev/null +++ b/src/ipsec/ipsec.8 @@ -0,0 +1,336 @@ +.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_EXECDIR directory containing published commands +IPSEC_LIBDIR directory containing internal executables +IPSEC_SBINDIR directory containing \fBipsec\fP command +IPSEC_CONFS directory containing configuration files +.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..04156e8ae --- /dev/null +++ b/src/ipsec/ipsec.in @@ -0,0 +1,269 @@ +#! /bin/sh +# prefix command to run stuff from our programs directory +# Copyright (C) 1998-2002 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: ipsec.in,v 1.13 2006/03/09 20:09:33 as Exp $ + +IPSEC_NAME=strongSwan + +# where the private directory and the config files are +IPSEC_EXECDIR="${IPSEC_EXECDIR-@IPSEC_EXECDIR@}" +IPSEC_LIBDIR="${IPSEC_LIBDIR-@IPSEC_LIBDIR@}" +IPSEC_SBINDIR="${IPSEC_SBINDIR-@IPSEC_SBINDIR@}" +IPSEC_CONFS="${IPSEC_CONFS-@IPSEC_CONFS@}" + +IPSEC_DIR="$IPSEC_LIBDIR" +export IPSEC_DIR IPSEC_CONFS IPSEC_LIBDIR IPSEC_EXECDIR + +IPSEC_STARTER_PID="/var/run/starter.pid" +IPSEC_CHARON_PID="/var/run/charon.pid" + +# standardize PATH, and export it for everything else's benefit +PATH="${IPSEC_SBINDIR}":/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin +export PATH + +# things not to be listed in --help command list +DONTMENTION='^(ipsec|_.*|.*\.old|.*~)$' + +# version numbering (details filled in by build) +# Possibly should call a C program to invoke the version_code() function +# instead, but for performance's sake, we inline it here (and only here). +version="xxx" + +# export the version information +IPSEC_VERSION="$version" +export IPSEC_VERSION + +# function for the funky user/kernel version stuff +fixversion() { + if test -f /proc/net/ipsec_version + then + stack=" (KLIPS)" + kv="`awk '{print $NF}' /proc/net/ipsec_version`" + else + if test -f /proc/net/pfkey + then + stack=" (native)" + kv="`uname -r`" + else + kv="(no kernel code presently loaded)" + fi + fi + if test " $kv" != " $version" + then + version="U$version/K$kv" + fi + version="$version$stack" +} + +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 + echo "Some of these functions have their own manual pages, e.g. ipsec_scepclient(8)." + exit 0 + ;; +--versioncode) + fixversion + echo "$version" + exit 0 + ;; +--copyright) + set _copyright + # and fall through, invoking "ipsec _copyright" + ;; +--directory) + echo "$IPSEC_DIR" + exit 0 + ;; +--confdir) + echo "$IPSEC_CONFS" + exit 0 + ;; +down) + shift + $IPSEC_EXECDIR/whack --name "$1" --terminate + if test -e $IPSEC_CHARON_PID + then + $IPSEC_EXECDIR/stroke down "$1" + fi + exit 0 + ;; +listalgs|listpubkeys|listcerts|listcacerts|\ +listaacerts|listocspcerts|listacerts|listgroups|\ +listcainfos|listcrls|listocsp|listcards|\ +listall|purgeocsp|rereadsecrets|rereadgroups|\ +rereadcacerts|rereadaacerts|rereadocspcerts|\ +rereadacerts|rereadcrls|rereadall) + op="$1" + shift + $IPSEC_EXECDIR/whack "$@" "--$op" + if test -e $IPSEC_CHARON_PID + then + $IPSEC_EXECDIR/stroke "$op" + fi + exit 0 + ;; +ready) + shift + $IPSEC_EXECDIR/whack --listen + 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 + $IPSEC_EXECDIR/whack --name "$1" "--$op" + exit 0 + ;; +scencrypt|scdecrypt) + op="$1" + shift + $IPSEC_EXECDIR/whack "--$op" "$@" + exit 0 + ;; +secrets) + $IPSEC_EXECDIR/whack --rereadsecrets + exit 0 + ;; +start) + shift + exec $IPSEC_EXECDIR/starter "$@" + ;; +status|statusall) + op="$1" + shift + if test $# -eq 0 + then + $IPSEC_EXECDIR/whack "--$op" + if test -e $IPSEC_CHARON_PID + then + $IPSEC_EXECDIR/stroke "$op" + fi + else + $IPSEC_EXECDIR/whack --name "$1" "--$op" + if test -e $IPSEC_CHARON_PID + then + $IPSEC_EXECDIR/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 + $IPSEC_EXECDIR/whack --name "$1" --initiate + if test -e $IPSEC_CHARON_PID + then + $IPSEC_EXECDIR/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) + fixversion + echo "Linux $IPSEC_NAME $version" + echo "See \`ipsec --copyright' for copyright information." + if [ -f $IPSEC_LIBDIR/distro.txt ] + then + cat $IPSEC_LIBDIR/distro.txt + fi + exit 0 + ;; +--*) + echo "$0: unknown option \`$1' (perhaps command name was omitted?)" >&2 + exit 1 + ;; +esac + +cmd="$1" +shift + +path="$IPSEC_EXECDIR/$cmd" + +if test ! -x "$path" +then + path="$IPSEC_LIBDIR/$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/openac/Makefile b/src/openac/Makefile new file mode 100644 index 000000000..7aeacee0a --- /dev/null +++ b/src/openac/Makefile @@ -0,0 +1,154 @@ +# Makefile for the openac attribute certificate generation tool +# Copyright (C) 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: Makefile,v 1.16 2006/02/17 19:33:27 as Exp $ + +FREESWANSRCDIR=../.. +include ${FREESWANSRCDIR}/Makefile.inc + +CONFDIR=$(DESTDIR)/etc/openac +PLUTODIR=../pluto + +PROGRAM=openac +EXTRA8PROC=${PROGRAM}.8 + +LIBS=${FREESWANLIB} $(LIBDESLITE) -lgmp +CFLAGS+= -DDEBUG -DNO_PLUTO + +# This compile option activates the leak detective +ifeq ($(USE_LEAK_DETECTIVE),true) + CFLAGS+= -DLEAK_DETECTIVE +endif + +X509_OBJS= 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 + +OBJS= build.o loglite.o ${X509_OBJS} + +include ../Makefile.program + +build.o : build.c build.h + $(CC) $(CFLAGS) -c -o $@ $< + +loglite.o : loglite.c $(PLUTODIR)/log.h + $(CC) $(CFLAGS) -c -o $@ $< + +# X.509 library + +ac.o : $(PLUTODIR)/ac.c $(PLUTODIR)/ac.h + $(CC) $(CFLAGS) -c -o $@ $< + +asn1.o : $(PLUTODIR)/asn1.c $(PLUTODIR)/asn1.h + $(CC) $(CFLAGS) -c -o $@ $< + +ca.o : $(PLUTODIR)/ca.c $(PLUTODIR)/ca.h + $(CC) $(CFLAGS) -c -o $@ $< + +certs.o : $(PLUTODIR)/certs.c $(PLUTODIR)/certs.h + $(CC) $(CFLAGS) -c -o $@ $< + +constants.o : $(PLUTODIR)/constants.c $(PLUTODIR)/constants.h + $(CC) $(CFLAGS) -c -o $@ $< + +crl.o : $(PLUTODIR)/crl.c $(PLUTODIR)/crl.h + $(CC) $(CFLAGS) -c -o $@ $< + +defs.o : $(PLUTODIR)/defs.c $(PLUTODIR)/defs.h + $(CC) $(CFLAGS) -c -o $@ $< + +mp_defs.o : $(PLUTODIR)/mp_defs.c $(PLUTODIR)/mp_defs.h + $(CC) $(CFLAGS) -c -o $@ $< + +fetch.o : $(PLUTODIR)/fetch.c $(PLUTODIR)/fetch.h + $(CC) $(CFLAGS) -c -o $@ $< + +id.o : $(PLUTODIR)/id.c $(PLUTODIR)/id.h + $(CC) $(CFLAGS) -c -o $@ $< + +keys.o : $(PLUTODIR)/keys.c $(PLUTODIR)/keys.h + $(CC) $(CFLAGS) -c -o $@ $< + +lex.o : $(PLUTODIR)/lex.c $(PLUTODIR)/lex.h + $(CC) $(CFLAGS) -c -o $@ $< + +md2.o : $(PLUTODIR)/md2.c $(PLUTODIR)/md2.h + $(CC) $(CFLAGS) -c -o $@ $< + +md5.o : $(PLUTODIR)/md5.c $(PLUTODIR)/md5.h + $(CC) $(CFLAGS) -c -o $@ $< + +ocsp.o : $(PLUTODIR)/ocsp.c $(PLUTODIR)/ocsp.h + $(CC) $(CFLAGS) -c -o $@ $< + +oid.o : $(PLUTODIR)/oid.c $(PLUTODIR)/oid.h + $(CC) $(CFLAGS) -c -o $@ $< + +pem.o : $(PLUTODIR)/pem.c $(PLUTODIR)/pem.h + $(CC) $(CFLAGS) -c -o $@ $< + +pgp.o : $(PLUTODIR)/pgp.c $(PLUTODIR)/pgp.h + $(CC) $(CFLAGS) -c -o $@ $< + +pkcs1.o : $(PLUTODIR)/pkcs1.c $(PLUTODIR)/pkcs1.h + $(CC) $(CFLAGS) -c -o $@ $< + +rnd.o : $(PLUTODIR)/rnd.c $(PLUTODIR)/rnd.h + $(CC) $(CFLAGS) -c -o $@ $< + +sha1.o : $(PLUTODIR)/sha1.c $(PLUTODIR)/sha1.h + $(CC) $(CFLAGS) -c -o $@ $< + +smartcard.o : $(PLUTODIR)/smartcard.c $(PLUTODIR)/smartcard.h + $(CC) $(CFLAGS) -c -o $@ $< + +x509.o : $(PLUTODIR)/x509.c $(PLUTODIR)/x509.h + $(CC) $(CFLAGS) -c -o $@ $< + +# Stolen from pluto/Makefile + +gatherdeps: + @ls | grep '\.c$$' | sed -e 's/\(.*\)\.c$$/\1.o: \1.c/' + @echo + @ls | grep '\.c$$' | xargs grep '^#[ ]*include[ ]*"' | \ + sed -e 's/\.c:#[ ]*include[ ]*"/.o: /' -e 's/".*//' + +# Dependencies generated by "make gatherdeps": + +build.o: build.c +loglite.o: loglite.c +openac.o: openac.c + +build.o: ../pluto/constants.h +build.o: ../pluto/defs.h +build.o: ../pluto/oid.h +build.o: ../pluto/asn1.h +build.o: ../pluto/x509.h +build.o: ../pluto/log.h +build.o: build.h +loglite.o: ../pluto/constants.h +loglite.o: ../pluto/defs.h +loglite.o: ../pluto/log.h +loglite.o: ../pluto/whack.h +openac.o: ../pluto/constants.h +openac.o: ../pluto/defs.h +openac.o: ../pluto/mp_defs.h +openac.o: ../pluto/log.h +openac.o: ../pluto/asn1.h +openac.o: ../pluto/certs.h +openac.o: ../pluto/x509.h +openac.o: ../pluto/crl.h +openac.o: ../pluto/keys.h +openac.o: ../pluto/ac.h +openac.o: build.h 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..b1763cc9f --- /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 "../pluto/constants.h" +#include "../pluto/defs.h" +#include "../pluto/log.h" +#include "../pluto/whack.h" + +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..524a302d7 --- /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 "/etc/openac" +#define OPENAC_SERIAL "/etc/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/.cvsignore b/src/pluto/.cvsignore new file mode 100644 index 000000000..fb96dae41 --- /dev/null +++ b/src/pluto/.cvsignore @@ -0,0 +1,3 @@ +_pluto_adns +pluto +whack diff --git a/src/pluto/Makefile b/src/pluto/Makefile new file mode 100644 index 000000000..c2660fd4f --- /dev/null +++ b/src/pluto/Makefile @@ -0,0 +1,1095 @@ +# Pluto Makefile +# 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: Makefile,v 1.44 2006/01/25 17:22:19 as Exp $ + +# relative path to top directory of FreeS/WAN source +# Note: referenced in ${FREESWANSRCDIR}/Makefile.inc +FREESWANSRCDIR=../.. + +include ${FREESWANSRCDIR}/Makefile.inc + +FMANDIR=$(MANTREE)/man5 +PMANDIR=$(MANTREE)/man8 + +# -O on Linux makes gcc coredump when compiling sha1.c +# -Wundef is nice but RHL5.2 compiler doesn't support it +CFLAGS = -g -Wall -W -Wmissing-prototypes -Wpointer-arith -Wbad-function-cast \ + -Wcast-qual -Wmissing-declarations -Wwrite-strings \ + -Wstrict-prototypes # -Wundef + +# where to find klips headers and FreeS/WAN headers +HDRDIRS = -I$(KLIPSINC) -I${FREESWANSRCDIR}/programs/pluto/linux26 + +# where to find sha2.h +LIBCRYPTO=$(FREESWANSRCDIR)/lib/libcrypto +HDRDIRS += -I$(LIBCRYPTO) + +# On non-LINUX systems, these one of these may be needed (see endian.h) +# BYTE_ORDER = -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234 -DBYTE_ORDER=BIG_ENDIAN +# BYTE_ORDER = -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234 -DBYTE_ORDER=LITTLE_ENDIAN + +# -DKLIPS enables interface to Kernel LINUX IPsec code +# -DDEBUG enables debugging code, allowing for debugging output +# (note that output must also be selected at runtime, so it is +# reasonable to always define this) +# -DVENDORID enables Pluto to send out a VendorID payload. +# this can be used by remote nodes to work around faults (bugs), +# but is most useful to humans who are debugging things. +# -DGCC_LINT uses gcc-specific declarations to improve compile-time +# diagnostics. +# -DLEAK_DETECTIVE enables crude code to find memory allocation leaks. +# -DOLD_RESOLVER. At some point, the resolver interface changed. +# This macro enables Pluto support for the old interface. +# It is automatically defined, based on the value of the +# macro __RES. We don't know the correct threshold, so you may +# find that you must manually define this. If so, please inform +# us so that we can refine the threshold. +# -DLIBCURL includes libcurl functions for the support of http-based protocols. +# -DLDAP_VER includes openldap functions for the support of ldap-based queries. +# LDAPv2 and LDAPv3 are supported. +# -DTHREADS enables an asynchronous thread managing CRL fetching. +# This option is activated either by -DLIBCURL or -DLDAP_VER. +# -DSMARTCARD enables PKCS11-based smartcard support +# -DPKCS11_DEFAULT_LIB defines a default PKCS11 library module which will be +# loaded during runtime and is overridden by the pkcs11module parameter in +# ipsec.conf. This option is activated by -DSMARTCARD. +# -DI_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT +# allows IPsec transport mode in NAT-ed environments. Because of the +# inherent security risks of such scenarios this options is deactivated +# by default. + +# The following are best left undefined -- each can be overridden at runtime +# if need be. +# -DPORT=n sets the default UDP port for IKE messages (otherwise 500) +# -DSHARED_SECRETS_FILE=string overrides /etc/ipsec.secrets as the +# default name of the file containing secrets used to authenticate other +# IKE daemons. In the Makefile, two levels of quoting are needed: +# -DSHARED_SECRETS_FILE='"/etc/ipsec.secrets"' +# -DDEFAULT_CTLBASE=string overrides /var/run/pluto as default directory +# and basename for pluto's lockfile (.pid) and control socket (.ctl). +# Double quoting may be needed. + +ifeq ($(USE_LWRES),true) + LWRESDEF=-DUSE_LWRES + USE_ADNS=false + BINNAMEADNSIFNEEDE= +else + USE_ADNS=true + BINNAMEADNSIFNEEDED=$(BINNAMEADNS) +endif + +ifeq ($(USE_IPSECPOLICY),true) + IPSECPOLICY_FILES=rcv_info.c + IPSECPOLICY_DEFINES=-DIPSECPOLICY + IPSECPOLICY_LIBS=$(POLICYLIB) + IPSECPOLICY_OBJS=rcv_info.o +endif + +ifeq ($(USE_KEYRR),true) + KEYRR_DEFINES=-DUSE_KEYRR +endif + +ifeq ($(USE_KERNEL26),true) + KERNEL26_DEFS=-DKERNEL26_SUPPORT -DKERNEL26_HAS_KAME_DUPLICATES + KERNEL26_SRCS=kernel_netlink.c kernel_netlink.h + KERNEL26_OBJS=kernel_netlink.o +endif + +ifeq ($(USE_NAT_TRAVERSAL),true) +NAT_DEFS=-DNAT_TRAVERSAL -DVIRTUAL_IP +endif + +ifeq ($(USE_NAT_TRAVERSAL_TRANSPORT_MODE),true) +NAT_DEFS+=-DI_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT +endif + +DEFINES = $(EXTRA_DEFINES) \ + $(IPSECPOLICY_DEFINES) \ + $(KEYRR_DEFINES) \ + $(BYTE_ORDER) \ + $(LWRESDEF) \ + $(KERNEL26_DEFS) \ + -DPLUTO \ + -DKLIPS \ + -DDEBUG \ + -DGCC_LINT \ + $(NAT_DEFS) + +# libefence is a free memory allocation debugger +# Solaris 2 needs -lsocket -lnsl +LIBSPLUTO = $(OBJSGCRYPT) $(LIBDESLITE) $(FREESWANLIB) $(IPSECPOLICY_LIBS) +LIBSPLUTO+= -lgmp -lresolv # -lefence + + +ifeq ($(USE_VENDORID),true) + DEFINES+= -DVENDORID +endif + +ifeq ($(USE_XAUTH_VID),true) + DEFINES+= -DXAUTH_VID +endif + +# This compile option activates dynamic URL fetching using libcurl +ifeq ($(USE_LIBCURL),true) + DEFINES+= -DLIBCURL + LIBSPLUTO+= -lcurl + THREADS=1 # Asynchronous cURL queries require threads +endif + +# This compile option activates dynamic LDAP CRL fetching +ifeq ($(USE_LDAP),true) + DEFINES+= -DLDAP_VER=$(LDAP_VERSION) + LIBSPLUTO+= -lldap -llber + THREADS=1 # Asynchronous LDAP queries require threads +endif + +# This compile option activates the use of threads +ifdef THREADS + DEFINES+= -DTHREADS + LIBSPLUTO+= -lpthread +endif + +# This compile option activates smartcard support +ifeq ($(USE_SMARTCARD),true) + DEFINES+= -DSMARTCARD + ifdef PKCS11_DEFAULT_LIB + DEFINES+= -DPKCS11_DEFAULT_LIB=$(PKCS11_DEFAULT_LIB) + endif + LIBSPLUTO+= -ldl +endif + +# enable IKEv2 support +ifeq ($(USE_IKEV2),true) + DEFINES+= -DIKEV2 +endif + +# This compile option activates the leak detective +ifeq ($(USE_LEAK_DETECTIVE),true) + DEFINES+= -DLEAK_DETECTIVE +endif + +CPPFLAGS = $(HDRDIRS) $(DEFINES) \ + -DSHARED_SECRETS_FILE=\"${FINALCONFDIR}/ipsec.secrets\" \ + -DPOLICYGROUPSDIR=\"${FINALCONFDDIR}/policies\" \ + -DPERPEERLOGDIR=\"${FINALLOGDIR}/pluto/peer\" + +ALLFLAGS = $(CPPFLAGS) $(CFLAGS) $(USERCOMPILE) + +ifneq ($(LD_LIBRARY_PATH),) + LDFLAGS=-L$(LD_LIBRARY_PATH) +endif + +LIBSADNS = $(FREESWANLIB) +LIBSADNS += -lresolv # -lefence + +# Solaris needs -lsocket -lnsl +LIBSWHACK = ${FREESWANLIB} + +BINNAMEPLUTO = pluto +BINNAMEWHACK = whack +BINNAMEADNS = _pluto_adns + +RM = /bin/rm +RMFLAGS = -f + +.SUFFIXES: +.SUFFIXES: .c .o + +# files for a (source) distribution + +DISTMISC = CHANGES PLUTO-CONVENTIONS TODO ipsec.secrets Makefile routing.txt \ + pluto.8 ipsec.secrets.5 .cvsignore + +DISTGCRYPT = \ + gcryptfix.c gcryptfix.h \ + dsa.c dsa.h \ + elgamal.c elgamal.h \ + primegen.c \ + smallprime.c + +DISTSRC = \ + ac.c ac.h \ + asn1.c asn1.h \ + ca.c ca.h \ + certs.c certs.h \ + connections.c connections.h \ + crl.c crl.h \ + foodgroups.c foodgroups.h \ + constants.c constants.h \ + cookie.c cookie.h \ + crypto.h crypto.c \ + defs.h defs.c \ + mp_defs.h mp_defs.c \ + demux.c demux.h \ + dnskey.c dnskey.h \ + fetch.c fetch.h \ + id.c id.h \ + ipsec_doi.c ipsec_doi.h \ + kernel.c kernel.h \ + kernel_netlink.c kernel_netlink.h \ + kernel_pfkey.c kernel_pfkey.h \ + kernel_noklips.c kernel_noklips.h \ + kernel_alg.c kernel_alg.h \ + ike_alg.c ike_alg.h \ + alg_info.c alg_info.h \ + rcv_whack.c rcv_whack.h \ + $(IPSECPOLICY_FILES) \ + log.c log.h \ + plutomain.c \ + md2.c md2.h \ + md5.c md5.h \ + modecfg.c modecfg.h \ + ocsp.c ocsp.h \ + oid.txt oid.pl oid.c oid.h \ + packet.c packet.h \ + pem.c pem.h \ + pgp.c pgp.h \ + pkcs1.c pkcs1.h \ + pkcs7.c pkcs7.h \ + lex.c lex.h \ + keys.c keys.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 \ + x509.c x509.h \ + $(DISTGCRYPT) \ + vendor.c nat_traversal.c virtual.c \ + adns.c adns.h \ + whack.c whack.h + +DIST = $(DISTMISC) $(DISTSRC) + + +# start of support for DSS/DSA. Not currently used. +# OBJSGCRYPT = gcryptfix.o dsa.o elgamal.o primegen.o smallprime.o +OBJSGCRYPT = + +OBJSPLUTO = asn1.o connections.o constants.o cookie.o crypto.o defs.o fetch.o foodgroups.o \ + log.o state.o plutomain.o server.o timer.o oid.o pem.o pgp.o pkcs1.o pkcs7.o x509.o \ + ca.o certs.o id.o ipsec_doi.o kernel.o $(KERNEL26_OBJS) kernel_pfkey.o mp_defs.o \ + kernel_noklips.o rcv_whack.o ${IPSECPOLICY_OBJS} demux.o packet.o lex.o keys.o \ + dnskey.o smartcard.o ac.o rnd.o spdb.o sha1.o md5.o md2.o modecfg.o ocsp.o crl.o \ + vendor.o nat_traversal.o virtual.o + +OBJSADNS = adns.o + +OBJSWHACK = whack.o + +all: $(BINNAMEPLUTO) $(BINNAMEADNSIFNEEDED) $(BINNAMEWHACK) +programs: $(BINNAMEPLUTO) $(BINNAMEADNSIFNEEDED) $(BINNAMEWHACK) + +oid.c: oid.txt oid.pl + perl oid.pl + +oid.h: oid.txt oid.pl + perl oid.pl + +install: all + mkdir -p ${LIBEXECDIR} ${LIBDIR} + 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 + $(INSTALL) $(INSTBINFLAGS) $(BINNAMEPLUTO) $(BINNAMEWHACK) $(LIBEXECDIR) + if $(USE_ADNS) ; then $(INSTALL) $(INSTBINFLAGS) $(BINNAMEADNS) $(LIBDIR) ; fi + $(INSTALL) $(INSTMANFLAGS) pluto.8 $(PMANDIR)/ipsec_pluto.8 + sh ${FREESWANSRCDIR}/utils/manlink pluto.8 | \ + while read from to ; \ + do \ + ln -s -f ipsec_$$from $(PMANDIR)/$$to; \ + done + $(INSTALL) $(INSTMANFLAGS) ipsec.secrets.5 $(FMANDIR) + sh ${FREESWANSRCDIR}/utils/manlink ipsec.secrets.5 | \ + while read from to ; \ + do \ + ln -s -f $$from $(FMANDIR)/$$to; \ + done + +install_file_list: + @echo $(LIBEXECDIR)/$(BINNAMEPLUTO) + @if $(USE_ADNS) ; then echo $(LIBDIR)/$(BINNAMEADNS) ; fi + @echo $(LIBEXECDIR)/$(BINNAMEWHACK) + @echo $(PMANDIR)/ipsec_pluto.8 + @sh ${FREESWANSRCDIR}/utils/manlink pluto.8 | \ + while read from to; \ + do\ + echo $(PMANDIR)/$$to; \ + done + @echo $(FMANDIR)/ipsec.secrets.5 + @sh ${FREESWANSRCDIR}/utils/manlink ipsec.secrets.5 | \ + while read from to; \ + do \ + echo $(FMANDIR)/$$to; \ + done + +alg_info_test: alg_info_test.o alg_info.o kernel_alg.o ike_alg.o constants.o defs.o log.o db_ops.o crypto.o $(LIBDESLITE) $(FREESWANLIB) + $(CC) -o $@ $^ $(LIBSPLUTO) + +# alg/libalg.o contains an already resolved object built with +# additional crypto algos inside. +OBJSPLUTO:= kernel_alg.o ike_alg.o alg_info.o db_ops.o $(OBJSPLUTO) alg/libalg.o +# if new alg source is created in alg directory, +# trigger libalg.o rebuild +alg/libalg.o: alg alg/Config.ike_alg + make -C alg libalg.o + touch alg/libalg.o + +# helper for creating alg/Make.common +showdefs: + @echo DEFINES=$(DEFINES) + @echo CFLAGS=$(CFLAGS) + @echo CPPFLAGS=$(CPPFLAGS) + @echo COPTS=$(COPTS) + +$(BINNAMEPLUTO): $(OBJSPLUTO) $(ALG_LIBS) + $(CC) -o $(BINNAMEPLUTO) $(LDFLAGS) $(OBJSPLUTO) $(LIBSPLUTO) + +$(BINNAMEADNS): $(OBJSADNS) + $(CC) -o $(BINNAMEADNS) $(OBJSADNS) $(LIBSADNS) + +$(BINNAMEWHACK): $(OBJSWHACK) + $(CC) -o $(BINNAMEWHACK) $(OBJSWHACK) $(LIBSWHACK) + +distlist: + @echo $(DIST) + +# Exuberant Ctags doesn't work if LC_ALL is set to something other than C + +CTAGSFLAGS = -N --format=1 # fishy options required for Exuberant Ctags + +tags: $(DISTSRC) + LC_ALL=C ctags $(CTAGSFLAGS) $(DISTSRC) $(LIBFREESWANDIR)/*.[ch] + +TAGS: $(DISTSRC) + LC_ALL=C etags $(ETAGSFLAGS) $(DISTSRC) $(LIBFREESWANDIR)/*.[ch] + +cleanall: clean + +distclean: clean + +mostlyclean: clean + +realclean: clean + +clean: + $(RM) $(RMFLAGS) *.core core *~ a.out ktrace.out \ + $(OBJSPLUTO) $(BINNAMEPLUTO) \ + $(OBJSWHACK) $(BINNAMEWHACK) \ + $(OBJSADNS) $(BINNAMEADNS) + make -C alg clean + +check: + echo no checks in lib right now. + +checkprograms: + +.c.o: + $(CC) $(COPTS) $(ALLFLAGS) -c $< + +# Gather dependencies caused by explicit #includes within .c files +# +# Each .c is assumed to compile into a .o with the corresponding name. +# Only dependencies on based on "" includes are considered, not <>. +# Dependencies caused by includes within headers are not noticed. +# Unlike dependencies generated by the compiler, these include dependencies +# suppressed by conditional compilation (good, we think). +# This code can be tricked by embeding #include in comments or +# vice-versa, but we're among friends. + +gatherdeps: + @ls $(DISTSRC) | grep '\.c' | sed -e 's/\(.*\)\.c$$/\1.o: \1.c/' + @echo + @ls $(DISTSRC) | grep '\.c' | xargs grep '^#[ ]*include[ ]*"' | \ + sed -e 's/\.c:#[ ]*include[ ]*"/.o: /' -e 's/".*//' + +# Dependencies generated by "make gatherdeps": + +ac.o: ac.c +adns.o: adns.c +alg_info.o: alg_info.c +asn1.o: asn1.c +ca.o: ca.c +certs.o: certs.c +connections.o: connections.c +constants.o: constants.c +cookie.o: cookie.c +crl.o: crl.c +crypto.o: crypto.c +defs.o: defs.c +demux.o: demux.c +dnskey.o: dnskey.c +dsa.o: dsa.c +elgamal.o: elgamal.c +fetch.o: fetch.c +foodgroups.o: foodgroups.c +gcryptfix.o: gcryptfix.c +id.o: id.c +ike_alg.o: ike_alg.c +ipsec_doi.o: ipsec_doi.c +kernel.o: kernel.c +kernel_alg.o: kernel_alg.c +kernel_netlink.o: kernel_netlink.c +kernel_noklips.o: kernel_noklips.c +kernel_pfkey.o: kernel_pfkey.c +keys.o: keys.c +lex.o: lex.c +log.o: log.c +md2.o: md2.c +md5.o: md5.c +modecfg.o: modecfg.c +mp_defs.o: mp_defs.c +nat_traversal.o: nat_traversal.c +ocsp.o: ocsp.c +oid.o: oid.c +packet.o: packet.c +pem.o: pem.c +pgp.o: pgp.c +pkcs1.o: pkcs1.c +pkcs7.o: pkcs7.c +plutomain.o: plutomain.c +primegen.o: primegen.c +rcv_whack.o: rcv_whack.c +rnd.o: rnd.c +server.o: server.c +sha1.o: sha1.c +smallprime.o: smallprime.c +smartcard.o: smartcard.c +spdb.o: spdb.c +state.o: state.c +timer.o: timer.c +vendor.o: vendor.c +virtual.o: virtual.c +whack.o: whack.c +x509.o: x509.c + +ac.o: constants.h +ac.o: defs.h +ac.o: asn1.h +ac.o: oid.h +ac.o: ac.h +ac.o: x509.h +ac.o: crl.h +ac.o: ca.h +ac.o: certs.h +ac.o: log.h +ac.o: whack.h +ac.o: fetch.h +adns.o: constants.h +adns.o: adns.h +alg_info.o: alg_info.h +alg_info.o: constants.h +alg_info.o: defs.h +alg_info.o: log.h +alg_info.o: whack.h +alg_info.o: sha1.h +alg_info.o: md5.h +alg_info.o: crypto.h +alg_info.o: kernel_alg.h +alg_info.o: ike_alg.h +asn1.o: constants.h +asn1.o: defs.h +asn1.o: mp_defs.h +asn1.o: asn1.h +asn1.o: oid.h +asn1.o: log.h +ca.o: constants.h +ca.o: defs.h +ca.o: log.h +ca.o: x509.h +ca.o: ca.h +ca.o: certs.h +ca.o: whack.h +ca.o: fetch.h +certs.o: constants.h +certs.o: defs.h +certs.o: log.h +certs.o: asn1.h +certs.o: id.h +certs.o: x509.h +certs.o: pgp.h +certs.o: pem.h +certs.o: certs.h +certs.o: pkcs1.h +connections.o: kameipsec.h +connections.o: constants.h +connections.o: defs.h +connections.o: id.h +connections.o: x509.h +connections.o: ca.h +connections.o: crl.h +connections.o: pgp.h +connections.o: certs.h +connections.o: ac.h +connections.o: smartcard.h +connections.o: fetch.h +connections.o: connections.h +connections.o: foodgroups.h +connections.o: demux.h +connections.o: state.h +connections.o: timer.h +connections.o: ipsec_doi.h +connections.o: server.h +connections.o: kernel.h +connections.o: log.h +connections.o: keys.h +connections.o: adns.h +connections.o: dnskey.h +connections.o: whack.h +connections.o: alg_info.h +connections.o: ike_alg.h +connections.o: kernel_alg.h +connections.o: nat_traversal.h +connections.o: virtual.h +constants.o: constants.h +constants.o: defs.h +constants.o: log.h +constants.o: packet.h +cookie.o: constants.h +cookie.o: defs.h +cookie.o: sha1.h +cookie.o: rnd.h +cookie.o: cookie.h +crl.o: constants.h +crl.o: defs.h +crl.o: log.h +crl.o: asn1.h +crl.o: oid.h +crl.o: x509.h +crl.o: crl.h +crl.o: ca.h +crl.o: certs.h +crl.o: keys.h +crl.o: whack.h +crl.o: fetch.h +crl.o: sha1.h +crypto.o: constants.h +crypto.o: defs.h +crypto.o: state.h +crypto.o: log.h +crypto.o: md5.h +crypto.o: sha1.h +crypto.o: crypto.h +crypto.o: alg_info.h +crypto.o: ike_alg.h +defs.o: constants.h +defs.o: defs.h +defs.o: log.h +defs.o: whack.h +demux.o: constants.h +demux.o: defs.h +demux.o: cookie.h +demux.o: connections.h +demux.o: state.h +demux.o: packet.h +demux.o: md5.h +demux.o: sha1.h +demux.o: crypto.h +demux.o: ike_alg.h +demux.o: log.h +demux.o: demux.h +demux.o: ipsec_doi.h +demux.o: timer.h +demux.o: whack.h +demux.o: server.h +demux.o: nat_traversal.h +demux.o: vendor.h +demux.o: modecfg.h +dnskey.o: constants.h +dnskey.o: adns.h +dnskey.o: defs.h +dnskey.o: log.h +dnskey.o: id.h +dnskey.o: connections.h +dnskey.o: keys.h +dnskey.o: dnskey.h +dnskey.o: packet.h +dnskey.o: timer.h +dsa.o: constants.h +dsa.o: defs.h +dsa.o: log.h +dsa.o: rnd.h +dsa.o: gcryptfix.h +dsa.o: dsa.h +elgamal.o: constants.h +elgamal.o: defs.h +elgamal.o: log.h +elgamal.o: rnd.h +elgamal.o: gcryptfix.h +elgamal.o: elgamal.h +fetch.o: constants.h +fetch.o: defs.h +fetch.o: log.h +fetch.o: id.h +fetch.o: asn1.h +fetch.o: pem.h +fetch.o: x509.h +fetch.o: ca.h +fetch.o: whack.h +fetch.o: ocsp.h +fetch.o: crl.h +fetch.o: fetch.h +foodgroups.o: constants.h +foodgroups.o: defs.h +foodgroups.o: connections.h +foodgroups.o: foodgroups.h +foodgroups.o: kernel.h +foodgroups.o: lex.h +foodgroups.o: log.h +foodgroups.o: whack.h +gcryptfix.o: constants.h +gcryptfix.o: defs.h +gcryptfix.o: log.h +gcryptfix.o: rnd.h +gcryptfix.o: gcryptfix.h +id.o: constants.h +id.o: defs.h +id.o: id.h +id.o: log.h +id.o: connections.h +id.o: packet.h +id.o: whack.h +ike_alg.o: constants.h +ike_alg.o: defs.h +ike_alg.o: sha1.h +ike_alg.o: md5.h +ike_alg.o: crypto.h +ike_alg.o: state.h +ike_alg.o: packet.h +ike_alg.o: log.h +ike_alg.o: whack.h +ike_alg.o: spdb.h +ike_alg.o: alg_info.h +ike_alg.o: ike_alg.h +ike_alg.o: db_ops.h +ike_alg.o: connections.h +ike_alg.o: kernel.h +ipsec_doi.o: constants.h +ipsec_doi.o: defs.h +ipsec_doi.o: mp_defs.h +ipsec_doi.o: state.h +ipsec_doi.o: id.h +ipsec_doi.o: x509.h +ipsec_doi.o: crl.h +ipsec_doi.o: ca.h +ipsec_doi.o: certs.h +ipsec_doi.o: smartcard.h +ipsec_doi.o: connections.h +ipsec_doi.o: keys.h +ipsec_doi.o: packet.h +ipsec_doi.o: demux.h +ipsec_doi.o: adns.h +ipsec_doi.o: dnskey.h +ipsec_doi.o: kernel.h +ipsec_doi.o: log.h +ipsec_doi.o: cookie.h +ipsec_doi.o: server.h +ipsec_doi.o: spdb.h +ipsec_doi.o: timer.h +ipsec_doi.o: rnd.h +ipsec_doi.o: ipsec_doi.h +ipsec_doi.o: whack.h +ipsec_doi.o: fetch.h +ipsec_doi.o: pkcs7.h +ipsec_doi.o: asn1.h +ipsec_doi.o: sha1.h +ipsec_doi.o: md5.h +ipsec_doi.o: crypto.h +ipsec_doi.o: vendor.h +ipsec_doi.o: alg_info.h +ipsec_doi.o: ike_alg.h +ipsec_doi.o: kernel_alg.h +ipsec_doi.o: nat_traversal.h +ipsec_doi.o: virtual.h +kernel.o: kameipsec.h +kernel.o: constants.h +kernel.o: defs.h +kernel.o: rnd.h +kernel.o: id.h +kernel.o: connections.h +kernel.o: state.h +kernel.o: timer.h +kernel.o: kernel.h +kernel.o: kernel_netlink.h +kernel.o: kernel_pfkey.h +kernel.o: kernel_noklips.h +kernel.o: log.h +kernel.o: ca.h +kernel.o: server.h +kernel.o: whack.h +kernel.o: keys.h +kernel.o: packet.h +kernel.o: nat_traversal.h +kernel.o: alg_info.h +kernel.o: kernel_alg.h +kernel_alg.o: constants.h +kernel_alg.o: defs.h +kernel_alg.o: connections.h +kernel_alg.o: state.h +kernel_alg.o: packet.h +kernel_alg.o: spdb.h +kernel_alg.o: kernel.h +kernel_alg.o: kernel_alg.h +kernel_alg.o: alg_info.h +kernel_alg.o: log.h +kernel_alg.o: whack.h +kernel_alg.o: db_ops.h +kernel_netlink.o: kameipsec.h +kernel_netlink.o: linux26/rtnetlink.h +kernel_netlink.o: linux26/xfrm.h +kernel_netlink.o: constants.h +kernel_netlink.o: defs.h +kernel_netlink.o: kernel.h +kernel_netlink.o: kernel_netlink.h +kernel_netlink.o: kernel_pfkey.h +kernel_netlink.o: log.h +kernel_netlink.o: whack.h +kernel_netlink.o: kernel_alg.h +kernel_noklips.o: constants.h +kernel_noklips.o: defs.h +kernel_noklips.o: kernel.h +kernel_noklips.o: kernel_noklips.h +kernel_noklips.o: log.h +kernel_noklips.o: whack.h +kernel_pfkey.o: constants.h +kernel_pfkey.o: defs.h +kernel_pfkey.o: kernel.h +kernel_pfkey.o: kernel_pfkey.h +kernel_pfkey.o: log.h +kernel_pfkey.o: whack.h +kernel_pfkey.o: demux.h +kernel_pfkey.o: nat_traversal.h +kernel_pfkey.o: alg_info.h +kernel_pfkey.o: kernel_alg.h +keys.o: constants.h +keys.o: defs.h +keys.o: mp_defs.h +keys.o: id.h +keys.o: x509.h +keys.o: pgp.h +keys.o: certs.h +keys.o: smartcard.h +keys.o: connections.h +keys.o: state.h +keys.o: lex.h +keys.o: keys.h +keys.o: adns.h +keys.o: dnskey.h +keys.o: log.h +keys.o: whack.h +keys.o: timer.h +keys.o: fetch.h +keys.o: nat_traversal.h +lex.o: constants.h +lex.o: defs.h +lex.o: log.h +lex.o: whack.h +lex.o: lex.h +log.o: constants.h +log.o: defs.h +log.o: log.h +log.o: server.h +log.o: state.h +log.o: connections.h +log.o: kernel.h +log.o: whack.h +log.o: timer.h +md2.o: md2.h +md5.o: md5.h +modecfg.o: constants.h +modecfg.o: defs.h +modecfg.o: state.h +modecfg.o: demux.h +modecfg.o: timer.h +modecfg.o: ipsec_doi.h +modecfg.o: log.h +modecfg.o: md5.h +modecfg.o: sha1.h +modecfg.o: crypto.h +modecfg.o: modecfg.h +modecfg.o: whack.h +mp_defs.o: constants.h +mp_defs.o: defs.h +mp_defs.o: mp_defs.h +mp_defs.o: log.h +nat_traversal.o: constants.h +nat_traversal.o: defs.h +nat_traversal.o: log.h +nat_traversal.o: server.h +nat_traversal.o: state.h +nat_traversal.o: connections.h +nat_traversal.o: packet.h +nat_traversal.o: demux.h +nat_traversal.o: kernel.h +nat_traversal.o: whack.h +nat_traversal.o: timer.h +nat_traversal.o: cookie.h +nat_traversal.o: sha1.h +nat_traversal.o: md5.h +nat_traversal.o: crypto.h +nat_traversal.o: vendor.h +nat_traversal.o: ike_alg.h +nat_traversal.o: nat_traversal.h +ocsp.o: constants.h +ocsp.o: defs.h +ocsp.o: log.h +ocsp.o: x509.h +ocsp.o: crl.h +ocsp.o: ca.h +ocsp.o: rnd.h +ocsp.o: asn1.h +ocsp.o: certs.h +ocsp.o: smartcard.h +ocsp.o: oid.h +ocsp.o: whack.h +ocsp.o: pkcs1.h +ocsp.o: keys.h +ocsp.o: fetch.h +ocsp.o: ocsp.h +oid.o: oid.h +packet.o: constants.h +packet.o: defs.h +packet.o: log.h +packet.o: packet.h +packet.o: whack.h +pem.o: constants.h +pem.o: defs.h +pem.o: log.h +pem.o: md5.h +pem.o: whack.h +pem.o: pem.h +pgp.o: constants.h +pgp.o: defs.h +pgp.o: mp_defs.h +pgp.o: log.h +pgp.o: id.h +pgp.o: pgp.h +pgp.o: certs.h +pgp.o: md5.h +pgp.o: whack.h +pgp.o: pkcs1.h +pgp.o: keys.h +pkcs1.o: constants.h +pkcs1.o: defs.h +pkcs1.o: mp_defs.h +pkcs1.o: asn1.h +pkcs1.o: oid.h +pkcs1.o: log.h +pkcs1.o: pkcs1.h +pkcs1.o: md2.h +pkcs1.o: md5.h +pkcs1.o: sha1.h +pkcs1.o: rnd.h +pkcs7.o: constants.h +pkcs7.o: defs.h +pkcs7.o: asn1.h +pkcs7.o: oid.h +pkcs7.o: log.h +pkcs7.o: x509.h +pkcs7.o: certs.h +pkcs7.o: pkcs7.h +pkcs7.o: rnd.h +plutomain.o: constants.h +plutomain.o: defs.h +plutomain.o: id.h +plutomain.o: ca.h +plutomain.o: certs.h +plutomain.o: ac.h +plutomain.o: connections.h +plutomain.o: foodgroups.h +plutomain.o: packet.h +plutomain.o: demux.h +plutomain.o: server.h +plutomain.o: kernel.h +plutomain.o: log.h +plutomain.o: keys.h +plutomain.o: adns.h +plutomain.o: dnskey.h +plutomain.o: rnd.h +plutomain.o: state.h +plutomain.o: ipsec_doi.h +plutomain.o: ocsp.h +plutomain.o: crl.h +plutomain.o: fetch.h +plutomain.o: sha1.h +plutomain.o: md5.h +plutomain.o: crypto.h +plutomain.o: virtual.h +plutomain.o: nat_traversal.h +primegen.o: constants.h +primegen.o: defs.h +primegen.o: log.h +primegen.o: rnd.h +primegen.o: gcryptfix.h +rcv_whack.o: constants.h +rcv_whack.o: defs.h +rcv_whack.o: id.h +rcv_whack.o: ca.h +rcv_whack.o: certs.h +rcv_whack.o: ac.h +rcv_whack.o: smartcard.h +rcv_whack.o: connections.h +rcv_whack.o: foodgroups.h +rcv_whack.o: whack.h +rcv_whack.o: packet.h +rcv_whack.o: demux.h +rcv_whack.o: state.h +rcv_whack.o: ipsec_doi.h +rcv_whack.o: kernel.h +rcv_whack.o: rcv_whack.h +rcv_whack.o: log.h +rcv_whack.o: keys.h +rcv_whack.o: adns.h +rcv_whack.o: dnskey.h +rcv_whack.o: server.h +rcv_whack.o: fetch.h +rcv_whack.o: ocsp.h +rcv_whack.o: crl.h +rcv_whack.o: kernel_alg.h +rcv_whack.o: ike_alg.h +rnd.o: sha1.h +rnd.o: constants.h +rnd.o: defs.h +rnd.o: rnd.h +rnd.o: log.h +rnd.o: timer.h +server.o: constants.h +server.o: defs.h +server.o: state.h +server.o: connections.h +server.o: kernel.h +server.o: log.h +server.o: server.h +server.o: timer.h +server.o: packet.h +server.o: demux.h +server.o: rcv_whack.h +server.o: rcv_info.h +server.o: keys.h +server.o: adns.h +server.o: dnskey.h +server.o: whack.h +server.o: kameipsec.h +server.o: nat_traversal.h +sha1.o: sha1.h +smallprime.o: constants.h +smallprime.o: defs.h +smallprime.o: gcryptfix.h +smartcard.o: constants.h +smartcard.o: rsaref/unix.h +smartcard.o: rsaref/pkcs11.h +smartcard.o: defs.h +smartcard.o: mp_defs.h +smartcard.o: log.h +smartcard.o: x509.h +smartcard.o: ca.h +smartcard.o: certs.h +smartcard.o: keys.h +smartcard.o: smartcard.h +smartcard.o: whack.h +smartcard.o: fetch.h +spdb.o: constants.h +spdb.o: defs.h +spdb.o: id.h +spdb.o: connections.h +spdb.o: state.h +spdb.o: packet.h +spdb.o: keys.h +spdb.o: kernel.h +spdb.o: log.h +spdb.o: spdb.h +spdb.o: whack.h +spdb.o: sha1.h +spdb.o: md5.h +spdb.o: crypto.h +spdb.o: alg_info.h +spdb.o: kernel_alg.h +spdb.o: ike_alg.h +spdb.o: db_ops.h +spdb.o: nat_traversal.h +state.o: constants.h +state.o: defs.h +state.o: connections.h +state.o: state.h +state.o: kernel.h +state.o: log.h +state.o: packet.h +state.o: keys.h +state.o: rnd.h +state.o: timer.h +state.o: whack.h +state.o: demux.h +state.o: ipsec_doi.h +state.o: sha1.h +state.o: md5.h +state.o: crypto.h +timer.o: constants.h +timer.o: defs.h +timer.o: connections.h +timer.o: state.h +timer.o: demux.h +timer.o: ipsec_doi.h +timer.o: kernel.h +timer.o: server.h +timer.o: log.h +timer.o: rnd.h +timer.o: timer.h +timer.o: whack.h +timer.o: nat_traversal.h +vendor.o: constants.h +vendor.o: defs.h +vendor.o: log.h +vendor.o: md5.h +vendor.o: connections.h +vendor.o: packet.h +vendor.o: demux.h +vendor.o: whack.h +vendor.o: vendor.h +vendor.o: kernel.h +vendor.o: nat_traversal.h +virtual.o: constants.h +virtual.o: defs.h +virtual.o: log.h +virtual.o: connections.h +virtual.o: whack.h +virtual.o: virtual.h +whack.o: constants.h +whack.o: defs.h +whack.o: whack.h +x509.o: constants.h +x509.o: defs.h +x509.o: mp_defs.h +x509.o: log.h +x509.o: id.h +x509.o: asn1.h +x509.o: oid.h +x509.o: pkcs1.h +x509.o: x509.h +x509.o: crl.h +x509.o: ca.h +x509.o: certs.h +x509.o: keys.h +x509.o: whack.h +x509.o: fetch.h +x509.o: ocsp.h +x509.o: sha1.h diff --git a/src/pluto/PLUTO-CONVENTIONS b/src/pluto/PLUTO-CONVENTIONS new file mode 100644 index 000000000..5288dd2bb --- /dev/null +++ b/src/pluto/PLUTO-CONVENTIONS @@ -0,0 +1,127 @@ +Notes on Pluto Conventions +========================== + +RCSID $Id: PLUTO-CONVENTIONS,v 1.1 2004/03/15 20:35:28 as Exp $ + +Pluto has its own stylistic conventions. They are fairly easily +inferred by reading the code. + +- sample formatting: + +void +fun(char *s) +{ + if (s == NULL) + { + return ""; + } + else + { + switch (*s) + { + default: + s++; + /* fall through */ + case '\0': + return s; + } + } +} + +- a function definition has its function identifier at the margin + +- indentation is in steps of 4 columns (tabstops are every 8 columns) + +- try to keep lines shorter than 80 columns + +- space should be canonical: + + no line should have trailing whitespace + + leading whitespace should use tabs where possible + + indentation should be precise + + there should be no empty lines at the end of a file. + +- braces go on their own line, indented the same as the start of what they are part of + +- switch labels are indented the same as the enclosing braces + +- if a case falls through, say so explicitly + +- spaces follow control flow reserved words (but not function names) + +- the operand of return need not be parenthesized + +- be careful with types. For example, use size_t and ssize_t. + Use const wherever possible. + +- we pretend that C has a strong boolean type. + We actually define bool with constants TRUE and FALSE. + Other types cannot be used as the complete expression in a test. + Hence: + if (s == NULL) + One exception: lset_t values can be treated as booleans + (technically they are, in the original sense of the word) + + +- memsetting a pointer to binary zero is not guaranteed to make it NULL + +- side-effects of expressions are to be avoided. + BAD: if (i++ == 9) + OK: i++; + +- variables are to have as small a scope as is possible. + Move definitions into inner blocks whenever possible. + Often initializing definitions become possible and are clearer. + +- within a block that has declarations, separate the declarations from + the other statements with a blank line. + +- "magic numbers" are suspect. Most integers in code stand for something. + They should be given a name, and that name used consistently. + +- don't use malloc/free -- use the wrappers (see defs.h) + +- it is good to put comments on #else and #endif to show what + they match with. I use ! to indicate the sense of the test: + #ifdef CRUD + #else /* !CRUD */ + #endif /* !CRUD */ + + #ifndef CRUD + #else /* CRUD */ + #endif /* CRUD */ + +- all functions and variables that are exported from a .c file should + be declared in that file's header file. Because the .c includes the + header, the declaration and the definition will be checked by the + compiler. There is almost no excuse for the "extern" keyword + in a .c file. + +- when lines are too long and expressions are to be broken, try to + break just before a binary operator. The outermost binary operator + is preferred. This is perhaps the most unconventional convention. + It allows the structure of code to be evident from a scan of the + left margin. Example: + if (next_step == vos_his_client + && sameaddr(&c->spd.that.host_addr, &his_client)) + next_step = vos_done; + and + p = oppo_instantiate(p, &c->spd.that.host_addr, &c->spd.that.id + , NULL, &our_client, &his_client); + Note the different indentation of the continuations. The continuation + of a control flow statement is not indented but other continuations are. + +- Never put two statements on one line. + REALLY BAD: if (cat); + Exception: some macro definitions. + +- C preprocessor macros are implemented by a kind of textual substitution. + Be sure to put parentheses around references to macro arguments and + around the whole macro body. If the body is meant to be a statement, + put braces around it instead. + + #define RETURN_STF_FAILURE(f) \ + { int r = (f); if (r != NOTHING_WRONG) return STF_FAIL + r; } + +- adding #include statements adds dependencies. The Makefile should be + changed to reflect them. Target "makedepend" will try to list dependencies + in a way suitable for pasting into Makefile 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/Config.ike_alg b/src/pluto/alg/Config.ike_alg new file mode 100644 index 000000000..0fcda4cad --- /dev/null +++ b/src/pluto/alg/Config.ike_alg @@ -0,0 +1,9 @@ +## +## IKE algorithms config. for static linking into pluto +## By now 3DES,MD5 and SHA1 are already present in pluto. +## +CONFIG_IKE_ALG_AES=y +CONFIG_IKE_ALG_BLOWFISH=y +CONFIG_IKE_ALG_SERPENT=y +CONFIG_IKE_ALG_TWOFISH=y +CONFIG_IKE_ALG_SHA2=y diff --git a/src/pluto/alg/Makefile b/src/pluto/alg/Makefile new file mode 100644 index 000000000..9732cc80e --- /dev/null +++ b/src/pluto/alg/Makefile @@ -0,0 +1,93 @@ +# pluto/alg Makefile +# 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. +# +# $Id: Makefile,v 1.3 2004/06/23 04:45:20 as Exp $ + +Make.common: ../Makefile + make -s -C .. showdefs > $@ + +-include Make.common +include Config.ike_alg + +LIBCRYPTO:=../../../lib/libcrypto +ALLFLAGS=$(CPPFLAGS) $(CFLAGS) -I .. -I- -I ../../../linux/include -I $(LIBCRYPTO) +LIBALG := libalg.o + +all : $(LIBALG) + +include $(wildcard Makefile.ike_alg_*) +#include $(wildcard Makefile.ike_alg_[ab]*) + +ALG_DIRS:=$(ALG_DIRS-y) +ALG_LIBS:=$(ALG_LIBS-y) +ALG_SRCS:=$(ALG_SRCS-y) +ALG_OBJS:=$(ALG_OBJS-y) +$(LIBALG): ike_alginit.o $(ALG_OBJS) $(ALG_LIBS) + $(LD) -r -o $@ $^ + +# Search for IKE_ALG_INIT_NAME: in ike_alg_*.c to +# build ike_alginit.c:ike_alginit() + +ike_alginit.c: $(ALG_SRCS) Makefile Config.ike_alg + @awk ' \ + BEGIN { print "extern int ike_alg_init(void); \ + int ike_alg_init(void) {" } \ + /IKE_ALG_INIT_NAME:/ \ + { print "{ extern int " $$2" (void); " $$2 "();}" } \ + END { print "return 0;}" } \ + ' $(ALG_SRCS) /dev/null > $@ + +clean : + @for i in $(ALG_DIRS);do make -C $$i clean;done + rm -f *.[oa] ike_alginit.c Make.common + +gatherdeps: + @ls $(ALG_SRCS) | grep '\.c' | sed -e 's/\(.*\)\.c$$/\1.o: \1.c/' + @echo + @ls $(ALG_SRCS) | grep '\.c' | xargs grep '^#[ ]*include[ ]*"' | \ + sed -n -e '/#include.*"lib/d' \ + -e 's/\.c:#[ ]*include[ ]*"/.o: ..\//' -e 's/".*//p' + +# Dependencies generated by "make gatherdeps": + +ike_alg_aes.o: ike_alg_aes.c +ike_alg_blowfish.o: ike_alg_blowfish.c +ike_alg_serpent.o: ike_alg_serpent.c +ike_alg_sha2.o: ike_alg_sha2.c +ike_alg_twofish.o: ike_alg_twofish.c + +ike_alg_aes.o: ../constants.h +ike_alg_aes.o: ../defs.h +ike_alg_aes.o: ../log.h +ike_alg_aes.o: ../alg_info.h +ike_alg_aes.o: ../ike_alg.h +ike_alg_blowfish.o: ../constants.h +ike_alg_blowfish.o: ../defs.h +ike_alg_blowfish.o: ../log.h +ike_alg_blowfish.o: ../alg_info.h +ike_alg_blowfish.o: ../ike_alg.h +ike_alg_serpent.o: ../constants.h +ike_alg_serpent.o: ../defs.h +ike_alg_serpent.o: ../log.h +ike_alg_serpent.o: ../alg_info.h +ike_alg_serpent.o: ../ike_alg.h +ike_alg_sha2.o: ../constants.h +ike_alg_sha2.o: ../defs.h +ike_alg_sha2.o: ../log.h +ike_alg_sha2.o: ../alg_info.h +ike_alg_sha2.o: ../ike_alg.h +ike_alg_twofish.o: ../constants.h +ike_alg_twofish.o: ../defs.h +ike_alg_twofish.o: ../log.h +ike_alg_twofish.o: ../alg_info.h +ike_alg_twofish.o: ../ike_alg.h diff --git a/src/pluto/alg/Makefile.ike_alg_aes b/src/pluto/alg/Makefile.ike_alg_aes new file mode 100644 index 000000000..12009ba5c --- /dev/null +++ b/src/pluto/alg/Makefile.ike_alg_aes @@ -0,0 +1,14 @@ +ALG:=aes +CONFIG_YES:=$(CONFIG_IKE_ALG_AES) +DIR_AES:=$(LIBCRYPTO)/libaes + +ALG_DIRS-$(CONFIG_YES) := $(ALG_DIRS-$(CONFIG_YES)) $(DIR_AES) +ALG_LIBS-$(CONFIG_YES) := $(ALG_LIBS-$(CONFIG_YES)) $(DIR_AES)/libaes.a +ALG_SRCS-$(CONFIG_YES) := $(ALG_SRCS-$(CONFIG_YES)) ike_alg_$(ALG).c +ALG_OBJS-$(CONFIG_YES) := $(ALG_OBJS-$(CONFIG_YES)) ike_alg_$(ALG).o + +$(DIR_AES)/libaes.a: + make -C $(DIR_AES) CFLAGS="$(CFLAGS)" libaes.a + +ike_alg_$(ALG).o: ike_alg_$(ALG).c + $(CC) -I $(LIBCRYPTO) -I$(DIR_AES) $(COPTS) $(ALLFLAGS) -c $< diff --git a/src/pluto/alg/Makefile.ike_alg_blowfish b/src/pluto/alg/Makefile.ike_alg_blowfish new file mode 100644 index 000000000..c3af6199b --- /dev/null +++ b/src/pluto/alg/Makefile.ike_alg_blowfish @@ -0,0 +1,13 @@ +ALG:=blowfish +CONFIG_YES:=$(CONFIG_IKE_ALG_BLOWFISH) +DIR_BLOWFISH:=$(LIBCRYPTO)/libblowfish +ALG_DIRS-$(CONFIG_YES) := $(ALG_DIRS-$(CONFIG_YES)) $(DIR_BLOWFISH) +ALG_LIBS-$(CONFIG_YES) := $(ALG_LIBS-$(CONFIG_YES)) $(DIR_BLOWFISH)/libblowfish.a +ALG_SRCS-$(CONFIG_YES) := $(ALG_SRCS-$(CONFIG_YES)) ike_alg_$(ALG).c +ALG_OBJS-$(CONFIG_YES) := $(ALG_OBJS-$(CONFIG_YES)) ike_alg_$(ALG).o + +$(DIR_BLOWFISH)/libblowfish.a: + make -C $(DIR_BLOWFISH) CFLAGS="$(CFLAGS)" libblowfish.a + +ike_alg_$(ALG).o: ike_alg_$(ALG).c + $(CC) -I $(LIBCRYPTO) -I$(DIR_BLOWFISH) $(COPTS) $(ALLFLAGS) -c $< diff --git a/src/pluto/alg/Makefile.ike_alg_serpent b/src/pluto/alg/Makefile.ike_alg_serpent new file mode 100644 index 000000000..3395ac0ea --- /dev/null +++ b/src/pluto/alg/Makefile.ike_alg_serpent @@ -0,0 +1,13 @@ +ALG:=serpent +CONFIG_YES:=$(CONFIG_IKE_ALG_SERPENT) +DIR_SERPENT:=$(LIBCRYPTO)/libserpent +ALG_DIRS-$(CONFIG_YES) := $(ALG_DIRS-$(CONFIG_YES)) $(DIR_SERPENT) +ALG_LIBS-$(CONFIG_YES) := $(ALG_LIBS-$(CONFIG_YES)) $(DIR_SERPENT)/libserpent.a +ALG_SRCS-$(CONFIG_YES) := $(ALG_SRCS-$(CONFIG_YES)) ike_alg_$(ALG).c +ALG_OBJS-$(CONFIG_YES) := $(ALG_OBJS-$(CONFIG_YES)) ike_alg_$(ALG).o + +$(DIR_SERPENT)/libserpent.a: + make -C $(DIR_SERPENT) CFLAGS="$(CFLAGS)" libserpent.a + +ike_alg_$(ALG).o: ike_alg_$(ALG).c + $(CC) -I $(LIBCRYPTO) -I$(DIR_SERPENT) $(COPTS) $(ALLFLAGS) -c $< diff --git a/src/pluto/alg/Makefile.ike_alg_sha2 b/src/pluto/alg/Makefile.ike_alg_sha2 new file mode 100644 index 000000000..67e68a667 --- /dev/null +++ b/src/pluto/alg/Makefile.ike_alg_sha2 @@ -0,0 +1,13 @@ +ALG:=sha2 +CONFIG_YES:=$(CONFIG_IKE_ALG_SHA2) +DIR_SHA2:=$(LIBCRYPTO)/libsha2 +ALG_DIRS-$(CONFIG_YES) := $(ALG_DIRS-$(CONFIG_YES)) $(DIR_SHA2) +ALG_LIBS-$(CONFIG_YES) := $(ALG_LIBS-$(CONFIG_YES)) $(DIR_SHA2)/libsha2.a +ALG_SRCS-$(CONFIG_YES) := $(ALG_SRCS-$(CONFIG_YES)) ike_alg_$(ALG).c +ALG_OBJS-$(CONFIG_YES) := $(ALG_OBJS-$(CONFIG_YES)) ike_alg_$(ALG).o + +$(DIR_SHA2)/libsha2.a: + make -C $(DIR_SHA2) libsha2.a + +ike_alg_$(ALG).o: ike_alg_$(ALG).c + $(CC) -I $(LIBCRYPTO) -I$(DIR_SHA2) $(COPTS) $(ALLFLAGS) -c $< diff --git a/src/pluto/alg/Makefile.ike_alg_twofish b/src/pluto/alg/Makefile.ike_alg_twofish new file mode 100644 index 000000000..dcd30dd3e --- /dev/null +++ b/src/pluto/alg/Makefile.ike_alg_twofish @@ -0,0 +1,13 @@ +ALG:=twofish +CONFIG_YES:=$(CONFIG_IKE_ALG_TWOFISH) +DIR_TWOFISH:=$(LIBCRYPTO)/libtwofish +ALG_DIRS-$(CONFIG_YES) := $(ALG_DIRS-$(CONFIG_YES)) $(DIR_TWOFISH) +ALG_LIBS-$(CONFIG_YES) := $(ALG_LIBS-$(CONFIG_YES)) $(DIR_TWOFISH)/libtwofish.a +ALG_SRCS-$(CONFIG_YES) := $(ALG_SRCS-$(CONFIG_YES)) ike_alg_$(ALG).c +ALG_OBJS-$(CONFIG_YES) := $(ALG_OBJS-$(CONFIG_YES)) ike_alg_$(ALG).o + +$(DIR_TWOFISH)/libtwofish.a: + make -C $(DIR_TWOFISH) CFLAGS="$(CFLAGS)" libtwofish.a + +ike_alg_$(ALG).o: ike_alg_$(ALG).c + $(CC) -I $(LIBCRYPTO) -I$(DIR_TWOFISH) $(COPTS) $(ALLFLAGS) -c $< 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..ad24f7cf0 --- /dev/null +++ b/src/pluto/alg/ike_alg_sha2.c @@ -0,0 +1,61 @@ +#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" + +#define SHA2_256_DIGEST_SIZE (256/BITS_PER_BYTE) +#define SHA2_512_DIGEST_SIZE (512/BITS_PER_BYTE) + +static void sha256_hash_final(u_char *hash, sha256_context *ctx) +{ + sha256_final(ctx); + memcpy(hash, &ctx->sha_out[0], SHA2_256_DIGEST_SIZE); +} +static void sha512_hash_final(u_char *hash, sha512_context *ctx) +{ + sha512_final(ctx); + memcpy(hash, &ctx->sha_out[0], SHA2_512_DIGEST_SIZE); +} +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_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, + hash_digest_size: SHA2_256_DIGEST_SIZE, +}; +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_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, + hash_digest_size: SHA2_512_DIGEST_SIZE, +}; +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_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_info.c b/src/pluto/alg_info.c new file mode 100644 index 000000000..4ac7f2ca9 --- /dev/null +++ b/src/pluto/alg_info.c @@ -0,0 +1,1197 @@ +/* + * 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; + + 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; + + 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..c1e0261d8 --- /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..92b40605f --- /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..cca128965 --- /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 "/etc/ipsec.d/private" +#define HOST_CERT_PATH "/etc/ipsec.d/certs" +#define CA_CERT_PATH "/etc/ipsec.d/cacerts" +#define A_CERT_PATH "/etc/ipsec.d/acerts" +#define AA_CERT_PATH "/etc/ipsec.d/aacerts" +#define OCSP_CERT_PATH "/etc/ipsec.d/ocspcerts" +#define CRL_PATH "/etc/ipsec.d/crls" +#define REQ_PATH "/etc/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..263bdbd1e --- /dev/null +++ b/src/pluto/connections.c @@ -0,0 +1,4431 @@ +/* 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.42 2006/04/22 21:59:20 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 "kernel_alg.h" +#ifdef NAT_TRAVERSAL +#include "nat_traversal.h" +#endif + +#ifdef VIRTUAL_IP +#include "virtual.h" +#endif + +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; + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + + 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); + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + + 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; +#ifdef NAT_TRAVERSAL + 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; +#else + hp->me.port = c->spd.this.host_port; + hp->him.port = c->spd.that.host_port; +#endif + 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) + { + 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); + } + } + +#ifdef VIRTUAL_IP + if (c->kind != CK_GOING_AWAY) pfreeany(c->spd.that.virt); +#endif + +#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'; + +#ifdef VIRTUAL_IP + if (is_virtual_end(this) && isanyaddr(&this->host_addr)) + { + host = "%virtual"; + } +#endif + + /* [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->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_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; + } + } +#ifdef VIRTUAL_IP + 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; + } +#endif + 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->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; + +#ifdef VIRTUAL_IP + 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; + } +#endif + + unshare_connection_strings(c); + (void)orient(c); + 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(); + +#ifdef VIRTUAL_IP + if (t->spd.that.virt) + { + DBG_log("virtual_ip not supported in group instance"); + t->spd.that.virt = NULL; + } +#endif + + /* 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 +#ifdef NAT_TRAVERSAL +, u_int16_t his_port +#endif +, 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); +#ifdef NAT_TRAVERSAL + if (his_port) d->spd.that.host_port = his_port; +#endif + 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 +#ifdef NAT_TRAVERSAL +, u_int16_t his_port +#endif +#ifdef VIRTUAL_IP +, const ip_subnet *his_net +#endif +, const struct id *his_id) +{ +#ifdef NAT_TRAVERSAL + struct connection *d = instantiate(c, him, his_port, his_id); +#else + struct connection *d = instantiate(c, him, his_id); +#endif + +#ifdef VIRTUAL_IP + 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; + } +#endif + + 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) +{ +#ifdef NAT_TRAVERSAL + struct connection *d = instantiate(c, him, 0, his_id); +#else + struct connection *d = instantiate(c, him, his_id); +#endif + + 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); +#ifdef NAT_TRAVERSAL + if (c->spd.that.host_port != pluto_port) + { + p += strlen(p); + sprintf(p, ":%d", c->spd.that.host_port); + } +#endif + } + } +} + +/* 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) + { +#ifdef NAT_TRAVERSAL + if (p->ike_float) continue; +#endif + 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) + { + 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(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, *n; + + for (c = con_by_name(nm, TRUE); c != NULL; c = n) + { + 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(); + } + } +} + +/* 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.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 */ + +#ifdef NAT_TRAVERSAL + 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))) +#else + 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)) +#endif + { + 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) + { + /* if we have requirements for the policy, + * choose the first matching connection. + */ + while (c != NULL) + { + if ((c->policy & policy) == policy) + 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 OAKLEY_RSA_SIG: + auth_policy = POLICY_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_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_trust && matching_request; + + DBG(DBG_CONTROLMORE, + DBG_log("%s: %s match (id: %s, trust: %s, request: %s)" + , d->name + , match ? "full":" no" + , match_name[matching_id] + , 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; + +#ifdef NAT_TRAVERSAL + if (c->spd.that.host_port != d->spd.that.host_port + && d->kind == CK_INSTANCE) + continue; +#endif + + /* authentication used must fit policy of this connection */ + if ((d->policy & auth_policy) == LEMPTY) + continue; /* our auth isn't OK for this connection */ + + switch (auth) + { + case OAKLEY_PRESHARED_KEY: + /* 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: + /* + * 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); + } +} + +#ifdef VIRTUAL_IP +/** + * 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 */ +} +#endif + +/* 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 + { +#ifdef VIRTUAL_IP + if ((!samesubnet(&sr->that.client, peer_net)) && (!is_virtual_connection(d))) +#else + if (!samesubnet(&sr->that.client, peer_net)) +#endif + continue; +#ifdef VIRTUAL_IP + 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; +#endif + } + } + 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 (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 (name == NULL || streq(c->name, name)) + array[count++]=c; + } + + /* sort it! */ + qsort(array, count, sizeof(struct connection *), connection_compare_qsort); + + for (i=0; iinterface->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); + } + } + 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; +#ifdef NAT_TRAVERSAL + 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; + } +#endif + } +} + +/* 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..6dfddbe22 --- /dev/null +++ b/src/pluto/connections.h @@ -0,0 +1,375 @@ +/* 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]); + +#ifdef VIRTUAL_IP +struct virtual_t; +#endif + +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; + 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 */ +#ifdef VIRTUAL_IP + struct virtual_t *virt; +#endif + 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; + 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 +#ifdef NAT_TRAVERSAL + , u_int16_t his_port +#endif +#ifdef VIRTUAL_IP + , const ip_subnet *his_net +#endif + , 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); +#ifdef NAT_TRAVERSAL +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 /* NAT_TRAVERSAL */ + +#endif /* _CONNECTIONS_H */ diff --git a/src/pluto/constants.c b/src/pluto/constants.c new file mode 100644 index 000000000..27e4db1e0 --- /dev/null +++ b/src/pluto/constants.c @@ -0,0 +1,1271 @@ +/* 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 LDAP_VER +#if LDAP_VER == 2 + " LDAP_V2" +#else + " LDAP_V3" +#endif +#endif +#ifdef SMARTCARD + " SMARTCARD" +#endif +#ifdef VENDORID + " VENDORID" +#endif +#ifdef XAUTH_VID + " XAUTH_VID" +#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", + 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 }; + +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", + NULL + }; + +enum_names modecfg_attr_names = + { INTERNAL_IP4_ADDRESS , INTERNAL_IP6_SUBNET, modecfg_attr_name , NULL }; + +/* 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..b66d002ee --- /dev/null +++ b/src/pluto/constants.h @@ -0,0 +1,1184 @@ +/* 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 DEFAULT_CTLBASE +# define DEFAULT_CTLBASE "/var/run/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) /* ought to be supplied by md2.h */ +#define MD5_DIGEST_SIZE (128 / BITS_PER_BYTE) /* ought to be supplied by md5.h */ +#define SHA1_DIGEST_SIZE (160 / BITS_PER_BYTE) /* ought to be supplied by sha1.h */ + +#define DES_CBC_BLOCK_SIZE (64 / BITS_PER_BYTE) + +#define DSS_QBITS 160 /* bits in DSS's "q" (FIPS 186-1) */ + +/* to statically allocate IV, we need max of + * MD5_DIGEST_SIZE, SHA1_DIGEST_SIZE, and DES_CBC_BLOCK_SIZE. + * To avoid combinatorial explosion, we leave out DES_CBC_BLOCK_SIZE. + */ +#define MAX_DIGEST_LEN_OLD (MD5_DIGEST_SIZE > SHA1_DIGEST_SIZE? MD5_DIGEST_SIZE : SHA1_DIGEST_SIZE) + +/* for max: SHA2_512 */ +#define MAX_DIGEST_LEN (512/BITS_PER_BYTE) + +/* 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, + + STATE_MODE_CFG_R0, /* these states are used on the responder */ + STATE_MODE_CFG_R1, + STATE_MODE_CFG_R2, + + STATE_MODE_CFG_I1, /* this is used on the initiator */ + STATE_MODE_CFG_I2, + + 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_MODE_CFG_R1) | LELEM(STATE_MODE_CFG_I2)) + +#define IS_PHASE1(s) ((STATE_MAIN_R0 <= (s) && (s) <= STATE_MAIN_I4) \ + || (STATE_MODE_CFG_R0 <= (s) && (s) <= STATE_MODE_CFG_I2)) +#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_MODE_CFG_R0 \ + || (s) == STATE_MODE_CFG_R1 \ + || (s) == STATE_MODE_CFG_I2) +#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; + +/* 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 LRANGES(POLICY_PSK, POLICY_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? */ + + +/* 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_PSK | POLICY_RSASIG)) + + +/* 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 +#define HMAC_BUFSIZE 64 + +/* 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..8d4b3bd7b --- /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..24939bd04 --- /dev/null +++ b/src/pluto/crypto.c @@ -0,0 +1,261 @@ +/* crypto interfaces + * 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: 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, +}; + +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_digest_size: MD5_DIGEST_SIZE, + hash_init: (void (*)(void *)) MD5Init, + hash_update: (void (*)(void *, const u_int8_t *, size_t)) MD5Update, + hash_final: (void (*)(u_char *, void *)) MD5Final, +}; + +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_digest_size: SHA1_DIGEST_SIZE, + 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(); +} + +/* 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', HMAC_BUFSIZE); + + if (key_len <= HMAC_BUFSIZE) + { + 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, HMAC_BUFSIZE); + + for (k = 0; k < HMAC_BUFSIZE; 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, HMAC_BUFSIZE); +} + +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, HMAC_BUFSIZE); + 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..d29475af2 --- /dev/null +++ b/src/pluto/crypto.h @@ -0,0 +1,107 @@ +/* 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[HMAC_BUFSIZE], buf2[HMAC_BUFSIZE]; + }; + +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..16f6a3949 --- /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 + */ +bool +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..7e92ea540 --- /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 bool 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..bbd50f93c --- /dev/null +++ b/src/pluto/demux.c @@ -0,0 +1,2427 @@ +/* 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.13 2005/02/18 21:08:59 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" +#ifdef NAT_TRAVERSAL +#include "nat_traversal.h" +#endif +#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 +#ifdef NAT_TRAVERSAL + , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(KE) +#else + , P(KE) | P(NONCE), P(VID) | P(CR), PT(KE) +#endif + , 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 +#ifdef NAT_TRAVERSAL + , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(ID) +#else + , P(KE) | P(NONCE), P(VID) | P(CR), PT(ID) +#endif + , 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 +#ifdef NAT_TRAVERSAL + , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(NONE) +#else + , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID), PT(NONE) +#endif + , 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 +#ifdef NAT_TRAVERSAL + , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(HASH) +#else + , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID), PT(HASH) +#endif + , 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 }, + + /* MODE_CFG_x: + * Case R0: Responder -> Initiator + * <- Req(addr=0) + * Reply(ad=x) -> + * + * Case R1: Set(addr=x) -> + * <- Ack(ok) + */ + + { STATE_MODE_CFG_R0, STATE_MODE_CFG_R1 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inR0 }, + + { STATE_MODE_CFG_R1, STATE_MODE_CFG_R2 + , SMF_ALL_AUTH | SMF_ENCRYPTED + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inR1 }, + + { STATE_MODE_CFG_R2, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, + + { 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_inR1 }, + +#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. + */ +#ifdef NAT_TRAVERSAL + 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 +#endif + 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 +#ifdef NAT_TRAVERSAL +_send_packet(struct state *st, const char *where, bool verbose) +#else +send_packet(struct state *st, const char *where) +#endif +{ + struct connection *c = st->st_connection; + int port_buf; + bool err; + +#ifdef NAT_TRAVERSAL + u_int8_t ike_pkt[MAX_OUTPUT_UDP_SIZE]; + u_int8_t *ptr; + unsigned long len; + + if ((c->interface->ike_float == TRUE) && (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; + } +#endif + + 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) */ + +#ifdef NAT_TRAVERSAL + err = sendto(c->interface->fd + , ptr, len, 0 + , sockaddrof(&c->spd.that.host_addr) + , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)len; +#else + err = sendto(c->interface->fd + , st->st_tpacket.ptr, st->st_tpacket.len, 0 + , sockaddrof(&c->spd.that.host_addr) + , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)st->st_tpacket.len; +#endif + + /* restore port */ + setportof(port_buf, &c->spd.that.host_addr); + + if (err) + { +#ifdef NAT_TRAVERSAL + /* do not log NAT-T Keep Alive packets */ + if (!verbose) + return FALSE; +#endif + 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; + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + + /* 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))); + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + +#ifdef IKEV2 +#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; + } +#endif /* IKEV2 */ + + 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); + 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; + } + + if (st->st_state == STATE_MODE_CFG_R2) /* Have we just give an IP address to peer? */ + { + st->st_state = STATE_MAIN_R3; /* ISAKMP is up... */ + } + + 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("Mode Config 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("Mode Config message is invalid because" + " it has a Responder Cookie of 0"); + /* XXX Could send notification back */ + return; + } + + if (md->hdr.isa_msgid == 0) + { + plog("Mode Config 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) + { + /* No appropriate Mode Config 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("Mode Config 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, "Mode Config 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. + * + */ + + 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_R1; + } + 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) + { + while (!LHAS(smc->flags, st->st_oakley.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; + } + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + + 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; +#ifdef NAT_TRAVERSAL + 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; +#endif + 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) +{ + 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"); + +#ifdef NAT_TRAVERSAL + if (nat_traversal_enabled) + nat_traversal_change_port_lookup(md, md->st); +#endif + + /* 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); + +#ifdef NAT_TRAVERSAL + 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 = "}"; + } +#endif + + /* 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); + } + + /* Should we start Mode Config as a client */ + if (st->st_connection->spd.this.modecfg + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !st->st_modecfg.started) + { + DBG(DBG_CONTROL, + DBG_log("modecfg client is starting") + ) + modecfg_send_request(st); + break; + } + + /* Should we set the peer's IP address regardless? */ +/* if (st->st_connection->spd.that.modecfg + && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !st->st_modecfg.vars_set + && !(st->st_connection->policy & POLICY_MODECFG_PULL)) + { + st->st_state = STATE_MODE_CFG_R1; + set_cur_state(st); + plog("Sending MODE CONFIG set"); + modecfg_start_set(st); + break; + } +*/ + /* wait for modecfg_set */ + 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->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..7adac44f3 --- /dev/null +++ b/src/pluto/demux.h @@ -0,0 +1,100 @@ +/* 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); +#ifdef NAT_TRAVERSAL +#define send_packet(st,wh) _send_packet(st,wh,TRUE) +extern bool _send_packet(struct state *st, const char *where, bool verbose); +#else +extern bool send_packet(struct state *st, const char *where); +#endif +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 20 + struct payload_digest + digest[PAYLIMIT], + *digest_roof, + *chain[ISAKMP_NEXT_ROOF]; +#ifdef NAT_TRAVERSAL + unsigned short nat_traversal_vid; +#endif +}; + +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..9aca1938d --- /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..075b88fd2 --- /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.11 2005/11/25 10:08:00 as Exp $ + */ + +#include +#include +#include +#include +#include + +#ifdef THREADS +#include +#endif + +#ifdef LIBCURL +#include +#endif + +#include + +#ifdef LDAP_VER +#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) */ + free(response.ptr); + } + return strlen(errorbuffer) > 0 ? "libcurl error" : NULL; +#else /* !LIBCURL */ + return "warning: not compiled with libcurl support"; +#endif /* !LIBCURL */ +} + +#ifdef LDAP_VER +/* + * 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_VER == 2)? LDAP_VERSION2 : 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 /* !LDAP_VER */ +static err_t +fetch_ldap_url(char *url, chunk_t *blob) +{ + return "LDAP URL fetching not activated in pluto source code"; +} +#endif /* !LDAP_VER */ + +/* + * 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_FILE, (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) */ + 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 != 0) + { + 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..52e32f0fb --- /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 "/etc/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..4e306d3a7 --- /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..47393079a --- /dev/null +++ b/src/pluto/ike_alg.c @@ -0,0 +1,459 @@ +/* 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; + u_int ealg, halg, modp, eklen = 0; + struct encrypt_desc *enc_desc; + int i; + + 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 (ike_info->ike_eklen) + db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, ike_info->ike_eklen); + db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY); + 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_") */ + ); +} + +/* + * 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..a41718c04 --- /dev/null +++ b/src/pluto/ike_alg.h @@ -0,0 +1,73 @@ +/* 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); +}; + +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_digest_size; + 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_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..fe5c846a7 --- /dev/null +++ b/src/pluto/ipsec_doi.c @@ -0,0 +1,5649 @@ +/* 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" +#ifdef NAT_TRAVERSAL +#include "nat_traversal.h" +#endif +#ifdef VIRTUAL_IP +#include "virtual.h" +#endif + +/* + * 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 an XAUTH VID (Cisco Mode Config Interoperability)? + */ +#ifdef XAUTH_VID +#define SEND_XAUTH_VID 1 +#else /* !XAUTH_VID */ +#define SEND_XAUTH_VID 0 +#endif /* !XAUTH_VID */ + +/* 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)) { + 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); +#ifdef NAT_TRAVERSAL + if (nat_traversal_enabled) + nat_traversal_change_port_lookup(md, dst); +#endif + 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); + +#ifdef NAT_TRAVERSAL + if (nat_traversal_enabled) + nat_traversal_change_port_lookup(md, dst); +#endif + 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_XAUTH_VID) + vids_to_send++; + if (c->spd.this.cert.type == CERT_PGP) + vids_to_send++; + /* always send DPD Vendor ID */ + vids_to_send++; +#ifdef NAT_TRAVERSAL + if (nat_traversal_enabled) + vids_to_send++; +#endif + + 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[auth_policy >> POLICY_ISAKMP_SHIFT] + , 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 XAUTH Vendor ID */ + if (SEND_XAUTH_VID) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &rbody, VID_MISC_XAUTH)) + { + 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 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; + } + } + +#ifdef NAT_TRAVERSAL + 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; + } + } +#endif + + 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: + if (!skeyid_preshared(st)) + return FALSE; + break; + + case OAKLEY_RSA_SIG: + 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(&st->st_connection->spd.that.id, 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); + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + + /* 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; + } + } + +#ifdef NAT_TRAVERSAL + /* 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; + } + } +#endif + + /* 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. + */ +#ifdef NAT_TRAVERSAL + 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 +#endif + 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, +#ifdef NAT_TRAVERSAL + c->spd.that.host_port, +#endif +#ifdef VIRTUAL_IP + NULL, +#endif + 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 = find_host_connection(&md->iface->addr, pluto_port + , &md->sender, md->sender_port, LEMPTY); + 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; + + RETURN_STF_FAILURE(preparse_isakmp_sa_body(&sa_pd->payload.sa + , &sa_pd->pbs, &ipsecdoisit, &proposal_pbs, &proposal)); + +#ifdef NAT_TRAVERSAL + if (c == NULL && md->iface->ike_float) + { + c = find_host_connection(&md->iface->addr, NAT_T_IKE_FLOAT_PORT + , &md->sender, md->sender_port, LEMPTY); + } +#endif + + 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; + + backup_pbs(&proposal_pbs); + RETURN_STF_FAILURE(parse_isakmp_policy(&proposal_pbs + , proposal.isap_notrans, &policy)); + restore_pbs(&proposal_pbs); + + 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, +#ifdef NAT_TRAVERSAL + md->sender_port, +#endif +#ifdef VIRTUAL_IP + NULL, +#endif + NULL); + } + } + + /* 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_XAUTH_VID) + vids_to_send++; + if (md->openpgp) + vids_to_send++; + /* always send DPD Vendor ID */ + vids_to_send++; +#ifdef NAT_TRAVERSAL + if (md->nat_traversal_vid && nat_traversal_enabled) + vids_to_send++; +#endif + + /* 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)); + + /* 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 XAUTH Vendor ID */ + if (SEND_XAUTH_VID) + { + if (!out_vendorid(vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE + , &md->rbody, VID_MISC_XAUTH)) + { + 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 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; + } + } + +#ifdef NAT_TRAVERSAL + DBG(DBG_CONTROLMORE, + DBG_log("sender checking NAT-t: %d and %d" + , nat_traversal_enabled, md->nat_traversal_vid) + ) + 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; + } + } +#endif + + 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)); + } + +#ifdef NAT_TRAVERSAL + DBG(DBG_CONTROLMORE, + DBG_log("sender checking NAT-t: %d and %d" + , nat_traversal_enabled, md->nat_traversal_vid) + ) + 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; + } + #endif + + /**************** 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 + +#ifdef NAT_TRAVERSAL + if (st->nat_traversal & NAT_T_WITH_NATD) + { + if (!nat_traversal_add_natd(ISAKMP_NEXT_NONE, &md->rbody, md)) + return STF_INTERNAL_ERROR; + } +#endif + + /* 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 send_cr = !no_cr_send && (st->st_oakley.auth == OAKLEY_RSA_SIG) && + !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")); + +#ifdef NAT_TRAVERSAL + DBG(DBG_CONTROLMORE, + DBG_log("inI2: checking NAT-t: %d and %d" + , nat_traversal_enabled, st->nat_traversal) + ) + 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(); + } +#endif + + /* 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; + } + } + } + +#ifdef NAT_TRAVERSAL + if (st->nat_traversal & NAT_T_WITH_NATD) + { + if (!nat_traversal_add_natd(ISAKMP_NEXT_NONE, &md->rbody, md)) + return STF_INTERNAL_ERROR; + } +#endif + + /* 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; + int auth_payload = st->st_oakley.auth == OAKLEY_PRESHARED_KEY + ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_SIG; + 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; + + /* 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 = st->st_oakley.auth == OAKLEY_RSA_SIG + && 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; + +#ifdef NAT_TRAVERSAL + 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(); + } +#endif + + /*************** 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 ( st->st_oakley.auth == OAKLEY_RSA_SIG) + { + 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: + { + 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: + 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 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; + send_cert = st->st_oakley.auth == OAKLEY_RSA_SIG + && 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 = st->st_oakley.auth == OAKLEY_PRESHARED_KEY + ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_SIG; + + /* 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 (st->st_oakley.auth == OAKLEY_RSA_SIG) + { + 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, +#ifdef NAT_TRAVERSAL + md->sender_port, +#endif +#ifdef VIRTUAL_IP + his_net, +#endif + &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; + } + +#ifdef VIRTUAL_IP + 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; + } +#endif + + /* 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); + +#ifdef NAT_TRAVERSAL + 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); + } +#endif + + /* 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; + } + +#ifdef NAT_TRAVERSAL + 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; + } +#endif + + /* 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; + } + } + +#ifdef NAT_TRAVERSAL + if ((st->nat_traversal & NAT_T_DETECTED) + && (st->nat_traversal & NAT_T_WITH_NATOA)) + { + nat_traversal_natoa_lookup(md); + } +#endif + + /* ??? 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..5d7c5f78a --- /dev/null +++ b/src/pluto/kernel.c @@ -0,0 +1,2997 @@ +/* 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.25 2006/04/17 14:58:09 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" + +#ifdef NAT_TRAVERSAL +#include "packet.h" /* for pb_stream in nat_traversal.h */ +#include "nat_traversal.h" +#endif + +#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 +#ifdef NAT_TRAVERSAL + && c->spd.this.host_port != NAT_T_IKE_FLOAT_PORT +#endif + && 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); + } +} + +/* 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 }, + }; + +#ifdef NAT_TRAVERSAL + u_int8_t natt_type = 0; + u_int16_t natt_sport = 0, 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; + } +#endif + + 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; +#ifdef NAT_TRAVERSAL + 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; +#endif + 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 NAT_TRAVERSAL +#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 */ +} +#endif + +/* 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..c01ff31f9 --- /dev/null +++ b/src/pluto/kernel.h @@ -0,0 +1,200 @@ +/* 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; +#ifdef NAT_TRAVERSAL + u_int16_t natt_sport, natt_dport; + u_int8_t transid, natt_type; + ip_address *natt_oa; +#endif + 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); + +#ifdef NAT_TRAVERSAL +extern bool update_ipsec_sa(struct state *st); +#endif diff --git a/src/pluto/kernel_alg.c b/src/pluto/kernel_alg.c new file mode 100644 index 000000000..920a879d7 --- /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..fd43c4653 --- /dev/null +++ b/src/pluto/kernel_netlink.c @@ -0,0 +1,1221 @@ +/* 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); + } + +#ifdef NAT_TRAVERSAL + 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); + } +#endif + + 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..76bfbaf9a --- /dev/null +++ b/src/pluto/kernel_pfkey.c @@ -0,0 +1,938 @@ +/* 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 */ +#ifdef NAT_TRAVERSAL +#include "demux.h" +#include "nat_traversal.h" +#endif + +#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), +#ifdef NAT_TRAVERSAL + NE(SADB_X_NAT_T_NEW_MAPPING), +#endif + 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) +#ifdef NAT_TRAVERSAL + || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_X_NAT_T_NEW_MAPPING) +#endif + )) + { + /* 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; +#ifdef NAT_TRAVERSAL + case SADB_X_NAT_T_NEW_MAPPING: + process_pfkey_nat_t_new_mapping(&(buf->msg), extensions); + break; +#endif + 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)) + +#ifdef NAT_TRAVERSAL + && (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)) +#endif + + && 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..21092383a --- /dev/null +++ b/src/pluto/keys.c @@ -0,0 +1,1404 @@ +/* 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" + +#ifdef NAT_TRAVERSAL +#define PB_STREAM_UNDEFINED +#include "nat_traversal.h" +#endif + +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; + 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); + +#ifdef DEBUG + DBG(DBG_PRIVATE, RSA_show_public_key(&pk->u.rsa)); +#endif + + 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; + } +#ifdef NAT_TRAVERSAL + else if (nat_traversal_enabled + && (c->policy & POLICY_PSK) + && kind == PPK_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; + } +#endif + + 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); + +#ifdef DEBUG + DBG(DBG_PRIVATE, + if (s == NULL) + DBG_log("no Preshared Key Found"); + else + DBG_dump_chunk("Preshared Key", s->u.preshared_secret); + ); +#endif + 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[RSA_MAX_ENCODING_BYTES]; /* 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 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("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_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); + +#ifdef DEBUG + DBG(DBG_PRIVATE, RSA_show_public_key(rsa)); +#endif + + + 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 */ + + 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..d47d8b0a2 --- /dev/null +++ b/src/pluto/keys.h @@ -0,0 +1,110 @@ +/* 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 "/etc/ipsec.secrets" +#endif + +const char *shared_secrets_file; + +extern void load_preshared_secrets(int whackfd); +extern void free_preshared_secrets(void); + +struct state; /* forward declaration */ + +enum PrivateKeyKind { + PPK_PSK, + /* PPK_DSS, */ /* not implemented */ + PPK_RSA, + PPK_PIN +}; + +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..137e92980 --- /dev/null +++ b/src/pluto/log.c @@ -0,0 +1,843 @@ +/* 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.7 2005/07/11 18:33:45 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); + whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ + show_states_status(name); +#ifdef KLIPS + whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ + 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..0bf8219aa --- /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 PERPERRLOGDIR +#define PERPERRLOGDIR "/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..1c22845a5 --- /dev/null +++ b/src/pluto/modecfg.c @@ -0,0 +1,798 @@ +/* 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 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT 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" + +/* + * Addresses assigned (usually via MODE_CONFIG) to the Initiator + */ +struct internal_addr +{ + ip_address ipaddr; + ip_address dns[2]; + ip_address wins[2]; +}; + +/* + * Get inside IP address for a connection + */ +static void +get_internal_addresses(struct connection *c, struct internal_addr *ia) +{ + zero(ia); + + if (isanyaddr(&c->spd.that.host_srcip)) + { + /* not defined in connection - fetch it from LDAP */ + } + else + { + ia->ipaddr = c->spd.that.host_srcip; + } +} + +/* + * Compute HASH of Mode Config. + */ +static size_t +mode_cfg_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("MODE CFG: HASH computed:"); + DBG_dump("", dest, ctx.hmac_digest_size) + ) + return ctx.hmac_digest_size; +} + + +/* Mode Config Reply + * Generates a reply stream containing Mode Config information (eg: IP, DNS, WINS) + */ +stf_status modecfg_resp(struct state *st + , u_int resp + , pb_stream *rbody + , u_int16_t replytype + , bool hackthat + , u_int16_t ap_id) +{ + u_char *r_hash_start,*r_hashval; + + /* START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); */ + + { + pb_stream hash_pbs; + int np = ISAKMP_NEXT_ATTR; + + 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 */ + } + + /* ATTR out */ + { + struct isakmp_mode_attr attrh; + struct isakmp_attribute attr; + pb_stream strattr,attrval; + int attr_type; + struct internal_addr ia; + int dns_idx, wins_idx; + bool dont_advance; + + attrh.isama_np = ISAKMP_NEXT_NONE; + attrh.isama_type = replytype; + + attrh.isama_identifier = ap_id; + if (!out_struct(&attrh, &isakmp_attr_desc, rbody, &strattr)) + return STF_INTERNAL_ERROR; + + get_internal_addresses(st->st_connection, &ia); + + if (!isanyaddr(&ia.dns[0])) /* We got DNS addresses, answer with those */ + resp |= LELEM(INTERNAL_IP4_DNS); + else + resp &= ~LELEM(INTERNAL_IP4_DNS); + + if (!isanyaddr(&ia.wins[0])) /* We got WINS addresses, answer with those */ + resp |= LELEM(INTERNAL_IP4_NBNS); + else + resp &= ~LELEM(INTERNAL_IP4_NBNS); + + if (hackthat) + { + if (memcmp(&st->st_connection->spd.that.client.addr + ,&ia.ipaddr + ,sizeof(ia.ipaddr)) != 0) + { + /* Make the Internal IP address and Netmask + * as that client address + */ + st->st_connection->spd.that.client.addr = ia.ipaddr; + st->st_connection->spd.that.client.maskbits = 32; + st->st_connection->spd.that.has_client = TRUE; + } + } + + attr_type = 0; + dns_idx = 0; + wins_idx = 0; + + while (resp != 0) + { + dont_advance = FALSE; + if (resp & 1) + { + const u_char *byte_ptr; + u_int len; + + /* ISAKMP attr out */ + 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: + { + char srcip[ADDRTOT_BUF]; + + addrtot(&ia.ipaddr, 0, srcip, sizeof(srcip)); + plog("assigning virtual IP source address %s", srcip); + 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: + 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: + 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; + 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++; + resp >>= 1; + } + } + close_message(&strattr); + } + + mode_cfg_hash(r_hashval,r_hash_start,rbody->cur,st); + close_message(rbody); + encrypt_message(rbody, st); + return STF_OK; +} + +/* Set MODE_CONFIG data to client. + * Pack IP Addresses, DNS, etc... and ship + */ +stf_status modecfg_send_set(struct state *st) +{ + pb_stream reply,rbody; + char buf[256]; + + /* set up reply */ + init_pbs(&reply, buf, sizeof(buf), "ModecfgR1"); + + st->st_state = STATE_MODE_CFG_R1; + /* 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, &reply, &rbody)) + { + return STF_INTERNAL_ERROR; + } + } + +#define MODECFG_SET_ITEM ( LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_SUBNET) | LELEM(INTERNAL_IP4_NBNS) | LELEM(INTERNAL_IP4_DNS) ) + + modecfg_resp(st, MODECFG_SET_ITEM + , &rbody + , ISAKMP_CFG_SET + , TRUE + , 0/* XXX ID */); +#undef MODECFG_SET_ITEM + + clonetochunk(st->st_tpacket, reply.start + , pbs_offset(&reply), "ModeCfg set"); + + /* Transmit */ + send_packet(st, "ModeCfg set"); + + /* RETRANSMIT if Main, SA_REPLACE if Aggressive */ + if (st->st_event->ev_type != EVENT_RETRANSMIT + && st->st_event->ev_type != EVENT_NULL) + { + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); + } + + return STF_OK; +} + +/* Set MODE_CONFIG data to client. + * Pack IP Addresses, DNS, etc... and ship + */ +stf_status +modecfg_start_set(struct state *st) +{ + if (st->st_msgid == 0) + { + /* pick a new message id */ + st->st_msgid = generate_msgid(st); + } + st->st_modecfg.vars_set = TRUE; + + return modecfg_send_set(st); +} + +/* + * Send modecfg IP address request (IP4 address) + */ +stf_status +modecfg_send_request(struct state *st) +{ + pb_stream reply; + pb_stream rbody; + char buf[256]; + u_char *r_hash_start,*r_hashval; + + /* set up reply */ + init_pbs(&reply, buf, sizeof(buf), "modecfg_buf"); + + plog("sending ModeCfg request"); + + /* this is the beginning of a new exchange */ + st->st_msgid = generate_msgid(st); + st->st_state = STATE_MODE_CFG_I1; + + /* 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, &reply, &rbody)) + { + return STF_INTERNAL_ERROR; + } + } + + START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); + + /* ATTR out */ + { + struct isakmp_mode_attr attrh; + struct isakmp_attribute attr; + pb_stream strattr; + + attrh.isama_np = ISAKMP_NEXT_NONE; + attrh.isama_type = ISAKMP_CFG_REQUEST; + attrh.isama_identifier = 0; + if (!out_struct(&attrh, &isakmp_attr_desc, &rbody, &strattr)) + return STF_INTERNAL_ERROR; + /* ISAKMP attr out (ipv4) */ + attr.isaat_af_type = INTERNAL_IP4_ADDRESS; + attr.isaat_lv = 0; + out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL); + + /* ISAKMP attr out (netmask) */ + attr.isaat_af_type = INTERNAL_IP4_NETMASK; + attr.isaat_lv = 0; + out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL); + + close_message(&strattr); + } + + mode_cfg_hash(r_hashval,r_hash_start,rbody.cur,st); + + close_message(&rbody); + close_output_pbs(&reply); + + init_phase2_iv(st, &st->st_msgid); + encrypt_message(&rbody, st); + + clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply) + , "modecfg: req"); + + /* Transmit */ + send_packet(st, "modecfg: req"); + + /* RETRANSMIT if Main, SA_REPLACE if Aggressive */ + if (st->st_event->ev_type != EVENT_RETRANSMIT) + { + delete_event(st); + event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0 * 3, st); + } + st->st_modecfg.started = TRUE; + + return STF_OK; +} + +/* + * parse a modecfg attribute payload + */ +static stf_status +modecfg_parse_attributes(pb_stream *attrs, u_int *set) +{ + struct isakmp_attribute attr; + pb_stream strattr; + + while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + { + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc, attrs, &strattr)) + { + int len = (attr.isaat_af_type & 0x8000)? 4 : attr.isaat_lv; + + if (len < 4) + { + plog("Attribute was too short: %d", len); + return STF_FAIL; + } + + attrs->cur += len; + } + + switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + { + case INTERNAL_IP4_ADDRESS: + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + *set |= LELEM(attr.isaat_af_type); + break; + default: + plog("unsupported mode cfg attribute %s received." + , enum_show(&modecfg_attr_names + , attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )); + break; + } + } + return STF_OK; +} + +/* STATE_MODE_CFG_R0: + * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP) + * + * This state occurs both in the responder and in the initiator. + * + * In the responding server, it occurs when the client *asks* for an IP + * address or other information. + * + * Otherwise, it occurs in the initiator when the server sends a challenge + * a set, or has a reply to our request. + */ +stf_status +modecfg_inR0(struct msg_digest *md) +{ + struct state *const st = md->st; + struct payload_digest *p; + stf_status stat; + + plog("received ModeCfg request"); + + st->st_msgid = md->hdr.isa_msgid; + CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val + ,hash_pbs->roof + , md->message_pbs.roof, st) + , "MODECFG-HASH", "MODE R0"); + + /* process the MODECFG payloads therein */ + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + u_int set_modecfg_attrs = LEMPTY; + + switch (p->payload.attribute.isama_type) + { + default: + plog("Expecting ISAKMP_CFG_REQUEST, got %s instead (ignored)." + , enum_name(&attr_msg_type_names + , p->payload.attribute.isama_type)); + + stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs); + if (stat != STF_OK) + return stat; + break; + + case ISAKMP_CFG_REQUEST: + stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs); + if (stat != STF_OK) + return stat; + + stat = modecfg_resp(st, set_modecfg_attrs + ,&md->rbody + ,ISAKMP_CFG_REPLY + ,TRUE + ,p->payload.attribute.isama_identifier); + + if (stat != STF_OK) + { + /* notification payload - not exactly the right choice, but okay */ + md->note = CERTIFICATE_UNAVAILABLE; + return stat; + } + + /* they asked us, we responded, msgid is done */ + st->st_msgid = 0; + } + } + return STF_OK; +} + +/* STATE_MODE_CFG_R2: + * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + * + * used in server push mode, on the client (initiator). + */ +static stf_status +modecfg_inI2(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs; + int resp = LEMPTY; + stf_status stat; + struct payload_digest *p; + u_int16_t isama_id = 0; + + st->st_msgid = md->hdr.isa_msgid; + CHECK_QUICK_HASH(md + , mode_cfg_hash(hash_val + ,hash_pbs->roof + , md->message_pbs.roof + , st) + , "MODECFG-HASH", "MODE R1"); + + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + struct isakmp_attribute attr; + pb_stream strattr; + + isama_id = p->payload.attribute.isama_identifier; + + if (p->payload.attribute.isama_type != ISAKMP_CFG_SET) + { + plog("Expecting MODE_CFG_SET, got %x instead." + ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type); + return STF_IGNORE; + } + + /* CHECK that SET has been received. */ + + while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + { + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc + , attrs, &strattr)) + { + int len; + + /* Skip unknown */ + if (attr.isaat_af_type & 0x8000) + len = 4; + else + len = attr.isaat_lv; + + if (len < 4) + { + plog("Attribute was too short: %d", len); + return STF_FAIL; + } + + attrs->cur += len; + } + + switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + { + case INTERNAL_IP4_ADDRESS: + { + struct connection *c = st->st_connection; + ip_address a; + u_int32_t *ap = (u_int32_t *)(strattr.cur); + a.u.v4.sin_family = AF_INET; + + memcpy(&a.u.v4.sin_addr.s_addr, ap + , sizeof(a.u.v4.sin_addr.s_addr)); + + if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 + || isanyaddr(&c->spd.this.host_srcip)) + { + char srcip[ADDRTOT_BUF]; + + c->spd.this.host_srcip = a; + addrtot(&a, 0, srcip, sizeof(srcip)); + plog("setting virtual IP source address to %s", srcip); + } + + /* setting client subnet as srcip/32 */ + addrtosubnet(&a, &c->spd.this.client); + c->spd.this.has_client = TRUE; + } + resp |= LELEM(attr.isaat_af_type); + break; + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + resp |= LELEM(attr.isaat_af_type); + break; + default: + plog("unsupported mode cfg attribute %s received." + , enum_show(&modecfg_attr_names, (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ))); + break; + } + } + } + + /* ack things */ + stat = modecfg_resp(st, resp + ,&md->rbody + ,ISAKMP_CFG_ACK + ,FALSE + ,isama_id); + + if (stat != STF_OK) + { + /* notification payload - not exactly the right choice, but okay */ + md->note = CERTIFICATE_UNAVAILABLE; + return stat; + } + + /* + * we are done with this exchange, clear things so + * that we can start phase 2 properly + */ + st->st_msgid = 0; + + if (resp) + { + st->st_modecfg.vars_set = TRUE; + } + return STF_OK; +} + +/* STATE_MODE_CFG_R1: + * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + */ +stf_status +modecfg_inR1(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs; + int set_modecfg_attrs = LEMPTY; + stf_status stat; + struct payload_digest *p; + + plog("parsing ModeCfg reply"); + + st->st_msgid = md->hdr.isa_msgid; + CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val,hash_pbs->roof, md->message_pbs.roof, st) + , "MODECFG-HASH", "MODE R1"); + + + /* process the MODECFG payloads therein */ + for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) + { + struct isakmp_attribute attr; + pb_stream strattr; + + attrs = &p->pbs; + + switch (p->payload.attribute.isama_type) + { + default: + { + plog("Expecting MODE_CFG_ACK, got %x instead." + ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type); + return STF_IGNORE; + } + break; + + case ISAKMP_CFG_ACK: + /* CHECK that ACK has been received. */ + stat = modecfg_parse_attributes(attrs, &set_modecfg_attrs); + if (stat != STF_OK) + return stat; + break; + + case ISAKMP_CFG_REPLY: + while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + { + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc + , attrs, &strattr)) + { + /* Skip unknown */ + int len; + if (attr.isaat_af_type & 0x8000) + len = 4; + else + len = attr.isaat_lv; + + if (len < 4) + { + plog("Attribute was too short: %d", len); + return STF_FAIL; + } + + attrs->cur += len; + } + + switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + { + case INTERNAL_IP4_ADDRESS: + { + struct connection *c = st->st_connection; + ip_address a; + u_int32_t *ap = (u_int32_t *)(strattr.cur); + a.u.v4.sin_family = AF_INET; + + memcpy(&a.u.v4.sin_addr.s_addr, ap + , sizeof(a.u.v4.sin_addr.s_addr)); + + if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 + || isanyaddr(&c->spd.this.host_srcip)) + { + char srcip[ADDRTOT_BUF]; + + c->spd.this.host_srcip = a; + addrtot(&a, 0, srcip, sizeof(srcip)); + plog("setting virtual IP source address to %s", srcip); + } + + /* setting client subnet as srcip/32 */ + addrtosubnet(&a, &c->spd.this.client); + setportof(0, &c->spd.this.client.addr); + c->spd.this.has_client = TRUE; + } + /* fall through to set attribute flage */ + + case INTERNAL_IP4_NETMASK: + case INTERNAL_IP4_DNS: + case INTERNAL_IP4_SUBNET: + case INTERNAL_IP4_NBNS: + set_modecfg_attrs |= LELEM(attr.isaat_af_type); + break; + default: + plog("unsupported mode cfg attribute %s received." + , enum_show(&modecfg_attr_names + , (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ))); + break; + } + } + break; + } + } + + /* we are done with this exchange, clear things so that we can start phase 2 properly */ + st->st_msgid = 0; + + if (set_modecfg_attrs) + { + st->st_modecfg.vars_set = TRUE; + } + return STF_OK; +} diff --git a/src/pluto/modecfg.h b/src/pluto/modecfg.h new file mode 100644 index 000000000..f6856d263 --- /dev/null +++ b/src/pluto/modecfg.h @@ -0,0 +1,33 @@ +/* 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 $ + */ + +struct state; + +stf_status modecfg_resp(struct state *st + , u_int resp + , pb_stream *s, u_int16_t cmd + , bool hackthat, u_int16_t id); + +stf_status modecfg_send_set(struct state *st); + +extern stf_status modecfg_start_set(struct state *st); + +/* Mode Config States */ + +extern stf_status modecfg_inR0(struct msg_digest *md); +extern stf_status modecfg_inR1(struct msg_digest *md); +extern stf_status modecfg_send_request(struct state *st); 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..2f5ba3cb4 --- /dev/null +++ b/src/pluto/nat_traversal.c @@ -0,0 +1,869 @@ +/* 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 $ + */ + +#ifdef NAT_TRAVERSAL + +#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(last_np, outs, VID_NATT_IETF_02); + } + 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", FALSE); + + /* 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); +} + +#endif + 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..f31b96c7f --- /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..71f8101cd --- /dev/null +++ b/src/pluto/oid.h @@ -0,0 +1,75 @@ +/* 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_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..eed46d59d --- /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" + 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/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..e8d381741 --- /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..015319aaf --- /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..413938976 --- /dev/null +++ b/src/pluto/pkcs1.c @@ -0,0 +1,635 @@ +/* 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 "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; + } + 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..0691a80d6 --- /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-style.el b/src/pluto/pluto-style.el new file mode 100644 index 000000000..0de474e44 --- /dev/null +++ b/src/pluto/pluto-style.el @@ -0,0 +1,4 @@ +(c-add-style "pluto" '("bsd" + (c-basic-offset . 4) + (c-offsets-alias . ((substatement-open . 0))))) + 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..f9badbae3 --- /dev/null +++ b/src/pluto/plutomain.c @@ -0,0 +1,696 @@ +/* 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 "sha1.h" +#include "md5.h" +#include "crypto.h" /* requires sha1.h and md5.h */ + +#ifdef VIRTUAL_IP +#include "virtual.h" +#endif + +#ifdef NAT_TRAVERSAL +#include "nat_traversal.h" +#endif + +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 +#ifdef NAT_TRAVERSAL + " [ --debug-natt]" + " \\\n\t" + "[--nat_traversal] [--keep_alive ]" + " \\\n\t" + "[--force_keepalive] [--disable_port_floating]" +#endif +#ifdef VIRTUAL_IP + " \\\n\t" + "[--virtual_private ]" +#endif + "\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; +#ifdef NAT_TRAVERSAL + bool nat_traversal = FALSE; + bool nat_t_spf = TRUE; /* support port floating */ + unsigned int keep_alive = 0; + bool force_keepalive = FALSE; +#endif +#ifdef VIRTUAL_IP + char *virtual_private = NULL; +#endif + 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' }, +#ifdef NAT_TRAVERSAL + { "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' }, +#endif +#ifdef VIRTUAL_IP + { "virtual_private", required_argument, NULL, '6' }, +#endif +#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